@ljt12138
2017-07-02T11:52:22.000000Z
字数 29642
阅读 922
之前一道题的增强版...
考虑所有都相等,则可以从深度由深到浅贪心,如果还没有被覆盖就从这个节点覆盖上去。
但对于不同是有反例的:
http://blog.csdn.net/mengxiang000000/article/details/73718867
考虑先用一边dfs记录子树中覆盖当前节点最浅到哪里,计为。考虑到一个未被覆盖的节点时,覆盖掉中所有的节点,并即可。
没太看懂神犇们的高端写法...只好大力树剖+树状数组维护了。
#include <bits/stdc++.h>using namespace std;const int MAXN = 100005;struct node {int to, next;} edge[MAXN];int head[MAXN], top = 0;inline void push(int i, int j){ edge[++top] = (node) {j, head[i]}, head[i] = top; }int siz[MAXN], id[MAXN], ind[MAXN], id_in_ds = 0;int c[MAXN];int n;int ki[MAXN];int depth[MAXN], fa[MAXN][20];int low_dep[MAXN];int dfn[MAXN], dfn_top = 0;inline int lowbit(int i){ return i&(-i); }void modify(int i, int dt){for (; i <= n; i += lowbit(i))c[i] += dt;}void cover(int i, int j){ modify(i, 1), modify(j+1, -1); }int dat(int i){int ans = 0;for (; i; i -= lowbit(i))ans += c[i];return ans;}int kth_ans(int nd, int k){if (k >= depth[nd]) k = depth[nd];for (register int i = 0; i < 20; i++)if (k&(1<<i))nd = fa[nd][i];return nd;}void dfs_init(int nd){siz[nd] = 1;for (register int i = 1; i < 20; i++)fa[nd][i] = fa[fa[nd][i-1]][i-1];low_dep[nd] = kth_ans(nd, ki[nd]-1);for (int i = head[nd]; i; i = edge[i].next) {depth[edge[i].to] = depth[nd]+1;dfs_init(edge[i].to), siz[nd] += siz[edge[i].to];if (depth[low_dep[edge[i].to]] < depth[low_dep[nd]])low_dep[nd] = low_dep[edge[i].to];}dfn[++dfn_top] = nd;}inline bool cmp(int a, int b){ return depth[a] > depth[b]; }void dfs_build(int nd, int from){id[nd] = ++id_in_ds, ind[nd] = from;int hev = -1;for (int i = head[nd]; i; i = edge[i].next) {int to = edge[i].to;if (hev == -1 || siz[to] > siz[hev]) hev = to;}if (hev == -1) return;dfs_build(hev, from);for (int i = head[nd]; i; i = edge[i].next) {int to = edge[i].to;if (to != hev) dfs_build(to, to);}}void cover_point(int nd, int pt){while (depth[ind[nd]] > depth[pt]) {cover(id[ind[nd]], id[nd]);nd = fa[ind[nd]][0];}if (depth[nd] >= depth[pt]) cover(id[pt], id[nd]);}int main(){scanf("%d", &n);for (int i = 2; i <= n; i++)scanf("%d", &fa[i][0]), push(fa[i][0], i);for (int i = 1; i <= n; i++) scanf("%d", &ki[i]);dfs_init(1);dfs_build(1, 1);int cnt = 0;for (int i = 1; i <= n; i++) {int pt = dfn[i];if (dat(id[pt]) != 0) continue;int anc = low_dep[pt];cover_point(pt, anc), cnt++;}printf("%d\n", cnt);return 0;}
填坑...
不知道为什么印象特别深刻去年一次NOIP模拟赛建出二维数点模型,然后Too Naive,并不会做...一直向转化成逆序对...学习了一个偏序那套理论才知道Too Simple..
经典题,一维排序,二维归并,三维树状数组。
注意check一下相同的情况。
#include <bits/stdc++.h>using namespace std;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;}const int MAXN = 100005;struct pt {int x, y, z, id;} pts[MAXN], tmp[MAXN];int n, k;inline bool cmp_x(const pt &a, const pt &b){ return a.x < b.x; }inline bool cmp_y(const pt &a, const pt &b){ return a.y < b.y; }int ans[MAXN];int c[MAXN*2];inline int lowbit(int i){ return i&(-i); }void modify(int i, int dt){for (; i <= k; i += lowbit(i))c[i] += dt;}int sum(int i){int ans = 0;for (; i; i -= lowbit(i))ans += c[i];return ans;}void solve(int l, int r, int L, int R){if (l >= r) return;L = pts[l].x, R = pts[r].x;if (L == R) {sort(pts+l, pts+r+1, cmp_y);int lp = l, rp = l;while (lp <= r) {while (rp+1 <= r && pts[rp+1].y == pts[lp].y) rp++;for (int i = lp; i <= rp; i++)modify(pts[i].z, 1);for (int i = lp; i <= rp; i++)ans[pts[i].id] += sum(pts[i].z)-1;lp = rp = rp+1;}for (int i = l; i <= r; i++) modify(pts[i].z, -1);return;}int MID = (L+R)>>1;int lp = l, rp = r;for (int i = l; i <= r; i++) {if (pts[i].x <= MID) tmp[lp++] = pts[i];else tmp[rp--] = pts[i];}int mid = lp-1;for (int i = l; i <= r; i++)pts[i] = tmp[i];solve(l, mid, L, MID), solve(mid+1, r, MID+1, R);lp = l, rp = mid+1;int tmp_l = l;while (lp <= mid && rp <= r) {if (pts[lp].y <= pts[rp].y) {modify(pts[lp].z, 1);tmp[tmp_l++] = pts[lp++];} else {ans[pts[rp].id] += sum(pts[rp].z);tmp[tmp_l++] = pts[rp++];}}while (lp <= mid) {modify(pts[lp].z, 1);tmp[tmp_l++] = pts[lp++];}while (rp <= r) {ans[pts[rp].id] += sum(pts[rp].z);tmp[tmp_l++] = pts[rp++];}for (int i = l; i <= mid; i++) modify(pts[i].z, -1);for (int i = l; i <= r; i++) pts[i] = tmp[i];}int d[MAXN];int main(){scanf("%d%d", &n, &k);for (int i = 1; i <= n; i++)pts[i].x = read(), pts[i].y = read(), pts[i].z = read(), pts[i].id = i;sort(pts+1, pts+n+1, cmp_x);solve(1, n, 1, k);for (int i = 1; i <= n; i++)d[ans[i]]++;for (int i = 0; i < n; i++)printf("%d\n", d[i]);return 0;}
填一下这个坑....
考虑向上的路径,一个观测点观测到的条件是:
也就是。因而一个向上的路径的贡献就是其路径上为定值的点。可以在打一个标记,打一个标记。反过来看,对于一个观测点,查询的其实就是子树中所有标记个数减去标记个数。这可以用序+可持久化数组来维护,后者可以使用可持久化线段树或者可持久化平衡树实现(rope也可以),复杂度,理论能拿到95分,然而在Cogs只80...后面还是RE...
#include <bits/stdc++.h>#include <ext/rope>using namespace __gnu_cxx;using namespace std;const int MAXN = 300005;struct node {int to, next;} edge[MAXN*2];int head[MAXN], top = 0;inline void push(int i, int j){ edge[++top] = (node){j, head[i]}, head[i] = top; }int depth[MAXN], W[MAXN];int u[MAXN*2], v[MAXN*2], id[MAXN*2];int n, m;int lca[MAXN], vis[MAXN];int fa[MAXN], father[MAXN], ans[MAXN];inline int findf(int nd){ return fa[nd]?fa[nd]=findf(fa[nd]):nd; }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;}vector<int> pt[MAXN];struct tag {int dat;int tp;};vector<tag> tag_up[MAXN], tag_down[MAXN];void dfs_init(int nd, int f){vis[nd] = 1, father[nd] = f;for (vector<int>::iterator i = pt[nd].begin(); i != pt[nd].end(); ++i)if (vis[v[*i]]) {lca[id[*i]] = findf(v[*i]);}for (int i = head[nd]; i; i = edge[i].next) {int to = edge[i].to;if (to == f) continue;depth[to] = depth[nd]+1, dfs_init(to, nd);}fa[nd] = f;}void put_tag(){for (int i = 1; i <= m; i++) {int c = lca[i], s = u[i], t = v[i];if (c == t) {tag_up[s].push_back((tag){depth[s], 1});tag_up[father[c]].push_back((tag){depth[s], -1});} else if (c == s) {tag_down[t].push_back((tag){-depth[s], 1});tag_down[father[c]].push_back((tag){-depth[s], -1});} else {tag_up[s].push_back((tag){depth[s], 1});tag_up[father[c]].push_back((tag){depth[s], -1});int beg = depth[s]-depth[c];tag_down[t].push_back((tag){beg-depth[c], 1});tag_down[c].push_back((tag){beg-depth[c], -1});}}}int dfn[MAXN], dfn_top = 0, out[MAXN];struct my_array {rope<int> *arr[MAXN];int range_l, range_r, lev;void build(){arr[0] = new rope<int>;lev = 0;for (int i = range_l; i <= range_r; i++)arr[0]->push_back(0);}inline void new_lev(){ ++lev, arr[lev] = new rope<int>(*arr[lev-1]); }inline void add_pos(int pos, int dt){pos = pos-range_l;int dat = arr[lev]->at(pos)+dt;arr[lev]->replace(pos, dat);}inline int segment_sum(int l, int r, int pos){ return arr[r]->at(pos-range_l)-arr[l-1]->at(pos-range_l); }} arr_up, arr_down;void dfs_travel(int nd){dfn[nd] = ++dfn_top;arr_up.new_lev(), arr_down.new_lev();for (register vector<tag>::iterator i = tag_up[nd].begin(); i != tag_up[nd].end(); ++i)arr_up.add_pos(i->dat, i->tp);for (register vector<tag>::iterator i = tag_down[nd].begin(); i != tag_down[nd].end(); ++i)arr_down.add_pos(i->dat, i->tp);for (register int i = head[nd]; i; i = edge[i].next)if (edge[i].to != father[nd]) dfs_travel(edge[i].to);out[nd] = dfn_top;}int main(){freopen("runninga.in", "r", stdin);freopen("runninga.out", "w", stdout);int size = 128 << 20;char *p = (char*)malloc(size) + size;__asm__("movl %0, %%esp\n" :: "r"(p));n = read(), m = read();for (register int i = 1; i < n; i++) {int x = read(), y = read();push(x, y), push(y, x);}for (register int i = 1; i <= n; i++) W[i] = read();for (register int i = 1; i <= m; i++) {u[i] = read(), v[i] = read();u[m+i] = v[i], v[m+i] = u[i], id[m+i] = id[i] = i;pt[u[i]].push_back(i), pt[v[i]].push_back(i+m);}dfs_init(1, 0);put_tag();arr_up.range_l = 0, arr_up.range_r = 2*n+1, arr_down.range_l = -n-1, arr_down.range_r = n+1;arr_up.build(), arr_down.build();dfs_travel(1);for (register int i = 1; i <= n; i++) {ans[i] += arr_up.segment_sum(dfn[i], out[i], depth[i]+W[i]);ans[i] += arr_down.segment_sum(dfn[i], out[i], W[i]-depth[i]);}for (register int i = 1; i <= n; i++)printf("%d ", ans[i]);puts("");return 0;}
然而这题就是典型“会的越多得分越低”型题目...这个可持久化维护的特别鸡肋。由于答案具有可减性,我们在dfs的过程中维护一个数组,进入时记录一下答案,退出时记录一下,差值就是所求了...
#include <bits/stdc++.h>#include <ext/rope>using namespace __gnu_cxx;using namespace std;const int MAXN = 300005;struct node {int to, next;} edge[MAXN*2];int head[MAXN], top = 0;inline void push(int i, int j){ edge[++top] = (node){j, head[i]}, head[i] = top; }int depth[MAXN], W[MAXN];int u[MAXN*2], v[MAXN*2], id[MAXN*2];int n, m;int lca[MAXN], vis[MAXN];int fa[MAXN], father[MAXN], ans[MAXN];inline int findf(int nd){ return fa[nd]?fa[nd]=findf(fa[nd]):nd; }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;}vector<int> pt[MAXN];struct tag {int dat;int tp;};vector<tag> tag_up[MAXN], tag_down[MAXN];void dfs_init(int nd, int f){vis[nd] = 1, father[nd] = f;for (vector<int>::iterator i = pt[nd].begin(); i != pt[nd].end(); ++i)if (vis[v[*i]]) {//cerr << *i << " " << id[*i] << " " << v[*i] << endl;lca[id[*i]] = findf(v[*i]);}for (int i = head[nd]; i; i = edge[i].next) {int to = edge[i].to;if (to == f) continue;depth[to] = depth[nd]+1, dfs_init(to, nd);}fa[nd] = f;}void put_tag(){for (int i = 1; i <= m; i++) {int c = lca[i], s = u[i], t = v[i];// cerr << "LCA(" << s << "," << t << ") = " << c << endl;if (c == t) {tag_up[s].push_back((tag){depth[s], 1});tag_up[father[c]].push_back((tag){depth[s], -1});} else if (c == s) {tag_down[t].push_back((tag){-depth[s], 1});tag_down[father[c]].push_back((tag){-depth[s], -1});} else {tag_up[s].push_back((tag){depth[s], 1});tag_up[father[c]].push_back((tag){depth[s], -1});int beg = depth[s]-depth[c];tag_down[t].push_back((tag){beg-depth[c], 1});tag_down[c].push_back((tag){beg-depth[c], -1});}}}int arr_up[MAXN*4], arr_down[MAXN*4], beg = 600000;void dfs_travel(int nd){int pre = arr_up[depth[nd]+W[nd]+beg] + arr_down[W[nd]-depth[nd]+beg];for (register int i = head[nd]; i; i = edge[i].next)if (edge[i].to != father[nd]) dfs_travel(edge[i].to);for (register vector<tag>::iterator i = tag_up[nd].begin(); i != tag_up[nd].end(); ++i)arr_up[i->dat+beg] += i->tp;for (register vector<tag>::iterator i = tag_down[nd].begin(); i != tag_down[nd].end(); ++i)arr_down[i->dat+beg] += i->tp;ans[nd] = arr_up[depth[nd]+W[nd]+beg] + arr_down[W[nd]-depth[nd]+beg]-pre;}int main(){n = read(), m = read();for (register int i = 1; i < n; i++) {int x = read(), y = read();push(x, y), push(y, x);}for (register int i = 1; i <= n; i++) W[i] = read();for (register int i = 1; i <= m; i++) {u[i] = read(), v[i] = read();u[m+i] = v[i], v[m+i] = u[i], id[m+i] = id[i] = i;pt[u[i]].push_back(i), pt[v[i]].push_back(i+m);}dfs_init(1, 0);put_tag();dfs_travel(1);for (register int i = 1; i <= n; i++)printf("%d ", ans[i]);puts("");return 0;}
继续填古代坑计划...
当时用SPFA水了25...
现在看来很水啊..二分后随便树剖就行了。也可以树上差分搞。
#include <bits/stdc++.h>using namespace std;const int MAXN = 300005;struct node {int to, next, dis;} edge[MAXN*2];int head[MAXN], top = 0;inline void push(int i, int j, int d){ edge[++top] = (node) {j, head[i], d}, head[i] = top; }int fa[MAXN];inline int findf(int a){ return fa[a]?fa[a] = findf(fa[a]):a; }int u[MAXN*2], v[MAXN*2], id[MAXN*2], lca[MAXN], vis[MAXN], father[MAXN];int dis[MAXN];int n, m;vector<int> pt[MAXN];int depth[MAXN], len[MAXN];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;}void dfs_init(int nd, int f){vis[nd] = 1, father[nd] = f;for (vector<int>::iterator i = pt[nd].begin(); i != pt[nd].end(); ++i)if (vis[v[*i]])lca[id[*i]] = findf(v[*i]);for (int i = head[nd]; i; i = edge[i].next) {int to = edge[i].to;if (to == f) continue;depth[to] = depth[nd]+1, len[to] = len[nd]+edge[i].dis;dfs_init(to, nd);}fa[nd] = f;}int tag[MAXN], max_able = 0;void dfs_collect(int nd){for (int i = head[nd]; i; i = edge[i].next) {int to = edge[i].to;if (to == father[nd]) continue;dfs_collect(to), tag[nd] += tag[to];}}bool check(int ans){memset(tag, 0, sizeof tag);int max_val = 0, cnt = 0;max_able = 0;for (int i = 1; i <= m; i++)if (dis[i] > ans) {max_val = max(max_val, dis[i]);cnt++;// cerr << u[i] << " " << v[i] << " " << lca[i] << endl;tag[u[i]]++, tag[v[i]]++, tag[father[lca[i]]]--, tag[lca[i]]--;}if (cnt == 0) return 1;//cerr << cnt << endl;dfs_collect(1);for (int i = 1; i <= n; i++) {if (tag[i] != cnt) continue;//cerr << tag[i] << endl;for (int j = head[i]; j; j = edge[j].next) {int to = edge[j].to;// if (to == father[i]) continue;if (tag[to] == cnt)max_able = max(max_able, edge[j].dis);}}//cerr << max_val << " " << max_able << " " << ans << endl;return max_val-max_able <= ans;}int main(){//freopen("transport.in", "r", stdin);//freopen("transport.out", "w", stdout);n = read(), m = read();for (int i = 1; i < n; i++) {int l = read(), r = read(), d = read();push(l, r, d), push(r, l, d);}for (int i = 1; i <= m; i++) {u[i] = read(), v[i] = read();v[i+m] = u[i], u[i+m] = v[i], id[i] = id[i+m] = i;pt[u[i]].push_back(i), pt[v[i]].push_back(i+m);}dfs_init(1, 0);for (int i = 1; i <= m; i++)dis[i] = len[u[i]]+len[v[i]]-len[lca[i]]*2;int l = 0, r = 300000ll*1000, mid;while (l <= r) {mid = (l+r)>>1;if (check(mid)) r = mid-1;else l = mid+1;}printf("%d\n", r+1);return 0;}
我SX....原来括号序列就是dfs序啊..
二分答案,维护括号序列hash,考虑对于节点,以其为根的子树恰好被删去即是在其个祖先处,打一个标记,然后对于每个节点大力计算就好了。复杂度,如果祖先用长链剖分、排序用基数排序,可以做到
#include <bits/stdc++.h>using namespace std;const int MAXN = 100005;struct node {int to, next;} edge[MAXN];int head[MAXN], top = 0;void push(int i, int j){ edge[++top] = (node) {j, head[i]}, head[i] = top; }int hash_dat[MAXN*2], dfn_top = 0;vector<pair<int, int> > forbidden[MAXN];int fa[MAXN][20], n;int dfn_in[MAXN], dfn_out[MAXN];typedef unsigned long long ull;ull hash_tab[MAXN*2], bs = 19260817, power[MAXN*2];int height[MAXN];// --- hash ---void hash_init(){power[0] = 1;for (int i = 1; i <= n*2; i++) power[i] = power[i-1]*bs;for (int i = 1; i <= n*2; i++)hash_tab[i] = hash_tab[i-1]*bs+hash_dat[i];}ull hash_val(int l, int r){ return hash_tab[r]-hash_tab[l-1]*power[r-l+1]; }// --- hash ---int kth_anc(int nd, int k){for (int i = 0; i < 20; i++)if (k&(1<<i))nd = fa[nd][i];return nd;}void dfs_init(int nd){height[nd] = 0;++dfn_top, hash_dat[dfn_top] = 1;dfn_in[nd] = dfn_top;for (int i = 1; i < 20; i++)fa[nd][i] = fa[fa[nd][i-1]][i-1];for (int i = head[nd]; i; i = edge[i].next) {fa[edge[i].to][0] = nd, dfs_init(edge[i].to);height[nd] = max(height[nd], height[edge[i].to]+1);}++dfn_top, hash_dat[dfn_top] = 2;dfn_out[nd] = dfn_top;}set<ull> st;bool calc(int k){for (int i = 1; i <= n; i++) forbidden[i].clear();st.clear();for (int i = 1; i <= n; i++) {int nd = kth_anc(i, k+1);forbidden[nd].push_back(make_pair(dfn_in[i], dfn_out[i]));}for (int i = 1; i <= n; i++) {sort(forbidden[i].begin(), forbidden[i].end());forbidden[i].push_back(make_pair(dfn_out[i]+1, dfn_out[i]+1));}for (int i = 1; i <= n; i++) {if (height[i] < k) continue;ull hash_v = 0;int l = dfn_in[i];for (int j = 0, sz = forbidden[i].size(); j < sz; j++) {int r = forbidden[i][j].first-1;if (l <= r)hash_v = hash_v*power[r-l+1]+hash_val(l, r);l = forbidden[i][j].second+1;}if (st.count(hash_v)) return 1;st.insert(hash_v);}return 0;}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 rd[MAXN], rt;int main(){n = read();for (int i = 1; i <= n; i++) {int x, v;x = read();for (int j = 1; j <= x; j++)v = read(), push(i, v), rd[v]++;}for (int i = 1; i <= n; i++)if (rd[i] == 0) {rt = i;break;}dfs_init(rt), hash_init();int l = 0, r = n, mid;while (l <= r) {mid = (l+r)>>1;if (calc(mid)) l = mid+1;else r = mid-1;}printf("%d\n", l-1);return 0;}
这个命题思路在哪里见过....当时是kmp,现在用SAM就好了。
考虑枚举矩形的高度,则高度为的矩形可以看成以维向量为字符集的长度为的字符串。
人话就是,把一列看成一个字符。
然后就可以用广义后缀自动机大法了。由于字符集太大,需要hash+map<ull>。
ps : std貌似是后缀数组,复杂度少一个,速度快到不知哪里去了...
#include <bits/stdc++.h>using namespace std;typedef unsigned long long ull;int n, m;char A[115][115];const int MAXN = 30005;const ull bas = 19260817;struct SAM {map<ull,int> chl[MAXN];int fa[MAXN], maxl[MAXN], top, root;void clear(){ top = root = 1, maxl[1] = 0; }int push(int nd, ull dat){int p = nd, np = ++top; maxl[np] = maxl[p]+1, chl[np].clear();while (p && !chl[p][dat]) chl[p][dat] = np, p = fa[p];if (!p) fa[np] = root;else {int q = chl[p][dat];if (maxl[q] == maxl[p]+1) fa[np] = q;else {int nq = ++top; chl[nq] = chl[q], maxl[nq] = maxl[p]+1;fa[nq] = fa[q], fa[q] = fa[np] = nq;while (p && chl[p][dat] == q) chl[p][dat] = nq, p = fa[p];}}return np;}} sam_main;struct trie {map<ull, int> tree[MAXN];int top, root;trie(){ top = root = 1; }void clear(){for (int i = 1; i <= top; i++) tree[i].clear();top = root = 1;}void push(vector<ull> &str){int nd = root;for (auto i : str) {if (!tree[nd].count(i)) tree[nd][i] = ++top;nd = tree[nd][i];}}} trie_main;queue<int> que;int st[MAXN];void bfs_build_sam(){sam_main.clear();que.push(trie_main.root);st[trie_main.root] = sam_main.root;while (!que.empty()) {int nd = que.front(); que.pop();for (auto i : trie_main.tree[nd]) {st[i.second] = sam_main.push(st[nd], i.first);que.push(i.second);}}}int solve(){bfs_build_sam();int ans = 0;for (int i = 2; i <= sam_main.top; i++)ans += sam_main.maxl[i]-sam_main.maxl[sam_main.fa[i]];return ans;}ull hash_val[120][120]; // i, j 第i列前j项哈希ull power[120];inline ull hash_seg(int i, int l, int r){ return hash_val[i][r]-hash_val[i][l-1]*power[r-l+1]; }int main(){scanf("%d%d", &n, &m);power[0] = 1;for (int i = 1; i <= n; i++) power[i] = power[i-1]*bas;for (int i = 1; i <= n; i++)scanf("%s", A[i]+1);for (int i = 1; i <= m; i++) {for (int j = 1; j <= n; j++)hash_val[i][j] = hash_val[i][j-1]*bas+A[j][i]-'A'+1;}vector<ull> vec;int ans = 0;for (int i = 1; i <= n; i++) {trie_main.clear();for (int j = 1; j+i-1 <= n; j++) {vec.clear();for (int k = 1; k <= m; k++)vec.push_back(hash_seg(k, j, j+i-1));trie_main.push(vec);}ans += solve();}cout << ans << endl;return 0;}
看不懂神犇五行代码系列qwq...
考虑展开右边级数第项,发现:
然后就py直接做了...
import mathdef choose(n, k):ans = 1for i in range(k):ans = ans*(n-i)for i in range(1, k+1):ans = ans/ireturn ansn = int(raw_input())t = int(raw_input())m = int(raw_input())def mul(a, b):c = {}for i in range(2):for j in range(2):c[i, j] = 0for k in range(2):c[i, j] = (c[i, j]+a[i, k]*b[k, j])%3389return cdef power(a, n):ans = {(0,0):1, (1,1):1, (1, 0):0, (0,1):0}for i in range(10000):if (((n>>i)&1) != 0):ans = mul(ans, a)a = mul(a, a)return ansdef get_A(n):c = {}c[0, 0], c[0, 1], c[1, 0], c[1, 1] = 1234, 5678, 0, 1c = power(c, n)return (c[0, 0]+c[0, 1])%3389b = {}b[0] = get_A(n)for i in range(1, 6):tmp, flag, tt = n-i, 1, tb[i] = get_A(n-i)for j in range(1, i+1):b[i] = b[i]+b[i-j]*choose(tmp+j, j)*t**j*flagflag, tt = -flag, tt*t# print(b[i])print(b[n-m])
补题计划continue...
首先考场是肯定写85-95的...后面的性价比不高..
正解其实也(看过题解后)比较简单。考虑求的个数只要求出形如的结尾位置和开头位置,然后即为所求。考虑枚举的长度,并将作为关键点。对于一个方案可以转化成求两个关键点和。如果,说明存在一个拆分,在对应的位置区间加1。求lcp和lcs可以用后缀数组或者二分哈希(单哈希会被卡..双哈希在uoj貌似TLE...但loj和bzoj过了)。
#include <bits/stdc++.h>using namespace std;const int MAXN = 100005;typedef unsigned long long ull;char str[MAXN];int n, T;ull hash_tab[MAXN], bas = 31;long long hash_tab2[MAXN], bas2 = 97, mod = 1e9+7;ull power[MAXN];long long power2[MAXN];void hash_init(){for (register int i = 1; i <= n+n; i++)hash_tab[i] = hash_tab[i-1]*bas+str[i]-'a'+1;for (register int i = 1; i <= n+n; i++)hash_tab2[i] = (hash_tab2[i-1]*bas2%mod+str[i]-'a'+1)%mod;}inline ull hash_val1(int l, int r){ return l <= r ? hash_tab[r]-hash_tab[l-1]*power[r-l+1] : 0; }inline ull hash_val2(int l, int r){ return l <= r ? ((hash_tab2[r]-hash_tab2[l-1]*power2[r-l+1]%mod)%mod+mod)%mod : 0; }int tag_after[MAXN], tag_bef[MAXN];int pos[MAXN], top = 0;int max_len_lr(int a, int b, int upp){register int l = 0, r = upp, mid;while (l <= r) {mid = (l+r)>>1;if (hash_val1(a, a+mid-1) == hash_val1(b, b+mid-1)&& hash_val2(a, a+mid-1) == hash_val2(b, b+mid-1)) l = mid+1;else r = mid-1;}return l-1;}int max_len_rl(int a, int b, int upp){register int l = 0, r = upp, mid;while (l <= r) {mid = (l+r)>>1;if (hash_val1(a-mid+1, a) == hash_val1(b-mid+1, b)&& hash_val2(a-mid+1, a) == hash_val2(b-mid+1, b)) l = mid+1;else r = mid-1;}return l-1;}int main(){scanf("%d", &T);while (T--) {scanf("%s", str+1);memset(tag_bef, 0, sizeof tag_bef);memset(tag_after, 0, sizeof tag_after);n = strlen(str+1);power[0] = power2[0] = 1;for (register int i = 1; i <= n; i++) power[i] = power[i-1]*bas;for (register int i = 1; i <= n; i++) power2[i] = power2[i-1]*bas2%mod;for (register int i = n+1; i <= n+n; i++) str[i] = int('z')+1;hash_init();for (register int i = 1; i <= n; i++) {top = 0;do {++top, pos[top] = pos[top-1]+i;} while (pos[top] < n);register int a, b, lr, rl;for (register int j = 1; j < top; j++) {a = pos[j], b = pos[j+1];lr = max_len_lr(a, b, i), rl = max_len_rl(a, b, i);if (lr+rl-1 < i) continue;tag_after[b+i-rl]++, tag_after[b+lr]--;tag_bef[a-(i-lr)+1]--, tag_bef[a-rl+1]++;}}for (register int i = 1; i <= n+n; i++) tag_after[i] += tag_after[i-1], tag_bef[i] += tag_bef[i-1];long long ans = 0;for (register int i = 1; i <= n; i++)ans += (long long)tag_after[i]*tag_bef[i+1];printf("%lld\n", ans);}return 0;}
好神啊....感觉在考场上最多推出84...
首先打个表发现所求其实就是:
然后交换求和顺序并莫比乌斯大力算,得出:
设,考虑这样一个序列:
由辗转相除法可以得出,因此这段和这段对答案的贡献是一样的。所以:
这样就可以大力拿到84了.
剩下的比较有构造性...设。如果能快速求出这个就可以喜闻乐见下底函数分块了。
考虑最小的因子,令为最大的,则考虑。则:
由可知。而,因此:
大功告成...
#include <bits/stdc++.h>using namespace std;const int MAXK = 2005, MAXN = 1000005;const int MOD = 1000007, bas = 2333;int cnt = 0;struct hash_table {long long tab[MOD];int n[MOD], k[MOD];inline int hash_val(int n, int k){ return ((long long)n*bas%MOD+k)%MOD; }inline long long val(int __n, int __k){int val = hash_val(__n, __k);while (n[val] != __n || k[val] != __k) (val += 1) %= MOD;return tab[val];}inline void insert(int __n, int __k, long long dat){int val = hash_val(__n, __k);while (k[val] != 0 || n[val] != 0) val++;n[val] = __n, k[val] = __k, tab[val] = dat;}inline bool count(int __n, int __k){int val = hash_val(__n, __k);while (n[val] != __n || k[val] != __k) {if (n[val] == 0 && k[val] == 0) return 0;val++;}return 1;}};inline int gcd(int a, int b){ return b == 0 ? a : gcd(b, a%b); }int prime[MAXN], not_prime[MAXN], mu[MAXN], top = 0;int M[MAXN];int n, m, k;int p[MAXK], q[MAXK]; // 拿出的第一个素因子; 剩余int f[MAXK];void get_prime(int n){mu[1] = 1, not_prime[1] = 1;for (int i = 2; i <= n; i++) {if (!not_prime[i]) prime[++top] = i, mu[i] = -1;for (int j = 1; j <= top && i*prime[j] <= n; j++) {not_prime[i*prime[j]] = 1;if (i%prime[j] == 0) {mu[i*prime[j]] = 0;break;}mu[i*prime[j]] = -mu[i];}}for (int i = 1; i <= n; i++)M[i] = M[i-1]+mu[i];}hash_table hash_M;long long get_M(int N){if (N < MAXN) return M[N];if (hash_M.count(N, 0)) return hash_M.val(N, 0);long long ans = 1;int last;for (int i = 2; i <= N; i = last+1) {last = N/(N/i);ans -= (last-i+1)*get_M(N/i);}hash_M.insert(N, 0, ans);return ans;}hash_table hash_s;long long get_G(int n, int k){// cerr << n << " " << k << endl;if (n == 0) return 0;if (k == 1) return get_M(n);if (hash_s.count(n, k)) return cnt++, hash_s.val(n, k);long long tmp = get_G(n, q[k])+get_G(n/p[k], p[k]*q[k]);hash_s.insert(n, k, tmp);return tmp;}void init_pq(){for (int i = 2; i <= k; i++) {if (!not_prime[i]) p[i] = i, q[i] = 1;else {int fac, pp = 1;for (int j = 1; j <= top; j++)if (i%prime[j] == 0) {fac = prime[j];break;}while (i%(pp*fac) == 0) pp *= fac;p[i] = fac, q[i] = i/pp;}}}void init_f(){f[0] = 0;for (int i = 1; i <= k; i++)f[i] = f[i-1]+(gcd(i, k) == 1);}inline int get_F(int n){ return n/k*f[k]+f[n%k]; }int main(){cin >> n >> m >> k;get_prime(MAXN-1);init_pq();init_f();int last;long long ans = 0;for (register int i = 1; i <= n && i <= m; i = last+1) {last = min(min(n/(n/i), m/(m/i)), n);ans += (get_G(last, k)-get_G(i-1, k))*(n/i)*get_F(m/i);}cout << ans << endl;return 0;}
可能是NOI2016唯一一道水题吧......
#include <bits/stdc++.h>using namespace std;const int MAXN = 500005;struct segm {int l, r, len;friend bool operator < (const segm &a, const segm &b){ return a.len < b.len; }} seg[MAXN];int n, m;int dx[MAXN*2], dx_top = 0;void dx_init(){for (int i = 1; i <= n; i++)dx[++dx_top] = seg[i].l, dx[++dx_top] = seg[i].r;sort(dx+1, dx+dx_top+1);dx_top = unique(dx+1, dx+dx_top+1)-dx-1;for (int i = 1; i <= n; i++) {seg[i].l = lower_bound(dx+1, dx+dx_top+1, seg[i].l)-dx;seg[i].r = lower_bound(dx+1, dx+dx_top+1, seg[i].r)-dx;}}int max_val[MAXN*4], lc[MAXN*4], rc[MAXN*4], l[MAXN*4], r[MAXN*4], tag[MAXN*4];int root = 0, top = 0;inline void pdw(int nd){max_val[nd] += tag[nd];if (lc[nd]) tag[lc[nd]] += tag[nd], tag[rc[nd]] += tag[nd];tag[nd] = 0;}inline void update(int nd){ max_val[nd] = max(max_val[lc[nd]], max_val[rc[nd]]); }void build(int &nd, int opl, int opr){nd = ++top, l[nd] = opl, r[nd] = opr;if (opl < opr)build(lc[nd], opl, (opl+opr)/2), build(rc[nd], (opl+opr)/2+1, opr);}void modify(int nd, int opl, int opr, int dt){pdw(nd);if (l[nd] == opl && r[nd] == opr) tag[nd] += dt;else {int mid = (l[nd] + r[nd]) >> 1;if (opr <= mid) modify(lc[nd], opl, opr, dt);else if (opl > mid) modify(rc[nd], opl, opr, dt);else modify(lc[nd], opl, mid, dt), modify(rc[nd], mid+1, opr, dt);pdw(lc[nd]), pdw(rc[nd]), update(nd);}}int query(int nd, int opl, int opr){pdw(nd);if (l[nd] == opl && r[nd] == opr) return max_val[nd];else {int mid = (l[nd] + r[nd]) >> 1;if (opr <= mid) return query(lc[nd], opl, opr);else if (opl > mid) return query(rc[nd], opl, opr);else return max(query(lc[nd], opl, mid), query(rc[nd], mid+1, opr));}}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(){n = read(), m = read();for (int i = 1; i <= n; i++) {seg[i].l = read();seg[i].r = read();seg[i].len = seg[i].r-seg[i].l;}sort(seg+1, seg+n+1);dx_init();build(root, 1, dx_top);int ans = INT_MAX;int l = 1, r = 0;while (r < n) {while (r < n && query(root, 1, dx_top) < m)++r, modify(root, seg[r].l, seg[r].r, 1);if (query(root, 1, dx_top) >= m)ans = min(ans, seg[r].len-seg[l].len);while (l <= n && query(root, 1, dx_top) >= m) {if (query(root, 1, dx_top) >= m)ans = min(ans, seg[r].len-seg[l].len);modify(root, seg[l].l, seg[l].r, -1);l++;}if (query(root, 1, dx_top) >= m)ans = min(ans, seg[r].len-seg[l].len);}if (ans == INT_MAX)puts("-1");else printf("%d\n", ans);return 0;}
每个数大于的质因子最多只有一个,然后把相同的化为一类,大力分类讨论dp即可。
#include <bits/stdc++.h>using namespace std;const int MAXN = 505;int n;long long P;int prime[] = {2, 3, 5, 7, 11, 13, 17, 19};long long dp[2][1<<8|1][1<<8|1];long long p[2][2][1<<8|1][1<<8|1];vector<int> kind[MAXN];int main(){scanf("%d%lld", &n, &P);for (int i = 2; i <= n; i++) {int S = 0, num = i;for (int j = 0; j < 8; j++)if (num%prime[j] == 0) {S |= 1<<j;while (num%prime[j] == 0) num /= prime[j];}if (num == 1) kind[n+1].push_back(S);else kind[num].push_back(S);}int now = 0, nxt = 1;dp[now][0][0] = 1;for (register int i = 0; i <= n; i++) {int nw = 0, nx = 1;if (kind[i].empty()) continue;memcpy(p[0][nw], dp[now], sizeof dp[now]);memset(p[0][nx], 0, sizeof p[0][nx]);memcpy(p[1][nw], dp[now], sizeof dp[now]);memset(p[1][nx], 0, sizeof p[1][nx]);for (register vector<int>::iterator l = kind[i].begin(); l != kind[i].end(); ++l) {//cerr << i << " " << *l << endl;for (register int j = 0; j < 1<<8; j++)for (register int k = 0; k < 1<<8; k++) {(p[0][nx][j][k] += p[0][nw][j][k]) %= P;(p[1][nx][j][k] += p[1][nw][j][k]) %= P;(p[0][nx][j|(*l)][k] += p[0][nw][j][k]) %= P;(p[1][nx][j][k|(*l)] += p[1][nw][j][k]) %= P;}memset(p[0][nw], 0, sizeof p[0][nw]);memset(p[1][nw], 0, sizeof p[1][nw]);swap(nw, nx);}for (register int j = 0; j < 1<<8; j++)for (register int k = 0; k < 1<<8; k++)dp[now][j][k] = (p[0][nw][j][k]+p[1][nw][j][k]-dp[now][j][k])%P;}for (register int i = 1; i <= kind[n+1].size(); i++) {int S = kind[n+1][i-1];for (register int j = 0; j < 1<<8; j++)for (register int k = 0; k < 1<<8; k++) {long long val = dp[now][j][k];(dp[nxt][j][k] += val) %= P;(dp[nxt][j][k|S] += val) %= P;(dp[nxt][j|S][k] += val) %= P;}memset(dp[now], 0, sizeof dp[now]);swap(now, nxt);}long long ans = 0;for (register int j = 0; j < 1<<8; j++) {for (register int k = 0; k < 1<<8; k++) {if ((j&k) == 0)(ans += dp[now][j][k]) %= P;//cerr << dp[now][j][k] << " ";}//cerr << endl;}printf("%lld\n", (ans+P)%P);return 0;}
真...哈夫曼编码裸题
#include <bits/stdc++.h>using namespace std;const int MAXN = 100050;long long w[MAXN];int n, k;long long tot_len = 0;struct pt {long long val;int lev;friend bool operator < (const pt &a, const pt &b){ return a.val == b.val ? a.lev > b.lev : a.val > b.val; }};priority_queue<pt> heap;int main(){scanf("%d%d", &n, &k);for (int i = 1; i <= n; i++) scanf("%lld", &w[i]);while ((n-1)%(k-1) != 0) w[++n] = 0;for (int i = 1; i <= n; i++) heap.push((pt){w[i], 0});while (heap.size() > 1) {long long val = 0;int lev = 0;for (int i = 1; i <= k; i++) {pt a = heap.top(); heap.pop();val += a.val, lev = max(lev, a.lev);}tot_len += val;heap.push((pt){val, lev+1});}cout << tot_len << endl << heap.top().lev << endl;return 0;}
LCT维护广义后缀自动机Right集合大小.......................
太TM难写了啊...
「不过貌似用splay维护dfs序要好写一点...什么时候尝试一个」
#include <bits/stdc++.h>using namespace std;const int MAXN = 300005;struct link_cut_trees {int chl[MAXN][2], fa[MAXN], rev[MAXN];int tag[MAXN], dat[MAXN];int stk[MAXN], top;bool is_rt(int x){ return chl[fa[x]][0] != x && chl[fa[x]][1] != x;}inline void pdw(int nd){if (chl[nd][0]) tag[chl[nd][0]] += tag[nd], rev[chl[nd][0]] ^= rev[nd];if (chl[nd][1]) tag[chl[nd][1]] += tag[nd], rev[chl[nd][1]] ^= rev[nd];if (rev[nd]) swap(chl[nd][0], chl[nd][1]);dat[nd] += tag[nd];rev[nd] = 0, tag[nd] = 0;}void zig(int nd){int p = fa[nd], g = fa[p], tp = chl[p][0] != nd, tg = chl[g][0] != p;int son = chl[nd][tp^1];if (son) fa[son] = p; if (!is_rt(p)) chl[g][tg] = nd;fa[nd] = g, fa[p] = nd, chl[nd][tp^1] = p, chl[p][tp] = son;}void splay(int nd){stk[top = 1] = nd;for (int i = nd; !is_rt(i); i = fa[i]) stk[++top] = fa[i];while (top) pdw(stk[top--]);while (!is_rt(nd)) {int p = fa[nd], g = fa[p], tp = chl[p][0] != nd, tg = chl[g][0] != p;if (is_rt(p)) { zig(nd); break; }if (tp == tg) zig(p), zig(nd);else zig(nd), zig(nd);}}void access(int x){for (int y = 0; x; x = fa[y = x])splay(x), chl[x][1] = y;}void make_rt(int x){ access(x), splay(x), rev[x] ^= 1; }void link(int x, int y) // x --> y{fa[x] = y, access(y), splay(y), tag[y] += dat[x];}void cut(int x){access(x), splay(x), tag[chl[x][0]] -= dat[x];fa[chl[x][0]] = 0, chl[x][0] = 0;}int get_dat(int x){access(x), splay(x);return dat[x];}} ;struct SAM {int chl[MAXN][3], fa[MAXN], maxl[MAXN], top, root;link_cut_trees LCT;long long ans;SAM(){ top = root = 1, ans = 0; }int push(int nd, int x){int p = nd, np = ++top; LCT.dat[np] = 1, maxl[np] = maxl[p]+1;while (p && !chl[p][x]) chl[p][x] = np, p = fa[p];if (!p) fa[np] = root, ans += maxl[np]-maxl[fa[np]], LCT.link(np, root);else {int q = chl[p][x];if (maxl[q] == maxl[p]+1) fa[np] = q, ans += maxl[np]-maxl[fa[np]], LCT.link(np, q);else {int nq = ++top; maxl[nq] = maxl[p]+1;memcpy(chl[nq], chl[q], sizeof chl[q]);fa[nq] = fa[q], LCT.link(nq, fa[nq]);ans += maxl[nq]-maxl[fa[nq]];ans -= maxl[q]-maxl[fa[q]];fa[q] = fa[np] = nq;ans += maxl[q]-maxl[fa[q]], ans += maxl[np]-maxl[fa[np]];LCT.cut(q), LCT.cut(np), LCT.link(np, nq), LCT.link(q, nq);while (p && chl[p][x] == q) chl[p][x] = nq, p = fa[p];}}return np;}int get_pt(int nd, const char *str){if (*str == '\0') return nd;if (!chl[nd][*str-'a']) return -1;return get_pt(chl[nd][*str-'a'], str+1);}inline long long diff_substring(){ return ans; }inline int match_times(const char *str){int nd = get_pt(root, str);if (nd == -1) return 0;return LCT.get_dat(nd);}};struct trie {struct node {int to, next;char dis;} edge[MAXN*2];int head[MAXN], top;inline void push(int i, int j, char c){ edge[++top] = (node){j, head[i], c}, head[i] = top; }int stat[MAXN], vis[MAXN];int root;SAM autometa;queue<int> que;trie(){ root = 1, stat[root] = 1, top = 0; }void dfs(int x){vis[x] = 1;for (int i = head[x]; i; i = edge[i].next) {int to = edge[i].to, c = edge[i].dis-'a';if (vis[to]) continue;stat[to] = autometa.push(stat[x], c);dfs(to);}}inline int match_times(const char *str){ return autometa.match_times(str); }inline long long diff_substring(){ return autometa.diff_substring(); }} T;int n, m;int id;char str[MAXN];int main(){scanf("%d%d", &id, &n);for (int i = 1; i < n; i++) {int u, v; char c;scanf("%d%d %c\n", &u, &v, &c);T.push(u, v, c), T.push(v, u, c);}T.dfs(1);scanf("%d", &m);for (int i = 1; i <= m; i++) {int opt, r, t;scanf("%d", &opt);if (opt == 1) {printf("%lld\n", T.diff_substring());} else if (opt == 2) {scanf("%d%d", &r, &t), t--;while (t--) {int u, v;char c;scanf("%d%d %c\n", &u, &v, &c);T.push(u, v, c), T.push(v, u, c);}T.dfs(r);} else if (opt == 3) {scanf("%s", str);printf("%d\n", T.match_times(str));} else throw;}return 0;}