湖师大集训记录
lyoi303:「2017 山东一轮集训 Day1」Sum
吕老板讲的方法只有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,复杂度少一个
log
…
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;
}
lyoi306「2017 山东一轮集训 Day2」Pair
考虑匹配条件
ai+bj≥h
,令
Bj=h−bj
也就是
ai≥Bj
。显然,一个序列匹配当且仅当排序后匹配。考虑排序后
n
个
#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;
}
lyoi323. 「2017 山东一轮集训 Day7」养猫
神奇的费用流…最大流限制/费用计算的好模型。
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;
}
loj#6060. 「2017 山东一轮集训 Day1」Set
首先有
x1⊕x2
为定值,设为
x
。考虑
做法1:首先取出
x
为0的位做一遍最大异或找到
// 但这个思路扩展性更好,可以出道题233
做法2:考虑统一处理,在贪心求最大异或的时候改变贪心顺序,首先是 x 为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;
}
loj #6068. 「2017 山东一轮集训 Day4」棋盘
套路题。首先行列划分,中间连线表示格子,考虑计算代价。不妨在
可以动态加边,但不加也过了。
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 3005;
struct node {
int to, next, flow, cost, neg;
} edge[MAXN*3005];
int head[MAXN], top = 0;
inline void push(int i, int j, int f, int 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;
}
int dis[MAXN], vis[MAXN];
queue<int> que;
int pre[MAXN], pre_edge[MAXN];
const int S = MAXN-1, T = MAXN-2, inf = 233333333;
bool spfa(int &flow, int &cost)
{
memset(dis, 127/3, sizeof dis), memset(vis, 0, sizeof vis);
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 == 0 || 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 maxf = inf;
for (int i = T; i != S; i = pre[i]) maxf = min(maxf, edge[pre_edge[i]].flow);
for (int i = T; i != S; i = pre[i]) {
edge[pre_edge[i]].flow -= maxf;
edge[edge[pre_edge[i]].neg].flow += maxf;
}
flow += maxf, cost += maxf*dis[T];
return 1;
}
void mcf(int &flow, int &cost)
{ while (spfa(flow, cost)); }
int top_pt = 0;
int n;
char str[55][55];
int ptlr[55][55], ptud[55][55];
int cnt = 0, g[MAXN], s = T-1;
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%s", str[i]+1);
for (int j = 1; j <= n; j++)
if (str[i][j] == '.')
cnt++;
}
for (int i = 1; i <= n; i++) {
int pt = 1;
while (pt <= n) {
while (pt <= n && str[i][pt] == '#') pt++;
if (pt > n) continue;
++top_pt;
for (int j = 0; j <= n; j++)
push(s, top_pt, 1, j);
while (pt <= n && str[i][pt] == '.') ptlr[i][pt++] = top_pt;
}
}
for (int i = 1; i <= n; i++) {
int pt = 1;
while (pt <= n) {
while (pt <= n && str[pt][i] == '#') pt++;
if (pt > n) continue;
++top_pt;
for (int j = 0; j <= n; j++)
push(top_pt, T, 1, j);
while (pt <= n && str[pt][i] == '.') ptud[pt++][i] = top_pt;
}
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
if (str[i][j] == '.')
push(ptlr[i][j], ptud[i][j], 1, 0);
}
}
g[0] = 0;
int flow = 0, cost = 0;
for (int i = 1; i <= cnt; i++) {
push(S, s, 1, 0);
mcf(flow, cost);
//cerr << flow << endl;
g[i] = cost;
}
int k; scanf("%d", &k);
while (k--) {
int t; scanf("%d", &t);
printf("%d\n", g[t]);
}
return 0;
}
loj#2254. 「SNOI2017」一个简单的询问
考虑莫队。想到 get(l,r,x)=get(1,r,x)−get(1,l−1,x) 就很简单了。
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 50005;
struct query {
int l1, r1, l2, r2;
} qy[MAXN];
int sqrtN;
int n, q;
struct qs {
int r1, r2;
int id, num; // num = 0,1,2,3
friend bool operator < (const qs &a, const qs &b)
{
if (a.r1/sqrtN == b.r1/sqrtN) return a.r2 < b.r2;
return a.r1 < b.r1;
}
} qys[MAXN*4];
int qtop = 0;
long long ans[MAXN][4];
int a[MAXN];
int A[MAXN], B[MAXN]; // 出现次数
void solve_q()
{
sort(qys+1, qys+qtop+1);
int L = qys[1].r1, R = qys[1].r2;
for (int i = 1; i <= L; i++) A[a[i]]++;
for (int j = 1; j <= R; j++) B[a[j]]++;
long long Ans = 0;
for (int i = 1; i <= n; i++) Ans += (long long)A[i]*B[i];
for (int i = 1; i <= qtop; i++) {
// cerr << qys[i].r1 << " -- " << qys[i].r2 << " " << A[1] << " " << B[1] << endl;
while (L < qys[i].r1) L++, A[a[L]]++, Ans += B[a[L]];
while (L > qys[i].r1) A[a[L]]--, Ans -= B[a[L]], L--;
while (R < qys[i].r2) R++, B[a[R]]++, Ans += A[a[R]];
while (R > qys[i].r2) B[a[R]]--, Ans -= A[a[R]], R--;
// cerr << Ans << endl;
ans[qys[i].id][qys[i].num] = Ans;
}
}
void solve()
{
for (int i = 1; i <= q; i++) {
qys[++qtop] = (qs) {qy[i].r1, qy[i].r2, i, 0};
if (qy[i].l2 != 1) qys[++qtop] = (qs) {qy[i].r1, qy[i].l2-1, i, 1};
else ans[i][1] = 0;
if (qy[i].l1 != 1) qys[++qtop] = (qs) {qy[i].l1-1, qy[i].r2, i, 2};
else ans[i][2] = 0;
if (qy[i].l1 != 1 && qy[i].l2 != 1) qys[++qtop] = (qs) {qy[i].l1-1, qy[i].l2-1, i, 3};
else ans[i][3] = 0;
}
solve_q();
for (int i = 1; i <= q; i++)
printf("%lld\n", ans[i][0]-ans[i][1]-ans[i][2]+ans[i][3]);
}
int main()
{
scanf("%d", &n), sqrtN = ceil(sqrt(n));
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
scanf("%d", &q);
for (int i = 1; i <= q; i++)
scanf("%d%d%d%d", &qy[i].l1, &qy[i].r1, &qy[i].l2, &qy[i].r2);
solve();
return 0;
}