bzoj刷题记录2017.4.2-4.4
2017-04-02 21:49
155 查看
bzoj刷题记录 4.2-4.4
月考完了。凭借多年应试经验连蒙带猜应付掉,然而坠稳的数学跪烂[没考过班里要学文的dalao]…还是认真刷OJ吧。bzoj1211:[HNOI2004]树的计数
从Zars19的blog里学习了一个prufer编码。经典文章是matrix67 dalao的介绍,传送门。总之就是把无根有标号树和长度为n−2的数组建立了双射,然后树上计数就变成序列计数了。总方案是:
(n−2d1−1)(n−2−(d1−1)d2−1)(n−2−(d1−1)−(d2−1)d3−1))…
然而直接组合数莫名其妙的跪,于是阶乘展开变成:
(n−2)!∏i(di−1)!
看到阶乘然后就可以上下分解质因数随便减一减就好。另外特判坑死人。
#include <bits/stdc++.h> using namespace std; int prime[155], tot = 0; int n, s, cnt = 0; int c[155]; void get_prime() { for (int i = 2; i <= n; i++) { int flg = 0; for (int j = 2; j < i; j++) if (i%j == 0) { flg = 1; break; } if (!flg) { prime[++tot] = i; for (int j = i; (n-2)/j > 0; j *= i) c[tot] += (n-2)/j; } } } void div(int nd) { for (int i = 1; i <= tot; i++) { for (int j = prime[i]; j <= nd; j *= prime[i]) c[i] -= nd/j; } } long long power(int a, int n) { if (n == 0) return 1; long long p = power(a, n>>1); p *= p, p *= (n&1)?a:1; return p; } int main() { scanf("%d", &n); get_prime(); long long ans = 1; if (n == 1) { int d; scanf("%d", &d); cout << (d == 0) << endl; return 0; } for (int i = 1; i <= n; i++) { int d; scanf("%d", &d); if (d == 0) { cout << 0 << endl; return 0; } div(d-1); cnt += d-1; } if (cnt != n-2) cout << 0 << endl; else { for (int i = 1; i <= tot; i++) { ans *= power(prime[i], c[i]); } cout << ans << endl; } return 0; }
bzoj1030:文本生成器
AC自动机上dp处理不包含任何一个串的方案总数,然后减去就好。顺便熟练一波AC自动机。
#include <bits/stdc++.h> using namespace std; struct ACM { struct node { bool finish; int chl[26], fail; node():finish(0),fail(0) { memset(chl, 0, sizeof chl); } } tree[20005]; int root, top; queue<int> que; ACM():root(0),top(0){} void push(int &nd, const char *str) { if (nd == 0) nd = ++top; if (*str == '\0') tree[nd].finish = 1; else push(tree[nd].chl[*str-'A'], str+1); } void push(const char *str) {push(root, str); } void init() { que.push(root); tree[root].fail = 0; while (!que.empty()) { int tp = que.front(); que.pop(); tree[tp].finish |= tree[tree[tp].fail].finish; for (int i = 0; i < 26; i++) { if (!tree[tp].chl[i]) continue; int nd = tree[tp].fail; while (nd && !tree[nd].chl[i]) nd = tree[nd].fail; tree[tree[tp].chl[i]].fail = nd?tree[nd].chl[i]:root; tree[tree[tp].chl[i]].finish |= tree[tp].finish; que.push(tree[tp].chl[i]); } } } void dfs(int nd, int tab = 0) { if (!nd) return; for (int i = 1; i <= tab; i++) putchar(' '); printf("%d(%d, %d) : ", nd, tree[nd].fail, tree[nd].finish); for (int i = 0; i < 26; i++) if (tree[nd].chl[i]) printf("--%c--> %d; ", i+'A', tree[nd].chl[i]); puts(""); for (int i = 0; i < 26; i++) if (tree[nd].chl[i]) dfs(tree[nd].chl[i], tab+2); } } ACM; int dp[105][20005]; int dfs(int m, int nd) { if (ACM.tree[nd].finish) return 0; if (m == 0) return 1; if (dp[m][nd] != -1) return dp[m][nd]; dp[m][nd] = 0; for (int i = 0; i < 26; i++) { int p = nd; while (p && !ACM.tree[p].chl[i]) p = ACM.tree[p].fail; if (p) (dp[m][nd] += dfs(m-1, ACM.tree[p].chl[i])) %= 10007; else (dp[m][nd] += dfs(m-1, ACM.root)) %= 10007; } return dp[m][nd]; } int power(int a, int n) { if (n == 0) return 1; int p = power(a, n>>1); (p *= p) %= 10007; if (n&1) (p *= a) %= 10007; return p; } char str[105]; int n, m; int main() { memset(dp, -1, sizeof dp); scanf("%d%d", &n, &m); for (int i = 1; i <= n; i++) { scanf("%s", str); ACM.push(str); } ACM.init(); int ans = power(26, m)-dfs(m, ACM.root); ((ans %= 10007) += 10007) %= 10007; cout << ans << endl; return 0; }
bzoj1016:[JSOI2008]最小生成树计数
原来讲过的题然而一直没有做。按边长从小到大排,每次做一个Matrix_Tree生成森林计数,然后把能缩的缩在一起。除了特判1A。
#include <bits/stdc++.h> using namespace std; const int MAXN = 105, MAXM = 1005; struct node { int i, j, d; friend bool operator < (const node &a, const node &b) { return a.d < b.d; } } edge[MAXM]; int n, m; int gp[MAXN], vis[MAXN], gt; // 缩在一起的点的新编号 double g[MAXN][MAXN]; // 求解行列式 struct ufs { int fa[MAXN]; void clear() { memset(fa, 0, sizeof fa); } ufs(){clear(); } inline int findf(int nd) { return fa[nd]?fa[nd] = findf(fa[nd]):nd; } void link(int i, int j) { int p = findf(i), q = findf(j); if (p != q) fa[p] = q; } } UFS, u2; int calc() { if (gt == 0) return 1; gt--; double ans = 1; for (int i = 1; i <= gt; i++) { if (g[i][i] == 0) { int k = i+1; while (!g[k][i]) k++; swap(g[i], g[k]); } ans *= g[i][i]; for (int j = i+1; j <= gt; j++) { double t = -g[j][i]/g[i][i]; for (int k = i; k <= gt; k++) g[j][k] += g[i][k]*t; } } // cout << ans << endl; return int(ans+0.05)%31011; } int main() { scanf("%d%d", &n, &m); for (int i = 1; i <= m; i++) scanf("%d%d%d", &edge[i].i, &edge[i].j, &edge[i].d); sort(edge+1, edge+m+1); long long ans = 1; for (int i = 1; i <= m; ) { // cout << "--" << i <<" " << edge[i].d<< endl; int d = edge[i].d; memset(vis, 0, sizeof vis); memset(g, 0, sizeof g); u2.clear(); gt = 0; for (int j = i; j <= m && edge[j].d == d; j++) { int p = UFS.findf(edge[j].i), q = UFS.findf(edge[j].j); if (p==q) continue; if (!vis[p]) gp[p] = ++gt, vis[p] = 1; if (!vis[q]) gp[q] = ++gt, vis[q] = 1; g[gp[p]][gp[q]]--, g[gp[q]][gp[p]]--, g[gp[p]][gp[p]]++, g[gp[q]][gp[q]]++, u2.link(gp[p], gp[q]); } // 建图 if (gt == 1) break; for (int k = 1; k <= gt; k++) for (int j = k+1; j <= gt; j++) { // cout << k << " " << j << endl; if (u2.findf(k) != u2.findf(j)) u2.link(k, j), g[k][j] = g[j][k] = -1, g[k][k]++, g[j][j]++; } // 建桥 (ans *= calc()) %= 31011; // matrix_tree theory while (edge[i].d == d) UFS.link(edge[i].i, edge[i].j), i++; // 合并 } for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) if (UFS.findf(i) != UFS.findf(j)) { cout << 0 << endl; return 0; } cout << ans << endl; return 0; }
bzoj1004: [HNOI2008]Cards
居然自己看并1A了这置换群…首先奇奇怪怪的表述让我们知道出题人要告诉我们是个置换群:
输入数据保证任意多次洗牌都可用这m种洗牌法中的一种代替——运算封闭;
置换显然满足结合律、交换律;
且对每种洗牌法,都存在一种洗牌法使得能回到原状态——每个元素都可逆;
有没有幺元你加一个不就有了吗?
然后就可以做波利亚计数了。dp预处理每个置换下不变(环内染一种色)的方案数,求和算平均。为了防止重复置换,加一个哈希判重。
#include <bits/stdc++.h> using namespace std; const int MAXN = 70; int Sr, Sb, Sg, m, p, n; int power(int a, int n) { if (n == 0) return 1; int k = power(a, n>>1); (k *= k) %= p, (k *= (n&1)?a:1) %= p; return k; } int inv(int a) { return power(a, p-2); } int fa[MAXN], siz[MAXN], per[MAXN]; inline int findf(int nd) { return fa[nd]?fa[nd] = findf(fa[nd]):nd; } inline void link(int a, int b) { int p = findf(a), q = findf(b); if (p != q) fa[p] = q, siz[q] += siz[p]; } int ht[MAXN], gt = 0; int dp[MAXN][21][21][21]; // dp嘛.. int cnp = 0; // |G| 波利亚计数原理 // ------ // 哈希表 防止重复而gg int hsh[3214567]; int hash_p(int p[]) { unsigned int val = 0, bs = 1; for (int i = 1; i <= n; i++) { val += p[i]*bs; bs *= (n+1); } return val%3214567; } // ------ int val(int pp[MAXN]) // 计算某一置换下不变的方案数, dp { int k = hash_p(pp); if (hsh[k] == 1) return 0; else hsh[k] = 1, cnp++; memset(fa, 0, sizeof fa); for (int i = 1; i <= n; i++) siz[i] = 1; for (int i = 1; i <= n; i++) link(i, pp[i]); gt = 0; for (int i = 1; i <= n; i++) if (fa[i] == 0) ht[++gt] = siz[i]; memset(dp, 0, sizeof dp); dp[0][0][0][0] = 1; for (int i = 1; i <= gt; i++) for (int j = 0; j <= Sr; j++) for (int k = 0; k <= Sg; k++) for (int l = 0; l <= Sb; l++) { if (j>=ht[i]) (dp[i][j][k][l] += dp[i-1][j-ht[i]][k][l]) %= p; if (k>=ht[i]) (dp[i][j][k][l] += dp[i-1][j][k-ht[i]][l]) %= p; if (l>=ht[i]) (dp[i][j][k][l] += dp[i-1][j][k][l-ht[i]]) %= p; } // cout << dp[gt][Sr][Sg][Sb] << endl; return dp[gt][Sr][Sg][Sb]; } int main() { scanf("%d%d%d%d%d", &Sr, &Sg, &Sb, &m, &p); n = Sr + Sg + Sb; int ans = 0; for (int i = 1; i <= n; i++) per[i] = i; // 加个幺元 (ans += val(per)) %= p; for (int i = 1; i <= m; i++) { for (int j = 1; j <= n; j++) scanf("%d", &per[j]); (ans += val(per)) %= p; } cout << ans*inv(cnp)%p << endl; return 0; }
1007: [HNOI2008]水平可见直线
水水的半平面交?都是大于号,用一个栈处理就好了,甚至不需要计算几何知识就能脑补1A…五道题打卡下班。#include <bits/stdc++.h> using namespace std; struct hp { double a, b; int id; hp(){} hp(double x, double y, int i):a(x),b(y),id(i){} friend bool operator < (const hp &a, const hp &b) { return a.a==b.a?a.b>b.b:a.a < b.a; } } sg[50005]; int n; stack<int> stk; int ptw[50005], tp = 0; bool onleft(const hp &a, const hp &b, const hp &sd) { double x = (b.b-a.b)/(a.a-b.a), y = a.a*x+a.b; return y+1e-7 >= sd.a*x+sd.b; } int main() { scanf("%d", &n); for (int i = 1; i <= n; i++) { double a, b; scanf("%lf%lf", &a, &b); sg[i] = hp(a, b, i); } sort(sg+1, sg+n+1); int nw = n; for (int i = 1; i <= n; i++) if (sg[i].a == sg[i-1].a) { sg[i].a = INT_MAX; nw--; } sort(sg+1, sg+n+1); n = nw; for (int i = 1; i <= n; i++) { // cout << sg[i].id << endl; while (stk.size() >= 2) { int tp = stk.top(); stk.pop(); int t2 = stk.top(); stk.pop(); if (onleft(sg[i], sg[t2], sg[tp])) stk.push(t2); else {stk.push(t2), stk.push(tp); break;} } stk.push(i); } while (!stk.empty()) ptw[++tp] = sg[stk.top()].id, stk.pop(); sort(ptw+1, ptw+tp+1); for (int i = 1; i <= tp; i++) printf("%d ", ptw[i]); return 0; }
bzoj1015: [JSOI2008]星球大战starwar
删除难而加入容易,只要反过来做就好了。#include <bits/stdc++.h> using namespace std; const int MAXN = 400005, MAXM = 200005; struct edge { int s, t; } graph[MAXM]; struct node { int to, next; } edge_list[2*MAXM]; int head[MAXN], top = 0; inline void push(int i, int j) { ++top, edge_list[top] = (node) {j, head[i]}, head[i] = top; } int n, m; int q[MAXN], l = 0; int fa[MAXN]; int vis[MAXN], ans[MAXN], cnt, del = 0; // cnt : 联通块个数 inline int findf(int i) { return fa[i]!=-1?fa[i]=findf(fa[i]):i; } void link(int i, int j) { int p = findf(i), q = findf(j); if (p != q) { fa[p] = q; cnt--; } } int main() { memset(fa, -1, sizeof fa); memset(head, -1, sizeof head); scanf("%d%d", &n, &m); for (int i = 1; i <= m; i++) { scanf("%d%d", &graph[i].s, &graph[i].t); push(graph[i].s, graph[i].t), push(graph[i].t, graph[i].s); } scanf("%d", &l); for (int i = 1; i <= l; i++) scanf("%d", &q[i]), vis[q[i]] = i; // for (int i = 1; i <= l; i++) cout << vis[q[i]] << " "; puts(""); cnt = n; for (int i = 1; i <= l; i++) del += vis[q[i]] == i; for (int i = 1; i <= m; i++) if (!vis[graph[i].s] && !vis[graph[i].t]) link(graph[i].s, graph[i].t); for (int i = l; i >= 1; i--) { ans[i] = cnt-del; // cout << del << endl; if (vis[q[i]] == i) { del--; vis[q[i]] = 0; for (int j = head[q[i]]; j != -1; j = edge_list[j].next) { int to = edge_list[j].to; if (vis[to] == 0) link(q[i], to); } } } cout << cnt << endl; for (int i = 1; i <= l; i++) printf("%d\n", ans[i]); return 0; }
bzoj2242: [SDOI2011]计算器
数论模板题,复习bsgs。注意bsgs的第二步求逆要在循环外求,否则会被肉眼可见卡常数。
#include <bits/stdc++.h> using namespace std; long long y, z, p; int t, k; long long power(long long a, long long n) { if (a == 0) return 0; if (n == 0) return 1; long long k = power(a, n>>1); (k *= k) %= p; if (n&1) (k *= a) %= p; return k; } long long inv(long long a) { return power(a, p-2); } map<long long, long long> hsh; void work() { if (k == 1) { printf("%lld\n", power(y, z)); } else if (k == 2) { long long x = inv(y)*z%p; if (x > 0 && x*y%p == z%p) printf("%lld\n", x); else puts("Orz, I cannot find x!"); } else { y %= p, z %= p; if (y == 0 && z == 0) {puts("1");return;} if (y == 0) {puts("Orz, I cannot find x!"); return;} hsh.clear(); long long tp = ceil(sqrt(p)+1e-7); long long delta = 1; for (int i = 1; i <= tp; i++) { (delta *= y) %= p; if (!hsh.count(delta))hsh[delta] = i; } long long bs = 1; bool fd = 0; delta = inv(delta); for (int i = 0; i*tp <= p; i++) { if (hsh.count(bs*z%p)) { // cout << i*tp << " " << bs << " " << inv(bs) << " " << hsh[inv(bs)] << endl; printf("%lld\n", i*tp+hsh[bs*z%p]); fd = 1; break; } (bs *= delta) %= p; } if (!fd) puts("Orz, I cannot find x!"); } } int main() { scanf("%d%d", &t, &k); for (int i = 1; i <= t; i++) { scanf("%lld%lld%lld", &y, &z, &p); work(); } return 0; }
bzoj1014: [JSOI2008]火星人prefix
一遇数据结构就跪综合症…因为位置和树上位置的转化调了一个多小时。首先“插入不超过10000个”可以想到是O(lg2n)的插入,就是二分+哈希了。然而哈希是动态的,于是要splay维护。
#include <bits/stdc++.h> using namespace std; const int MAXM = 2000005; int chl[MAXM][2]; unsigned long long dat[MAXM], cha[MAXM], power[MAXM]; unsigned int siz[MAXM], fa[MAXM]; int top = 0, root = 0; inline int new_node(char c, int f) { return ++top, dat[top] = cha[top] = c-'a'+1, siz[top] = 1, fa[top] = f, top; } void update(int nd) { siz[nd] = siz[chl[nd][0]] + siz[chl[nd][1]] + 1; dat[nd] = dat[chl[nd][0]] + power[siz[chl[nd][0]]]*(cha[nd] + (unsigned long long)27*dat[chl[nd][1]]); } void zig(int nd) { // cout << "zigging : " << nd << endl; int p = fa[nd], g = fa[fa[nd]], tp = chl[p][0] != nd; int tg = chl[g][0] != p, son = chl[nd][tp^1]; if (son) fa[son] = p; fa[p] = nd, fa[nd] = g; if (g) chl[g][tg] = nd; else root = nd; chl[nd][tp^1] = p, chl[p][tp] = son; update(p), update(nd); } void dfs(int nd, int tab = 0) { if (!nd) return; for (int i = 0; i < tab; i++) cout << " "; printf("%d--%c--(%d,%d), siz = %d, fa = %d\n", nd, cha[nd], chl[nd][0], chl[nd][1], siz[nd], fa[nd]); dfs(chl[nd][0], tab+2); dfs(chl[nd][1], tab+2); } void splay(int nd, int tar = 0) { // cout << "splaying : " << nd << " --> son of " << tar << endl; while (fa[nd] != tar) { // cout << nd << endl; int p = fa[nd], g = fa[nd], tp = chl[p][0] != nd, tg = chl[g][0] != p; if (fa[p] == tar) {zig(nd); break;} if (tp == tg) zig(p), zig(nd); else zig(nd), zig(nd); } } // splay tree int position(int k) { if (k > siz[root] || k <= 0) return 0; k--; int nd = root; while (k != siz[chl[nd][0]]) { // cout << nd << " " << k << endl; nd = (siz[chl[nd][0]] > k) ? (chl[nd][0]) : (k -= siz[chl[nd][0]]+1, chl[nd][1]); } return nd; } // get the position of k-th element int segment(int l, int r) { int k = position(l-1), p = position(r+1); if (l == 1 && r == siz[root]) return root; if (l == 1) return splay(p), chl[root][0]; if (r == siz[root]) return splay(k), chl[root][1]; return splay(k), splay(p, root), chl[chl[root][1]][0]; } // get a segment [l, r] unsigned long long query(int l, int r) { return dat[segment(l, r)]; } // get the hash value in [l, r] void insert(int k, char c) // push a new element after k-th element { // cout << k << " " << c << " " << siz[root] << endl; if (!root && k == 0) { root = new_node(c, 0); return; } // no element if (k == 0) { splay(position(1)); chl[root][0] = new_node(c, root); update(root); } else { int nd = segment(k, k); // puts("-----"); dfs(root); chl[nd][1] = new_node(c, nd); while (nd) update(nd), nd = fa[nd]; } // dfs(root); } void change(int k, char c) { splay(k), cha[k] = c-'a'+1, update(k); } // change the value of an existed element void init() { power[0] = 1; for (int i = 1; i < MAXM; i++) power[i] = power[i-1]*27; // power for hash } // ------------- int ask(int opl, int opr) { int len = siz[root]; // maxlen int l = 0, r = len-max(opl, opr)+1; while (l <= r) { int mid = (l+r)>>1; if (query(opl, opl+mid-1) == query(opr, opr+mid-1)) l = mid+1; else r = mid-1; } return l-1; } int n; char str[MAXM]; char get_char() { char c; do c = getchar(); while(!isalpha(c)); return c; } inline int read() { int a = 0, c; do c = getchar(); while(!isdigit(c)); while (isdigit(c)) { a = a*10+c-'0'; c = getchar(); } return a; } int main() { init(); scanf("%s", str); for (int i = 0, l = strlen(str); i < l; i++) insert(i, str[i]); // build a tree n = read(); int cnt = 0; for (int i = 1; i <= n; i++) { char c = get_char(); int l, r, p; char d; switch (c) { case 'Q': cnt++; l = read(), r = read(); printf("%d\n", ask(l, r)); break; case 'R': p = read(), d = get_char(); if (!isalpha(d)) throw; change(position(p), d); break; case 'I': p = read(), d = get_char(); if (!isalpha(d)) throw; insert(p, d); break; default: throw; break; } } return 0; }
相关文章推荐
- BZOJ满框记录,排名999记录![BZOJ5040 的HINT]
- bzoj1046上升序列 DP+记录路径
- BZOJ 刷题记录 PART 2
- [置顶] BZOJ刷题记录
- BZOJ 1002 1003 1007 被屠记录
- bzoj 4942 整数 思考记录
- bzoj刷题记录4.25-5.1
- bzoj4200 [Noi2015]小园丁与老司机(dp+记录路径+有源汇有上下界最小流)
- MongoDB管理与开发精要《红丸出品》4.4 插入记录
- 【OI做题记录】【BZOJ】【Scoi2010】游戏
- bzoj刷题记录5.6-5.10
- 【OI做题记录】【BZOJ】【斜率优化DP】【HNOI2008】玩具装箱
- Lenovo k860i 移植Android 4.4 cm11进度记录【上篇已完结】
- bzoj刷题记录4.17-4.21
- bzoj1116 [POI2008]CLO(记录带环连通块)
- 【OI做题记录】【BZOJ】[HAOI2007]理想的正方形
- 【OI做题记录】【BZOJ】【HAOI2006】旅行comf
- BZOJ 刷题记录 PART 3
- BZOJ 刷题记录 PART 6
- BZOJ刷题记录(施工到AFO)