@ljt12138
2017-04-03T11:29:17.000000Z
字数 12543
阅读 1042
月考完了。凭借多年应试经验连蒙带猜应付掉,然而坠稳的数学跪烂[没考过班里要学文的dalao]...还是认真刷OJ吧。
从Zars19的blog里学习了一个prufer编码。经典文章是matrix67 dalao的介绍,传送门。
总之就是把无根有标号树和长度为
然而直接组合数莫名其妙的跪,于是阶乘展开变成:
看到阶乘然后就可以上下分解质因数随便减一减就好。另外特判坑死人。
#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;}
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;}
原来讲过的题然而一直没有做。
按边长从小到大排,每次做一个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 theorywhile (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;}
居然自己看并1A了这置换群...
首先奇奇怪怪的表述让我们知道出题人要告诉我们是个置换群:
然后就可以做波利亚计数了。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| 波利亚计数原理// ------// 哈希表 防止重复而ggint 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;}
水水的半平面交?都是大于号,用一个栈处理就好了,甚至不需要计算几何知识就能脑补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;}
删除难而加入容易,只要反过来做就好了。
#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;}
数论模板题,复习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;}
一遇数据结构就跪综合症...因为位置和树上位置的转化调了一个多小时。
首先“插入不超过10000个”可以想到是
#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 treeint 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 elementint 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 elementif (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 elementvoid 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]; // maxlenint 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 treen = 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;}