NOI 模拟赛
T1
给一棵树,每次求链第 $k$ 小,之后把这条链赋值为 $(ans \times x + y)\space mod \space p$
$n \leq 200000$
sol:
树剖,每条重链维护一个珂朵莉树,暴力就完事了
这题不随机复杂度也是对的,因为每次操作完必推平,相当于每次操作最多增加常数个信息量,而珂朵莉树的复杂度是 $O(信息量)$(相同信息会缩到一起),最坏复杂度 $O((n+q)log^2n)$
#include<bits/stdc++.h> #define LL long long #define rep(i,s,t) for(register int i = (s),i##end = (t); i <= i##end; ++i) #define dwn(i,s,t) for(register int i = (s),i##end = (t); i >= i##end; --i) #define int long long using namespace std; inline int read() { int x=0,f=1;char ch; for(ch=getchar();!isdigit(ch);ch=getchar())if(ch=='-')f=-f; for(;isdigit(ch);ch=getchar())x=10*x+ch-'0'; return x*f; } const int maxn = 400010; int n, x, y, p, w[maxn]; int first[maxn], to[maxn << 1], nx[maxn << 1], cnt; inline void add(int u, int v) { to[++cnt] = v; nx[cnt] = first[u]; first[u] = cnt; } int size[maxn], fa[maxn], dep[maxn], bl[maxn], pos[maxn], dfn, reg[maxn]; void dfs1(int x) { size[x] = 1; for(int i=first[x];i;i=nx[i]) { if(to[i] == fa[x]) continue; fa[to[i]] = x; dep[to[i]] = dep[x] + 1; dfs1(to[i]); size[x] += size[to[i]]; } } void dfs2(int x, int col) { bl[x] = col; pos[x] = ++dfn; reg[dfn] = x; int k = 0; for(int i=first[x];i;i=nx[i]) if(to[i] != fa[x] && size[to[i]] > size[k]) k = to[i]; if(!k) return; dfs2(k, col); for(int i=first[x];i;i=nx[i]) if(to[i] != fa[x] && to[i] != k) dfs2(to[i], to[i]); } #define ls (x << 1) #define rs ((x << 1) | 1) #define pii pair<int, int> #define mp make_pair pii seg[maxn << 2]; int tag[maxn << 2]; inline int isfull(int x, int l, int r) { return (seg[x].second == (r - l + 1)); } void pushup(int x, int l, int r) { int mid = (l + r) >> 1; if(seg[ls].first != -1 && seg[rs].first != -1 && seg[ls].first == seg[rs].first) { seg[x].first = seg[ls].first; seg[x].second = (r - l + 1); } else seg[x].first = -1; } void pushdown(int x, int l, int r) { if(tag[x]) { int mid = (l + r) >> 1; seg[ls].first = tag[x]; seg[rs].first = tag[x];tag[ls]=tag[rs]=tag[x]; seg[ls].second = (mid - l + 1), seg[rs].second = r - mid; tag[x] = 0; } } void build(int x, int l, int r) { seg[x].first = seg[x].second = -1; if(l == r) { seg[x] = mp(w[reg[l]], 1); return; } int mid = (l + r) >> 1; build(ls, l, mid); build(rs, mid + 1, r); pushup(x, l, r); } struct seq { int l, r, v; seq(){} seq(int _1, int _2, int _3): l(_1), r(_2), v(_3) {} bool operator < (const seq &b) const { return v < b.v; } }st[maxn]; int top; void update(int x, int l, int r, int L, int R, int v) { if(L <= l && r <= R) { tag[x] = v; seg[x].first = v; seg[x].second = r - l + 1; return; } pushdown(x, l, r); int mid = (l + r) >> 1; if(L <= mid) update(ls, l, mid, L, R, v); if(R > mid) update(rs, mid + 1, r, L, R, v); pushup(x, l, r); } void query(int x, int l, int r, int L, int R) { if(L <= l && r <= R) { if(seg[x].first != -1) { st[++top] = seq(l, r, seg[x].first); return; } } pushdown(x, l, r); int mid = (l + r) >> 1; if(L <= mid) query(ls, l, mid, L, R); if(R > mid) query(rs, mid+1, r, L, R); } int qry(int a, int b, int c) { top = 0; while(bl[a] != bl[b]) { if(dep[bl[a]] < dep[bl[b]]) swap(a, b); query(1, 1, n, pos[bl[a]], pos[a]); a = fa[bl[a]]; } if(pos[a] > pos[b]) swap(a, b); query(1, 1, n, pos[a], pos[b]); sort(st + 1, st + top + 1); rep(i, 1, top) { int len = st[i].r - st[i].l + 1; c -= len; if(c <= 0) return st[i].v; } } int upd(int a, int b, int c) { while(bl[a] != bl[b]) { if(dep[bl[a]] < dep[bl[b]]) swap(a, b); update(1, 1, n, pos[bl[a]], pos[a], c); a = fa[bl[a]]; } if(pos[a] > pos[b]) swap(a, b); update(1, 1, n, pos[a], pos[b], c); } signed main() { n = read(), x = read(), y = read(), p = read(); rep(i, 1, n) w[i] = read(); rep(i, 2, n) { int u = read(), v = read(); add(u, v); add(v, u); } dfs1(1); dfs2(1, 1); build(1, 1, n); int q = read(); while(q--) { int a = read(), b = read(), c = read(); int res = qry(a, b, c); printf("%lld\n", res); //cout << "upd: " << (1LL * x * res + y) % p + 1 << endl; upd(a, b, (1LL * x * res + y) % p + 1); } }
有点慌,写的很丑,维护了很多没必要的东西
T2
给 $n$ 个长方体箱子,把它们套在一起,每个箱子里最多有一个箱子(不一定是一层箱子,也就是箱套箱可以放在箱子里),箱子不能旋转,长宽分别小于等于才能套,求最小总占地面积
$n \leq 200$
sol:
首先肯定是算总和 - 最大节省量
1.可以将箱子间的二维偏序关系看成一个有向图,然后最大费用可行流
2.可以将箱子间的嵌套关系看成二分图匹配,求一个二分图最优匹配就完事了
3.可以将每个点拆成出入两个点,每个点限流 $1$,相邻的点连边,$S->入$ ,$出->T$,最大费用可行流
爱咋做咋做,别 dp 就行
#include<bits/stdc++.h> #define LL long long #define rep(i,s,t) for(register int i = (s),i##end = (t); i <= i##end; ++i) #define dwn(i,s,t) for(register int i = (s),i##end = (t); i >= i##end; --i) using namespace std; inline int read() { int x=0,f=1;char ch; for(ch=getchar();!isdigit(ch);ch=getchar())if(ch=='-')f=-f; for(;isdigit(ch);ch=getchar())x=10*x+ch-'0'; return x*f; } int n; struct pos { int a,b; bool operator < (const pos &y) const { if(a == y.a) return b < y.b; else return a < y.a; } bool operator == (const pos &y) const { return a == y.a && b == y.b; } }ps[207]; const int maxn = 400010; struct ZKW { #define oo 2147483233 int n, m, s, t, ans, cost; int head[maxn], nx[maxn]; struct Edge { int from, to, caps, cost; Edge() {} Edge(int _1, int _2, int _3, int _4) : from(_1), to(_2), caps(_3), cost(_4) {} } es[maxn]; void add(int u, int v, int w, int c) { es[m] = Edge(u, v, w, c); nx[m] = head[u]; head[u] = m++; es[m] = Edge(v, u, 0, -c); nx[m] = head[v]; head[v] = m++; } ZKW() { m = 0; ans = 0; cost = 0; memset(head, -1, sizeof(head)); } queue<int> q; int inq[maxn], dis[maxn], vis[maxn]; int BFS() { for (int i = 0; i <= n; i++) dis[i] = -oo; dis[t] = 0; q.push(t); inq[t] = 1; while (!q.empty()) { int now = q.front(); q.pop(); inq[now] = 0; for (int i = head[now]; i != -1; i = nx[i]) { Edge &e = es[i ^ 1]; if (dis[e.from] < dis[now] + e.cost && e.caps) { dis[e.from] = dis[now] + e.cost; if (!inq[e.from]) { q.push(e.from); inq[e.from] = 1; } } } } if (dis[s] <= 0)return 0; cost = dis[s]; return 1; } int DFS(int u, int a) { if (u == t || !a) { ans -= cost * a; return a; } if (vis[u]) return 0; vis[u] = 1; int flow = 0, f; for (int i = head[u]; i != -1; i = nx[i]) { Edge &e = es[i]; if (dis[e.to] == dis[u] - e.cost && (f = DFS(e.to, min(e.caps, a)))) { flow += f; a -= f; e.caps -= f; es[i ^ 1].caps += f; if (a == 0) return flow; } } return flow; } int MCMF(int _s, int _t) { s = _s, t = _t; int flow = 0, f; while (BFS()) { do { memset(vis, 0, sizeof(vis)); f = DFS(s, oo); flow += f; } while (f); } return flow; } } sol; int main() { n = read(); rep(i, 1, n) ps[i].a = read(), ps[i].b = read(); sort(ps + 1, ps + n + 1); n = unique(ps + 1 , ps + n + 1) - ps - 1; int S = n + n + 1, T = S + 1; sol.n = T + 2; rep(i, 1, n) { sol.ans+=ps[i].a*ps[i].b; rep(j, 1, n) if(i!=j&&ps[i].a<=ps[j].a&&ps[i].b<=ps[j].b) sol.add(i, j+n, 1, ps[i].a*ps[i].b); sol.add(S,i,1,0); sol.add(i+n,T,1,0); //sol.add(i,T,1,0); }sol.MCMF(S, T); cout << sol.ans << endl; }
T3
求数列 $k$ 次方和
记 $f(x)$ 为 $k$ 次方和的生成函数,$g(x)$ 为从数列中选出 $k$ 个不同的数乘起来的生成函数
则 $f(n) = \sum\limits_{i=1}^{n-1} f(i) \times g(n-i) \times (-1)^{n-i+1} + n \times g(n)$
而 $g(x) = \prod\limits_{i=1}^n (a_i+1)$ 这个生成函数可以分治 + 卷积
$f(x)$ 可以 cdq 分治/多项式求逆
#include <bits/stdc++.h> #define LL long long #define rep(i, s, t) for (register int i = (s), i##end = (t); i <= i##end; ++i) #define dwn(i, s, t) for (register int i = (s), i##end = (t); i >= i##end; --i) using namespace std;/* const int Size=1<<16; char buffer[Size],*head,*tail; inline char Getchar() { if(head==tail) { int l=fread(buffer,1,Size,stdin); tail=(head=buffer)+l; } if(head==tail) return -1; return *head++; }*/ inline int read() { int x=0,f=1;char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } const int mod = 998244353, maxn = 800010; int a[maxn], r[maxn], lg[maxn], n, k; inline int skr(int x, int t) { int res = 1; while (t) { if (t & 1) res = 1LL * res * x % mod; x = 1LL * x * x % mod; t = t >> 1; } return res; } int wn[maxn], iwn[maxn]; void init(int n) { wn[0] = iwn[0] = 1; rep(i, 1, n-1) wn[i] = skr(3, (mod - 1) / (i << 1)); rep(i, 1, n-1) iwn[i] = skr(332748118, (mod - 1) / (i << 1)); } inline void fft_init(int n) {rep(i, 0, n - 1) r[i] = (r[i >> 1] >> 1) | ((i & 1) << (lg[n] - 1));} inline void fft(int *a, int n, int type) { rep(i, 0, n - 1) if (i < r[i]) swap(a[i], a[r[i]]); for (int i = 1; i < n; i <<= 1) { //int wn = skr(3, (mod - 1) / (i << 1)); //if (type == -1) // wn = skr(wn, mod - 2); int twn = (type == -1) ? iwn[i] : wn[i]; for (int j = 0; j < n; j += (i << 1)) { int w = 1; for (int k = 0; k < i; k++, w = 1LL * w * twn % mod) { int x = a[j + k], y = 1LL * w * a[j + k + i] % mod; a[j + k] = (x + y) % mod; a[j + k + i] = (x - y + mod) % mod; } } } if (type == -1) { int inv_n = skr(n, mod - 2); rep(i, 0, n - 1) a[i] = 1LL * a[i] * inv_n % mod; } } int A[maxn], B[maxn]; int C[maxn], D[maxn]; int mul(int *A, int *B, int len) { fft_init(len); // fft_init(len); fft(A, len, 1); // for(int i=0;i<len;i++)cout<<A[i]<<" "; // cout<<endl; fft(B, len, 1); for (int i = 0; i < len; i++) A[i] = (LL)A[i] * B[i] % mod; fft(A, len, -1); --len; while (!A[len]) --len; return len; } vector<int> poly[maxn]; int solve(int l, int r) { if (l == r) return poly[l].size() - 1; int mid = (l + r) >> 1; int ls = solve(l, mid), rs = solve(mid + 1, r); int L = 1; for (; L <= ls + rs; L <<= 1) ; for (int i = 0; i <= ls; i++) A[i] = poly[l][i]; for (int i = ls + 1; i < L; i++) A[i] = 0; for (int i = 0; i <= rs; i++) B[i] = poly[mid + 1][i]; for (int i = rs + 1; i < L; i++) B[i] = 0; poly[l].clear(); poly[mid + 1].clear(); L = mul(A, B, L); for (int i = 0; i <= L; i++) poly[l].push_back(A[i]); return L; } int g[maxn], f[maxn]; void mulfac(int *A, int *B, int len) { fft_init(len); fft(A, len, 1); fft(B, len, 1); for (int i = 0; i < len; i++) A[i] = 1LL * A[i] * B[i] % mod; fft(A, len, -1); } void cdq_fft(int *f, int *g, int l, int r) { if (l == r) { (f[l] += (1LL * l * g[l] % mod)) %= mod; return; } int mid = (l + r) >> 1; cdq_fft(f, g, l, mid); int len = 1, ls = 0, rs = 0; // for(;len <= ((r - l + mid)<<1);len <<= 1); // for(int i=0;i<len;i++)A[i] = B[i] = 0; for (int i = l; i <= mid; i++) C[ls++] = f[i]; for (int i = 1; i <= r - l; i++) D[rs++] = g[i]; for (; len <= (ls + rs - 1); len <<= 1) ; mulfac(C, D, len); for (int i = mid + 1; i <= r; i++) f[i] = (f[i] + C[i - l - 1]) % mod; for (int i = 0; i < len; i++) C[i] = D[i] = 0; cdq_fft(f, g, mid + 1, r); } int main() { //freopen("1.in","r",stdin); //freopen("1.out","w",stdout); lg[0] = -1; init(1 << 19); rep(i, 1, maxn - 1) lg[i] = lg[i >> 1] + 1; n = read(); for (int i = 1; i <= n; i++) { a[i] = read(); if(a[i] >= mod) a[i] -= mod; poly[i].push_back(1); poly[i].push_back(a[i]); } solve(1, n); for (int i = 0; i < poly[1].size(); i++) g[i] = (((i & 1) ? 1 : (-1)) * poly[1][i] + mod) % mod; poly[1].clear(); cdq_fft(f, g, 0, n); for (int i = 1; i <= n; i++) printf("%d\n", f[i]); }
这场出了 T2 和 T3
难度非常悬殊的两道题,一道只有一个人没 A,一道只有一个人 A 了
过于真实,引起不适
不出题了QAQ