@ljt12138
2017-06-19T13:33:30.000000Z
字数 9549
阅读 920
吕老板讲的方法只有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 hereNTT(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 herecerr << 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 herefor (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-msmx = k-ms, mn = me; // [mn, mx]long long cnt = 0;for (int i = 1; i <= n; i++) scanf("%d", &s[i]), cnt += s[i]; // sleepfor (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;}