NOI 模拟赛
T1 binary
求有多少不同构的二叉树最长链为 $n$,答案对 $10^k$ 取模
$n \leq 200000,k \leq 64$
sol:
首先转化一下,原题要求的链长是边数,转换成点数好做一点,把 n+1 即可
记 $f_{(i,0)}$ 表示最大深度为 $i$,当前没有长为 $n$ 的链,有多少不同构二叉树
$f_{(i,1)}$ 表示最大深度为 $i$,当前有长为 $n$ 的链,有多少不同构二叉树
因为只有二叉,考虑将两个二叉树的根向上连出一个新点,转移答案
枚举这个新的二叉树深度,显然原来两个二叉树至少有一个深度为 $i-1$
如果新的二叉树有长为 $n$ 的链,分两种情况
1.原来有一个二叉树有长为 $n$ 的链,不妨设在左边,这样只要保证右边跟左边拼出来的链长度不超过 $n$ 就可以了,显然右边深度不超过 $min(n-i,i-1)$,因为左右对称,要乘 2 ,之后因为乘了 2 ,要减去左右两边深度一样的情况,而这种情况在 $n-i<i-1$ 的时候是不存在的
2.两边都没有长为 $n$ 的链,那就一定要拼出来一条长度为 $n$ 的链,左边是 $i-1$ ,右边就是 $n-i$ ,注意 $i-1$ 要大于等于 $n-i$,等于的时候只有一种情况,不等于可以交换左右儿子
没有的话,保证拼不出来即可,也就是如果左边是 $i-1$ ,右边的上界就是 $min(n-i-1,i-1)$ ,还是可以交换左右,注意当 $i-1 \leq n-i-1$ 的时候要排除两边一样的情况
前缀和优化 dp 即可,然后因为 k 是 64 ,需要高精度
但我不想写
#include<bits/stdc++.h> #define LL long long using namespace std; inline LL read() { LL x = 0,f = 1;char ch = getchar(); for(;!isdigit(ch);ch = getchar())if(ch == '-') f = -f; for(;isdigit(ch);ch = getchar())x = 10 * x + ch - '0'; return x * f; } const int maxn = 200010; LL n,k,mod = 1; LL f[maxn][2],sum[maxn][2]; namespace subtask_low { int main() { for(int i=1;i<=k;i++)mod *= 10; f[0][0] = 1; sum[0][0] = 1; for(LL i=1;i<=n;i++) { if(i != n)f[i][0] = (f[i][0] + (2 * f[i - 1][0] * sum[min(n - i - 1,i - 1)][0]) % mod) % mod; if((i - 1) < (n - i))f[i][0] = (f[i][0] + mod - ((f[i - 1][0] * f[i - 1][0]) % mod)) % mod; f[i][1] = (f[i][1] + (2 * (f[i - 1][1] * ((sum[min(n-i,i-1)][0] + sum[min(n-i,i-1)][1]) % mod)) % mod) % mod) % mod; if((i - 1) <= (n - i))f[i][1] = (f[i][1] + mod - (f[i - 1][1] * f[i - 1][1] % mod)) % mod; if((i - 1) >= (n - i))f[i][1] = (f[i][1] + (2 * f[i - 1][0] * f[n - i][0]) % mod) % mod; if((i - 1) == (n - i))f[i][1] = (f[i][1] + mod - ((f[i - 1][0] * f[n - i][0]) % mod)) % mod; sum[i][0] = (sum[i - 1][0] + f[i][0]) % mod; sum[i][1] = (sum[i - 1][1] + f[i][1]) % mod; //cout << f[i][0] << " " << f[i][1] << endl; } cout << sum[n][1] << endl; return 0; } } int main() { freopen("binary.in","r",stdin); freopen("binary.out","w",stdout); n = read() + 1,k = read(); subtask_low::main(); }
T2 sort
求一个有向图字典序最小的拓扑序和字典序最大的拓扑序
$n \leq 200000,m=n$
sol:
送分题,字典序最大的拓扑序直接用个堆,字典序最小的就建反图用个堆
#include<bits/stdc++.h> #define LL long long using namespace std; inline int read() { int x = 0,f = 1;char ch = getchar(); for(;!isdigit(ch);ch = getchar())if(ch == '-') f = -f; for(;isdigit(ch);ch = getchar())x = 10 * x + ch - '0'; return x * f; } const int maxn = 200010; int n,a[maxn]; priority_queue<int> q; int first[maxn],to[maxn << 1],nx[maxn << 1],cnt,ind[maxn],ans[maxn],pnt,reh[maxn]; inline void add(int u,int v){to[++cnt] = v;nx[cnt] = first[u];first[u] = cnt;} int main() { freopen("sort.in","r",stdin); freopen("sort.out","w",stdout); n = read(); for(int i=1;i<=n;i++)a[i] = read(); memset(ans,0,sizeof(ans));memset(reh,0,sizeof(reh));pnt = 0; for(int i=1;i<=n;i++)if(a[i]){add(i,a[i]);ind[a[i]]++;} for(int i=1;i<=n;i++)if(ind[i] == 0)q.push(i); while(!q.empty()) { int now = q.top();q.pop(); //cout << now << " "; ans[++pnt] = now; for(int i=first[now];i;i=nx[i]){ind[to[i]]--;if(ind[to[i]] == 0)q.push(to[i]);} } for(int i=1;i<=n;i++)reh[ans[n - i + 1]] = i; for(int i=1;i<=n;i++)cout << reh[i] << " "; cout << endl; memset(first,0,sizeof(first));cnt = 0;memset(ind,0,sizeof(ind)); memset(ans,0,sizeof(ans));memset(reh,0,sizeof(reh));pnt = 0; for(int i=1;i<=n;i++)if(a[i]){add(a[i],i);ind[i]++;} for(int i=1;i<=n;i++)if(ind[i] == 0)q.push(i); while(!q.empty()) { int now = q.top();q.pop(); // cout << now << " "; ans[++pnt] = now; for(int i=first[now];i;i=nx[i]) { ind[to[i]]--; if(ind[to[i]] == 0)q.push(to[i]); } } for(int i=1;i<=n;i++)reh[ans[i]] = i; for(int i=1;i<=n;i++)cout << reh[i] << " "; cout << endl; //for(int i=pnt;i;i--)cout << ans[i] << " "; //cout << endl; memset(first,0,sizeof(first));cnt = 0;memset(ind,0,sizeof(ind)); }
T3 mst
$q$ 次询问图中删掉一条边的最小生成树边权和,或者输出 "Not Connected"
$n,m,q \leq 100000$
sol:
每条树边记录一下能覆盖它的非树边的最小权值
用树剖套线段树区间修改单点查询
#include<bits/stdc++.h> #define LL long long #define maxn 250010 #define lson l, mid, x << 1 #define rson mid + 1, r, x << 1 | 1 using namespace std; inline int read() { int x = 0,f = 1;char ch = getchar(); for(;!isdigit(ch);ch = getchar())if(ch == '-') f = -f; for(;isdigit(ch);ch = getchar())x = 10 * x + ch - '0'; return x * f; } struct data { int x, y, z, id; bool vis; } a[maxn << 1]; int f[maxn], head[maxn], to[maxn << 1], val[maxn << 1], nx[maxn << 1], cnt, reh[maxn << 1], fa[maxn], deep[maxn], si[maxn], bl[maxn], pos[maxn], tot, mn[maxn << 2], n; bool cmp1(const data &a, const data &b) { return a.z < b.z; } bool cmp2(const data &a, const data &b) { return a.id < b.id; } int find(int x) { return x == f[x] ? x : f[x] = find(f[x]); } void add(int x, int y, int z) { to[++cnt] = y, val[cnt] = z, nx[cnt] = head[x], head[x] = cnt; } void dfs1(int x) { int i; si[x] = 1; for (i = head[x]; i; i = nx[i]) if (to[i] != fa[x]) fa[to[i]] = x, deep[to[i]] = deep[x] + 1, reh[val[i]] = to[i], dfs1(to[i]), si[x] += si[to[i]]; } void dfs2(int x, int c) { int i, k = 0; bl[x] = c, pos[x] = ++tot; for (i = head[x]; i; i = nx[i]) if (to[i] != fa[x] && si[to[i]] > si[k]) k = to[i]; if (k) { dfs2(k, c); for (i = head[x]; i; i = nx[i]) if (to[i] != fa[x] && to[i] != k) dfs2(to[i], to[i]); } } void update(int b, int e, int a, int l, int r, int x) { if (b <= l && r <= e) { mn[x] = min(mn[x], a); return; } int mid = (l + r) >> 1; if (b <= mid) update(b, e, a, lson); if (e > mid) update(b, e, a, rson); } int query(int p, int l, int r, int x) { if (l == r) return mn[x]; int mid = (l + r) >> 1; if (p <= mid) return min(mn[x], query(p, lson)); else return min(mn[x], query(p, rson)); } void modify(int x, int y, int v) { while (bl[x] != bl[y]) { if (deep[bl[x]] < deep[bl[y]]) swap(x, y); update(pos[bl[x]], pos[x], v, 1, n, 1), x = fa[bl[x]]; } if (deep[x] > deep[y]) swap(x, y); if (x != y) update(pos[x] + 1, pos[y], v, 1, n, 1); } signed main() { freopen("mst.in","r",stdin); freopen("mst.out","w",stdout); int m, q, x, v, c = 0; LL sum = 0; n = read(),m = read(); for (int i = 1; i <= m; i++) a[i].x = read(), a[i].y = read(), a[i].z = read(), a[i].id = i; sort(a + 1, a + m + 1, cmp1); for (int i = 1; i <= n; i++) f[i] = i; for (int i = 1; i <= m; i++) if (find(a[i].x) != find(a[i].y)) sum += a[i].z, a[i].vis = 1, add(a[i].x, a[i].y, a[i].id), add(a[i].y, a[i].x, a[i].id), f[f[a[i].x]] = f[a[i].y], c++; if (c < n - 1) { q = read(); while (q--) puts("Not connected"); return 0; } dfs1(1), dfs2(1, 1); memset(mn, 127, sizeof(mn)); sort(a + 1, a + m + 1, cmp2); for (int i = 1; i <= m; i++) if (!a[i].vis) modify(a[i].x, a[i].y, a[i].z); q = read(); while (q--) { x = read(); if (!a[x].vis) printf("%lld\n", sum); else { v = query(pos[reh[x]], 1, n, 1); if (v == 2139062143) puts("Not connected"); else printf("%lld\n", sum - a[x].z + v); } } return 0; }