NOI 模拟赛
三道大原题,我就直接写了
T1 scoi2016 背单词
建一个 Trie 树,递推出每个点子树里单词节点的数量,把单词节点拿出来建个树形结构,所有单词节点向他上面最近的单词节点连边,每次贪心往比较小的那边走就可以了
不建树是错的,因为会把不同的单词节点算成一个
例如:
比如左边四个单词节点就被算到了一起,应该先从左边一个一个走,镘走了右边(#上香
#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 = 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 = 1000010; int n; char str[maxn]; int danger[maxn], id[maxn], tr[maxn][26], dfn, size[maxn]; vector<int> sons[maxn]; void Insert(char *str) { int len = strlen(str), now = 0; for(int i=len-1;~i;i--) { int pp = str[i] - 'a'; if(!tr[now][pp]) tr[now][pp] = ++dfn; now = tr[now][pp]; } danger[now] = 1; } int cmp(int x, int y) {return size[x] < size[y];} int pnt = 1; void dfs(int x, int cur) { if(danger[x]) sons[cur].push_back(++pnt), size[cur = pnt] = 1; rep(i, 0, 25) if(tr[x][i]) dfs(tr[x][i], cur); } void makesize(int x) { for(int i=0;i<(int)sons[x].size();i++) { makesize(sons[x][i]); size[x] += size[sons[x][i]]; } } int ind[maxn], pre, _tim; LL ans; void solve(int x, int wfa) { _tim++; ans += _tim - wfa; wfa = _tim; sort(sons[x].begin(), sons[x].end(), cmp); for(int i=0;i<(int)sons[x].size();i++) solve(sons[x][i], wfa); } int main() { //freopen("words.in","r",stdin); //freopen("words.out","w",stdout); n = read(); rep(i, 1, n) { scanf("%s", str); Insert(str); } dfs(0, 1); makesize(1); //return 0; solve(1, 1); cout << ans << endl; }
T2 sdoi2017 树点染色
LCT + 线段树套路题,30 分钟码完
#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 = 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 = 300010; int n, q; 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; } inline void ins(int u, int v) {add(u, v); add(v, u);} int anc[maxn], size[maxn], bl[maxn], pos[maxn], _tim, dep[maxn]; inline void dfs(int x) { size[x] = 1; for(int i=first[x];i;i=nx[i]) { if(to[i] == anc[x]) continue; anc[to[i]] = x; dep[to[i]] = dep[x] + 1; dfs(to[i]); size[x] += size[to[i]]; } } inline void dfs2(int x, int col) { bl[x] = col; int k = 0; pos[x] = ++_tim; for(int i=first[x];i;i=nx[i]) if(to[i] != anc[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] != anc[x] && to[i] != k) dfs2(to[i], to[i]); } int seg[maxn << 2], tag[maxn << 2], mxseg[maxn << 2]; inline void pushdown_seg(int x, int l, int r) { if(tag[x]) { int mid = (l + r) >> 1; tag[x << 1] += tag[x]; tag[x << 1| 1] += tag[x]; mxseg[x << 1] += tag[x]; mxseg[x << 1| 1] += tag[x]; tag[x] = 0; } } inline void update(int x, int l, int r, int L, int R, int v) { if(L <= l && r <= R) { tag[x] += v; mxseg[x] += v; return; } pushdown_seg(x, l, r); int mid = (l + r) >> 1; if(L <= mid) update(x << 1, l, mid, L, R, v); if(R > mid) update(x << 1 | 1, mid + 1, r, L, R, v); mxseg[x] = max(mxseg[x << 1], mxseg[x << 1 | 1]); } inline int querymx(int x, int l, int r, int L, int R) { if(L <= l && r <= R) { return mxseg[x]; } pushdown_seg(x, l, r); int mid = (l + r) >> 1, ans = 0; if(L <= mid) ans = max(ans, querymx(x << 1, l, mid, L, R)); if(R > mid) ans = max(ans, querymx(x << 1 | 1, mid + 1, r, L, R)); return ans; } inline void Mod(int x, int opt) { update(1, 1, n, pos[x], pos[x] + size[x] - 1, opt); } inline int lca(int x, int y) { while(bl[x] != bl[y]) { if(dep[bl[x]] < dep[bl[y]]) swap(x, y); x = anc[bl[x]]; } return dep[x] < dep[y] ? x : y; } inline int qn(int x) { return querymx(1, 1, n, pos[x], pos[x] + size[x] - 1); } #define ls (ch[x][0]) #define rs (ch[x][1]) int ch[maxn][2], fa[maxn], st[maxn], top, tg[maxn]; inline int isroot(int x) {return (ch[fa[x]][0] != x) && (ch[fa[x]][1] != x);} inline void pushup(int x) { tg[x] = x; if(tg[ls]) tg[x] = tg[ls]; } inline void rotate(int x) { int y = fa[x], z = fa[y]; int l = (ch[y][1] == x), r = l ^ 1; if(!isroot(y))ch[z][ch[z][1] == y] = x; fa[x] = z; fa[y] = x; fa[ch[x][r]] = y; ch[y][l] = ch[x][r]; ch[x][r] = y; pushup(y); pushup(x); } inline void splay(int x) { while(!isroot(x)) { int y = fa[x], z = fa[y]; if(!isroot(y)) { if(ch[z][0] == y ^ ch[y][0] == x) rotate(x); else rotate(y); } rotate(x); } } inline void access(int x) { for(int y=0;x;y=x,x=fa[x]) { splay(x); int tmp; if(rs) {tmp = tg[rs];Mod(tmp, 1);} if(y) {tmp = tg[y];Mod(tmp, -1);} rs = y; pushup(rs); } } int main() { //freopen("tree.in","r",stdin); //freopen("tree.out","w",stdout); dep[1] = 1; n = read(), q = read(); for(int i=2;i<=n;i++) { int u = read(), v = read(); ins(u, v); } dfs(1); dfs2(1, 1); rep(i, 1, n) { fa[i] = anc[i]; tg[i] = i; update(1, 1, n, pos[i], pos[i], dep[i]); } while(q--) { int opt = read(); if(opt == 1) { int x = read(); access(x); } if(opt == 2) { int x = read(), y = read(); int hx = lca(x, y); int t1 = querymx(1, 1, n, pos[x], pos[x]), t2 = querymx(1, 1, n, pos[y], pos[y]); int t3 = querymx(1, 1, n, pos[hx], pos[hx]); printf("%d\n", t1 + t2 - (t3 << 1) + 1); } if(opt == 3) { int x = read(); printf("%d\n", qn(x)); } } }
T3 hnoi2017 抛硬币
考场上只写了 30 ,还是太菜了 qnq
#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 = 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 = 100010; LL a, b; int mod; int ksm(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; } namespace solve1 { int cc[50][50]; void solve() { cc[0][0] = 1; rep(i, 1, 30) cc[i][0] = 1; rep(i, 1, 30) rep(j, 1, 30) cc[i][j] = (cc[i - 1][j] + cc[i - 1][j - 1]) % mod; int maxstate = (1 << a); int ans = 0; for(int S=1;S<maxstate;S++) { int nS = (LL)__builtin_popcount(S) - 1; dwn(i, nS, 0) (ans += cc[b][i]) %= mod; } if(mod == 10) printf("%01d", ans); if(mod == 100) printf("%02d", ans); if(mod == 1000) printf("%03d", ans); if(mod == 10000) printf("%04d", ans); if(mod == 100000) printf("%05d", ans); if(mod == 1000000) printf("%06d", ans); if(mod == 10000000) printf("%07d", ans); if(mod == 100000000) printf("%08d", ans); if(mod == 1000000000) printf("%09d", ans); cout << endl; } } namespace solve2 { int cc[500][500]; void solve() { cc[0][0] = 1; rep(i, 1, 130) cc[i][0] = 1; rep(i, 1, 130) rep(j, 1, 130) cc[i][j] = (cc[i - 1][j] + cc[i - 1][j - 1]) % mod; int ans = 0; rep(i, 1, a) { int res = 0; rep(j, 0, i-1) (res += cc[b][j]) %= mod; (ans += (1LL * cc[a][i] * res) % mod) %= mod; } if(mod == 10) printf("%01d", ans); if(mod == 100) printf("%02d", ans); if(mod == 1000) printf("%03d", ans); if(mod == 10000) printf("%04d", ans); if(mod == 100000) printf("%05d", ans); if(mod == 1000000) printf("%06d", ans); if(mod == 10000000) printf("%07d", ans); if(mod == 100000000) printf("%08d", ans); if(mod == 1000000000) printf("%09d", ans); cout << endl; } } int main() { while(scanf("%lld%lld%d", &a, &b, &mod) == 3 ) { mod = pow(10, mod); if(a <= 100 && b <= 100) solve2::solve(); else puts("QAQ"); } }
30pts就是:
$\sum\limits_{i=0}^a \sum\limits_{j=0}^{b} [i > j] \times C_a^i \times C_b^j$
也就是 $\sum\limits_{i=0}^a \sum\limits_{j=0}^{a-i} C_a^{i+j} \times C_b^{i-j}$
然后根据范德蒙德卷积(链接里的第一个)得到原式就是:$\sum\limits_{i=b+1}^{b+a} C_{a+b}^i$
然后就是组合数取模方面的东西了
先把式子拆成两部分,记 $c = \lceil \frac{a+b}{2} \rceil$,则原式 = $\sum\limits_{i=c}^{a+b} C_{a+b}^i + \sum\limits_{i=b+1}^{c} C_{a+b}^i$
后一项可以暴力,前一项当 $a+b$ 是奇数的时候是杨辉三角的一行和,也就是 $2^{a+b-1}$,当 $a+b$ 是偶数的时候,是 $2^{a+b-1} - \frac{1}{2} \times C_{a+b}^{\frac{a+b}{2}}$
然后需要扩展 lucas 求组合数,然而不会
。。
补档:扩展 lucas
要求 $C_n^m \space mod \space p$,$p$ 不一定是质数
我们可以唯一分解然后用 excrt 合并一下,现在问题就是求 $C_n^m \space mod \space p^k$,$p$ 是质数
根据组合数的定义式,我们只要快速求出模意义下的阶乘就可以了
举个简单的例子:
$22! \space mod \space 3^2$
发现 $22! = (3^7) \times (1 \times 2 \times 3 \times ... \times 7) \times (1 \times 2 \times 4 \times 5 \times 7 \times 8 \times 10 \times 11 \times 13 \times 14 \times 16 \times 17 \times 19 \times 22)$
第一个括号是 $p^{\lfloor \frac{n}{p} \rfloor}$,第二个括号是 $(\lfloor \frac{n}{p} \rfloor) !$ ,递归解决,第三个括号在模意义下存在一个循环,循环了 $\lfloor \frac{n}{p^k} \rfloor$ 次,每次是 $\prod\limits_{i=1}^{p^k} i \times [gcd(i,p)==1]$ ,可以先算这一个循环,然后算它的 $\lfloor \frac{n}{p^k} \rfloor$ 次幂,最后还剩了 $n \space mod \space p^k$ 项,注意到每一项有贡献当且仅当它与 $p$ 互质,暴力算即可
算组合数的时候,可以把所有 $p$ 提到分数外面,这样就有逆元了,也就是你要算 $\frac{\frac{n!}{p^{p_1}}}{\frac{m!}{p^{p_2}} \times \frac{(n-m)!}{p^{p_3}}} \times p^{p_1-p_2-p_3}$
$p_1,p_2,p_3$ 可以递推算一下
然后在算阶乘的时候其实并不用算第一部分,因为不算第一部分算出来的是 $\frac{n!}{p^{p_1}}$,就不用除了
复杂度大概是 $O(plogp)$
#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; } const int mod = 1e9, maxn = 2e6 + 10; LL a, b; int pp, k2, k5, fac2[maxn], fac5[maxn]; inline int ksm(int x, LL t, int md) { int res = 1; while (t) { if (t & 1) res = 1LL * res * x % md; x = 1LL * x * x % md; t = t >> 1; } return res; } inline void exgcd(LL a, LL b, LL &x, LL &y) { if(!b) { x = 1, y = 0; return; } exgcd(b, a%b, y, x); y -= a / b * x; } LL x, y; inline LL inv(LL a, LL md) { x = 0, y = 0; exgcd(a, md, x, y); return (x + md) % md; } void print(int ans) { // cout << pp << " " << ans << endl; int md = pow(10, pp); ans %= md; if (pp == 1) printf("%01d", ans); if (pp == 2) printf("%02d", ans); if (pp == 3) printf("%03d", ans); if (pp == 4) printf("%04d", ans); if (pp == 5) printf("%05d", ans); if (pp == 6) printf("%06d", ans); if (pp == 7) printf("%07d", ans); if (pp == 8) printf("%08d", ans); if (pp == 9) printf("%09d", ans); cout << endl; } LL fc1(LL n, int type) { LL ans = 0, p = type ? 2 : 5; for (LL i = n; i; i /= p) ans += i / p; return ans; } LL fc2(LL n, int type) { if(!n) return 1; int md = type ? k2 : k5; int s = type ? fac2[md] : fac5[md]; s = ksm(s, n / md, md); s = 1LL * s * (type ? fac2[n % md] : fac5[n % md]) % md; return s * fc2(n / (type ? 2 : 5), type) % md; } int aa, bb; LL tms, cur, ret; int C(LL n, LL m, int type, int div) { int md = type ? k2 : k5; if (n < m) return 0; tms = fc1(n, type) - fc1(m, type) - fc1(n - m, type); if (div && type) tms--; cur = ksm((type ? 2 : 5), tms, md); if(div && !type)cur = cur * inv(2, md); if (tms >= 9) return 0; ret = fc2(n, type) * inv(fc2(m, type), md) % md * inv(fc2(n - m, type), md) % md * cur % md; return ret; } int getC(LL n, LL m, int div) { int ans = 0; aa = C(n, m, 1, div), bb = C(n, m, 0, div); aa = aa * (mod/k2) %mod * inv(mod/k2, k2) %mod; ans = (ans + aa) %mod; bb = bb * (mod/k5) %mod * inv(mod/k5, k5) %mod; ans = (ans + bb) %mod; return ans; } int main() { k2 = ksm(2, 9, 1e9); k5 = ksm(5, 9, 1e9); fac2[0] = fac5[0] = 1; rep(i, 1, k2) if (i % 2) fac2[i] = 1LL * fac2[i - 1] * i % k2; else fac2[i] = fac2[i - 1]; rep(i, 1, k5) if (i % 5) fac5[i] = 1LL * fac5[i - 1] * i % k5; else fac5[i] = fac5[i - 1]; while (scanf("%lld%lld%d", &a, &b, &pp) == 3) { int ans = ksm(2, a + b - 1, 1e9); for (LL i = b + 1; i <= (a + b) / 2; i++) (ans += getC(a + b, i, 0)) %= mod; if (!((a + b) & 1)) (ans += (mod - getC(a + b, ((a + b) >> 1), 1))) %= mod; print(ans); } }
#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; } LL n, m, p; inline LL ksm(LL x, LL t, LL mod) { LL res = 1; while(t) { if(t & 1) (res *= x) %= mod; (x *= x) %= mod; t = t >> 1; } return res; } inline void exgcd(LL a, LL b, LL &x, LL &y) { if(!b) { x = 1, y = 0; return; } exgcd(b, a%b, y, x); y -= (a / b) * x; } inline LL inv(LL a, LL mod) { LL x, y; exgcd(a, mod, x, y); return (x + mod) % mod; } #define mp make_pair pair<LL, LL> a, b; inline LL fc1(LL n, LL p) { int ans = 0; for(LL i = n; i; i /= p) ans += (i / p); return ans; } inline LL fc2(LL n, LL p, LL pr) { if(!n) return 1; LL s = 1; rep(i, 1, pr) if(i % p) s = 1LL * s * i % pr; s = ksm(s, n / pr, pr); rep(i, 1, n % pr) if(i % p) s = 1LL * s * i % pr; return s * fc2(n / p, p, pr) % pr; } inline LL C(LL n, LL m, LL p, LL tms) { LL pr = pow(p, tms), px = fc1(n, p) - fc1(m, p) - fc1(n - m, p); if(px >= tms) return 0; LL res = fc2(n, p, pr) * inv(fc2(m, p, pr), pr) % pr * inv(fc2(n - m, p, pr), pr) % pr; return ksm(p, px, pr) * res % pr; } pair<LL, LL> merge(pair<LL, LL> a, pair<LL, LL> b) { LL t = __gcd(a.second, b.second); LL gm = b.second / t, M = gm * a.second; pair<LL, LL> c; c.first = ((LL) inv(a.second / t, gm) * ((b.first - a.first) / t) % gm * a.second + a.first) % M; c.second = M; if(c.first < 0) c.first += M; return c; } int main() { cin >> n >> m >> p; a = mp(0, 1); for(LL i=2;i<=p;i++) if(p % i == 0) { LL tms = 0, cur = 1; while(p % i == 0) tms++, cur *= i, p /= i; b = mp(C(n, m, i, tms), cur); a = merge(a, b); } cout << a.first << endl; }
代码暂时没拿到,先咕,明天补
我卢本伟没有咕咕