@ljt12138
2017-06-19T21:33:30.000000Z
字数 9549
阅读 757
吕老板讲的方法只有50....
TLE
#include <bits/stdc++.h>
using namespace std;
int p, n, m;
const int mod = 998244353, g = 3;
int power(int a, int n)
{
int ans = 1, b = a;
for (register int i = 0; i <= 30; i++) {
if ((n>>i)&1) ans = (long long)ans*b%mod;
b = (long long)b*b%mod;
}
return ans;
}
inline int inv(int a)
{ return power(a, mod-2); }
const int MAXN = 2049;
int rev[MAXN];
void NTT(int a[], int n, int flag)
{
int m = 1, lgn = 0;
while (m < n) lgn++, m <<= 1;
rev[0] = 0;
for (register int i = 1; i < n; i++) rev[i] = (rev[i>>1]>>1)|((i&1)<<(lgn-1));
for (register int i = 0; i < n; i++)
if (rev[i] < i) swap(a[i], a[rev[i]]);
for (register int k = 1; k <= n; k <<= 1) {
int dw = power(g, (mod-1)/k);
if (flag == -1) dw = inv(dw);
for (register int i = 0; i < n; i += k) {
int w = 1;
for (register int j = 0; j < k>>1; j++) {
int u = a[i+j], t = (long long)w*a[i+j+(k>>1)]%mod;
a[i+j] = (u+t)%mod, a[i+j+(k>>1)] = ((u-t)%mod+mod)%mod;
w = (long long)w*dw%mod;
}
}
}
if (flag == -1) {
int Inv = inv(n);
for (register int i = 0; i < n; i++)
a[i] = (long long)Inv*a[i]%mod;
}
}
int B[2049], C[2049];
void mul(int a[2049], int M, int b[2049], int c[2049]) // $a = b\oplus c$
{
memcpy(B, b, sizeof B), memcpy(C, c, sizeof C);
memset(a, 0, sizeof B);
int n = 1;
while (n < M) n <<= 1;
n <<= 1; // todo here
NTT(B, n, 1), NTT(C, n, 1);
for (register int i = 0; i < n; i++)
a[i] = (long long)B[i]*C[i]%mod;
NTT(a, n, -1);
for (register int i = M; i < 2048; i++)
a[i] = 0;
}
struct node {
int N, Pow; // 10^N = Pow;
int A[55][2049];
void print()
{
puts("Matrix : ");
for (int i = 0; i < p; i++) {
for (int j = 0; j <= m; j++)
cerr << A[i][j] << " ";
cerr << endl;
}
puts("End Matrix.");
}
node() {
memset(A, 0, sizeof A);
}
friend node operator * (node &a, node &b)
{
node ret;
static int A[MAXN];
memset(A, 0, sizeof A);
ret.N = a.N+b.N, ret.Pow = (long long)a.Pow*b.Pow%p;
for (register int i = 0; i < p; i++) {
for (register int j = 0; j < p; j++) {
int M = ((long long)i*b.Pow%p+j)%p;
mul(A, m+1, a.A[i], b.A[j]);
for (register int k = 0; k <= m; k++) (ret.A[M][k] += A[k]) %= mod;
}
}
return ret;
}
};
node power(node a, int n)
{
node ans;
int flag = -1;
for (int i = 0; i <= 30; i++) {
if ((n>>i) == 0) break;
if ((n>>i)&1) {
if (flag == -1) ans = a, flag = 1;
else ans = ans*a;
}
a = a*a;
}
return ans;
}
node A;
int a[MAXN], b[MAXN], c[MAXN];
int main()
{
scanf("%d%d%d", &n, &p, &m);
A.N = 1, A.Pow = 10;
for (int j = 0; j <= min(m, 9); j++)
A.A[j%p][j]++;
A = power(A, n);
int ans = 0;
for (int i = 0; i <= m; i++) {
ans = (ans+A.A[0][i])%mod;
printf("%d ", ans);
}
puts("");
return 0;
}
upd: 原来是我走神了QwQ。
做循环卷积的时候要先全部DFT,卷完了全部IDFT,复杂度少一个...
AC...
#include <bits/stdc++.h>
using namespace std;
int p, n, m;
const int mod = 998244353, g = 3;
int power(int a, int n)
{
int ans = 1, b = a;
for (register int i = 0; i <= 30; i++) {
if ((n>>i)&1) ans = (long long)ans*b%mod;
b = (long long)b*b%mod;
}
return ans;
}
inline int inv(int a)
{ return power(a, mod-2); }
const int MAXN = 2049;
int rev[MAXN];
void NTT(int a[], int n, int flag)
{
int m = 1, lgn = 0;
while (m < n) lgn++, m <<= 1;
rev[0] = 0;
for (register int i = 1; i < n; i++) rev[i] = (rev[i>>1]>>1)|((i&1)<<(lgn-1));
for (register int i = 0; i < n; i++)
if (rev[i] < i) swap(a[i], a[rev[i]]);
for (register int k = 1; k <= n; k <<= 1) {
int dw = power(g, (mod-1)/k);
if (flag == -1) dw = inv(dw);
for (register int i = 0; i < n; i += k) {
int w = 1;
for (register int j = 0; j < k>>1; j++) {
int u = a[i+j], t = (long long)w*a[i+j+(k>>1)]%mod;
a[i+j] = (u+t)%mod, a[i+j+(k>>1)] = ((u-t)%mod+mod)%mod;
w = (long long)w*dw%mod;
}
}
}
if (flag == -1) {
int Inv = inv(n);
for (register int i = 0; i < n; i++)
a[i] = (long long)Inv*a[i]%mod;
}
}
int B[2049], C[2049];
void mul(int a[2049], int M, int b[2049], int c[2049]) // $a = b\oplus c$
{
memcpy(B, b, sizeof B), memcpy(C, c, sizeof C);
memset(a, 0, sizeof B);
int n = 1;
while (n < M) n <<= 1;
n <<= 1; // todo here
cerr << n << endl;
NTT(B, n, 1), NTT(C, n, 1);
for (register int i = 0; i < n; i++)
a[i] = (long long)B[i]*C[i]%mod;
NTT(a, n, -1);
for (register int i = M; i < 2048; i++)
a[i] = 0;
}
struct node {
int N, Pow; // 10^N = Pow;
int A[55][2049];
void display()
{
puts("Matrix : ");
for (int i = 0; i < p; i++) {
for (int j = 0; j <= m; j++)
cerr << A[i][j] << " ";
cerr << endl;
}
puts("End Matrix.");
}
node() {
memset(A, 0, sizeof A);
}
friend node operator * (node a, node b)
{
node ret;
static int C[55][MAXN], d[MAXN];
memset(C, 0, sizeof C);
ret.N = a.N+b.N, ret.Pow = (long long)a.Pow*b.Pow%p;
int np = 1;
while (np < m+1) np <<= 1;
np <<= 1; // todo here
for (int i = 0; i < p; i++) NTT(a.A[i], np, 1), NTT(b.A[i], np, 1);
for (int i = 0; i < p; i++) {
for (int j = 0; j < p; j++) {
int M = ((long long)i*b.Pow%p+j)%p;
for (int k = 0; k < np; k++)
C[M][k] = (C[M][k]+(long long)a.A[i][k]*b.A[j][k]%mod)%mod;
}
}
for (int i = 0; i < p; i++) {
NTT(a.A[i], np, -1), NTT(b.A[i], np, -1),
NTT(C[i], np, -1);
for (int k = 0; k <= m; k++) ret.A[i][k] = (ret.A[i][k]+C[i][k])%mod;
}
return ret;
}
};
node power(node a, int n)
{
node ans;
int flag = -1;
for (int i = 0; i <= 30; i++) {
if ((n>>i) == 0) break;
if ((n>>i)&1) {
if (flag == -1) ans = a, flag = 1;
else ans = ans*a;
}
a = a*a;
}
return ans;
}
node A;
int a[MAXN], b[MAXN], c[MAXN];
int main()
{
scanf("%d%d%d", &n, &p, &m);
A.N = 1, A.Pow = 10;
for (int j = 0; j <= min(m, 9); j++)
A.A[j%p][j]++;
A = power(A, n);
int ans = 0;
for (int i = 0; i <= m; i++) {
ans = (ans+A.A[0][i])%mod;
printf("%d ", ans);
}
puts("");
return 0;
}
考虑匹配条件,令也就是。显然,一个序列匹配当且仅当排序后匹配。考虑排序后个和个的情况,容易发现,匹配当且仅当(排序后)对于任何一个前缀,的数量不少于。如果把看成左括号,看成右括号,就是经典的括号序列匹配问题了。用线段树维护前缀和的区间最小值即可。
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 150005;
int gt[MAXN];
int n, m, h;
struct p {
int dt, pos;
friend bool operator < (const p &a, const p &b)
{
if (a.dt == b.dt) return a.pos < b.pos;
return a.dt < b.dt;
}
}a[MAXN], b[MAXN], dx[MAXN+MAXN];
int A[MAXN], B[MAXN], dx_top = 0;
void dx_init()
{
for (int i = 1; i <= m; i++) dx[++dx_top] = b[i];
for (int i = 1; i <= n; i++) dx[++dx_top] = a[i];
sort(dx+1, dx+dx_top+1);
}
inline int dx_num(const p &a)
{ return lower_bound(dx+1, dx+dx_top+1, a)-dx; }
struct tree {
int dat[MAXN*4], l[MAXN*4], r[MAXN*4], lc[MAXN*4], rc[MAXN*4], tag[MAXN*4], top, root;
inline void clear()
{ top = root = 0; }
tree()
{ clear(); }
void build(int &nd, int opl, int opr)
{
nd = ++top, dat[nd] = tag[nd] = 0, l[nd] = opl, r[nd] = opr;
if (opl < opr)
build(lc[nd], opl, (opl+opr)/2), build(rc[nd], (opl+opr)/2+1, opr);
}
void pdw(int nd)
{
if (!nd) return;
if (lc[nd]) tag[lc[nd]] += tag[nd], tag[rc[nd]] += tag[nd];
dat[nd] += tag[nd], tag[nd] = 0;
}
inline void update(int nd)
{ if (lc[nd]) dat[nd] = min(dat[lc[nd]], dat[rc[nd]]); }
void modify(int nd, int opl, int opr, int dt)
{
pdw(nd);
if (l[nd] == opl && r[nd] == opr) tag[nd] += dt;
else {
int mid = (l[nd]+r[nd])/2;
if (opr <= mid) modify(lc[nd], opl, opr, dt);
else if (opl > mid) modify(rc[nd], opl, opr, dt);
else modify(lc[nd], opl, mid, dt), modify(rc[nd], mid+1, opr, dt);
pdw(lc[nd]), pdw(rc[nd]), update(nd);
}
}
int query(int nd, int opl, int opr)
{
pdw(nd);
if (l[nd] == opl && r[nd] == opr) return dat[nd];
int mid = (l[nd]+r[nd])/2;
if (opr <= mid) return query(lc[nd], opl, opr);
else if (opl > mid) return query(rc[nd], opl, opr);
else return min(query(lc[nd], opl, mid), query(rc[nd], mid+1, opr));
}
void display()
{
for (int i = 1; i <= dx_top; i++) printf("%d ", query(root, i, i));
puts("");
}
} seg;
int main()
{
scanf("%d%d%d", &n, &m, &h);
for (int i = 1; i <= m; i++)
scanf("%d", &b[i].dt), b[i].dt = h-b[i].dt, b[i].pos = -i;
for (int i = 1; i <= n; i++)
scanf("%d", &a[i].dt), a[i].pos = i;
dx_init();
for (int i = 1; i <= m; i++) B[i] = dx_num(b[i]);//, printf("%d ", B[i]);
for (int i = 1; i <= n; i++) A[i] = dx_num(a[i]);//, printf("%d ", A[i]);
seg.build(seg.root, 1, dx_top);
for (int i = 1; i <= m; i++) seg.modify(seg.root, B[i], dx_top, 1);
int cnt = 0;
for (int i = 1; i <= m; i++) seg.modify(seg.root, A[i], dx_top, -1);
for (int i = 1; i <= n; i++) {
if (seg.query(seg.root, 1, dx_top) >= 0) cnt++;
seg.modify(seg.root, A[i], dx_top, 1);
if (i+m > n) break;
seg.modify(seg.root, A[i+m], dx_top, -1);
}
printf("%d\n", cnt);
return 0;
}
神奇的费用流...最大流限制/费用计算的好模型。
from visit_world
把 1 到 n 串成一条链
考虑上限:
想象你带了 mx 个小弟在链上走,到一个位置 p,你可以派出一个小弟去获得对应收益,但是他在 p + k 的时候才能归队
考虑下限:
在 p 这里派出一个小弟,会使得 [p, p+k) 上走“主干道”的小弟减少一名。
因此,我们限制“主干道”的流量为 mx - mi 即可满足下限
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1005;
int n, k, ms, me;
int mx, mn;
int s[MAXN], e[MAXN];
struct node {
int to, next, flow;
long long cost;
int neg;
} edge[MAXN*200];
int head[MAXN], top = 0;
inline int push(int i, int j, int f, long long c)
{
++top, edge[top] = (node) {j, head[i], f, c, top+1}, head[i] = top;
++top, edge[top] = (node) {i, head[j], 0, -c, top-1}, head[j] = top;
return top-1;
}
long long dis[MAXN];
int vis[MAXN];
queue<int> que;
int pre[MAXN], pre_edge[MAXN];
int S = MAXN-1, T = MAXN-2;
const long long inf = 1e14;
bool spfa(int &flow, long long &cost)
{
memset(vis, 0, sizeof vis), memset(dis, 127/3, sizeof dis);
vis[S] = 1, dis[S] = 0, que.push(S);
while (!que.empty()) {
int nd = que.front(); que.pop(), vis[nd] = 0;
for (int i = head[nd]; i; i = edge[i].next) {
int to = edge[i].to;
if (!edge[i].flow || dis[to] <= dis[nd]+edge[i].cost) continue;
dis[to] = dis[nd]+edge[i].cost, pre[to] = nd, pre_edge[to] = i;
if (!vis[to])
vis[to] = 1, que.push(to);
}
}
if (dis[T] > inf) return 0;;
int mf = INT_MAX;
for (int i = T; i != S; i = pre[i]) mf = min(mf, edge[pre_edge[i]].flow);
for (int i = T; i != S; i = pre[i])
edge[pre_edge[i]].flow -= mf, edge[edge[pre_edge[i]].neg].flow += mf;
cost += dis[T]*mf;
flow += mf;
return 1;
}
void mcf(int &flow, long long &cost)
{
cost = flow = 0;
while (spfa(flow, cost));
}
int eg[MAXN];
int main()
{
scanf("%d%d%d%d", &n, &k, &ms, &me); // change t, t \le me, k-t \le ms \rightarrow t \ge k-ms
mx = k-ms, mn = me; // [mn, mx]
long long cnt = 0;
for (int i = 1; i <= n; i++) scanf("%d", &s[i]), cnt += s[i]; // sleep
for (int i = 1; i <= n; i++) scanf("%d", &e[i]);
push(S, 1, mx, 0), push(n, T, mx, 0);
for (int i = 1; i < n; i++) {
if (i >= k) push(i, i+1, mx-mn, 0);
else push(i, i+1, mx, 0);
}
for (int i = 1; i <= n; i++) {
int t = i+k <= n?i+k:T;
eg[i] = push(i, t, 1, -e[i]+s[i]);
}
int flow;
long long cost;
mcf(flow, cost);
printf("%lld\n", cnt-cost);
for (int i = 1; i <= n; i++) {
if (edge[eg[i]].flow)
putchar('S');
else putchar('E');
}
puts("");
return 0;
}
首先有为定值,设为。考虑的每一位,如果为1,由于必然一边为1一边为0,对和的贡献不变;如果为0,则应该尽可能让更高位两边全为1。
做法1:首先取出为0的位做一遍最大异或找到中必须为1的位置,然后作出线性基。贪心的从高到低放置每一位,用异或方程组判断下面是否有解「太麻烦了根本写不出来...」
// 但这个思路扩展性更好,可以出道题233
做法2:考虑统一处理,在贪心求最大异或的时候改变贪心顺序,首先是为0的位,然后是为1的。直接做就好了。
#include <bits/stdc++.h>
using namespace std;
struct linear_base {
long long A[70];
void push(long long x, long long lim)
{
for (int i = 60; i >= 0; i--) {
if (lim&(1ll<<i)) continue;
if (!(x&(1ll<<i))) continue;
if (!A[i]) { A[i] = x; return;}
else x ^= A[i];
}
for (int i = 60; i >= 0; i--) {
if (!(lim&(1ll<<i))) continue;
if (!(x&(1ll<<i))) continue;
if (!A[i]) { A[i] = x; return; }
else x ^= A[i];
}
}
long long max_xor(long long lim)
{
long long val = 0;
for (int i = 60; i >= 0; i--)
if (!(lim&(1ll<<i)) && !(val&(1ll<<i)))
val ^= A[i];
for (int i = 60; i >= 0; i--)
if ((lim&(1ll<<i)) && !(val&(1ll<<i)))
val ^= A[i];
return val;
}
} lb;
int n;
long long val[100005], xr = 0;
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%lld", &val[i]), xr ^= val[i];
for (int i = 1; i <= n; i++)
lb.push(val[i], xr);
printf("%lld\n", xr^lb.max_xor(xr));
return 0;
}