BJOI2015 Day3
(wzj这蒟蒻终于滚Cu了,今天第一题SB题写+调用了1.5h,测试时还WA了一个点。第二题数位DP20分钟写完遇到鬼打墙,中间一切正常最后输出一坨负数。调了1h发现是一个数组开小了。又花了20+min写暴力对拍,大概拍了拍感觉没问题交上去就爆零了2333.最后一道题花10min写个暴力,然后开始写正解,然后,然后,然后写完了却没有调完,最后只能交暴力结果暴力3WA6T,只有10分2333.)
---------------------------------------------------------------------------
T1: http://oj.cnuschool.org.cn/oj/home/problem.htm?problemID=497
这是一个博弈游戏,我们可以考虑将当前的n,m记到状态,但可行的(n,m)可能很大,怎么办呢?
注意当m>1时,n最多到sqrt(c)也就是35000+,而当m=1时我们手动分析即可,时间复杂度即为O(sqrt(c)*logc)。
怎么处理Draw的情况呢?注意只有初始n=1且(n+1)^m>=c时两人都不会加n,于是就平局了。(开始这里没有想清楚WA了一个点)
实现时可以看我的code也可以看std的code。我的code用了一个辅助数组t[i]表示最大的指数使i^(t[i])<c来加速判断,相比之下std的可能更好理解。
我的code:
#include<cstdio> #include<cctype> #include<cstring> #include<algorithm> #define rep(s,t) for(int i=s;i<=t;i++) using namespace std; inline int read() { int x=0,f=1;char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } typedef long long LL; int c,f[40010][41],vis[40010][41]; LL qpow(int n,int m) { LL ans=1,t=n; while(m) { if(m&1) ans*=t; t*=t;m>>=1; } return ans; } int t[41]; void init() { memset(t,0,sizeof(t));memset(vis,0,sizeof(vis)); t[1]=c;rep(2,30) while(qpow(t[i]+1,i)<=c) t[i]++; } int dp(int n,int m) { if(n>t[m]) return 0; if(n>t[2]) return (c-n)&1; if(n>t[m+1]) return (t[m]-n)&1; int& ans=f[n][m];if(vis[n][m]) return ans; vis[n][m]=1; if(n+1<=t[m]&&!dp(n+1,m)) return ans=1; if(n<=t[m]+1&&!dp(n,m+1)) return ans=1; return ans=0; } int main() { int T=read(); while(T--) { int n=read(),m=read();c=read()-1;init(); if(n==1&&qpow(2,m)>c) puts("Draw"); else printf("%s\n",dp(n,m)?"Alice":"Bob"); } return 0; }
std的code:
#include <cmath> #include <cstdio> #include <cstring> using namespace std; const double eps = 1e-8; const int MAXA = 32001, MAXB = 31; int set[MAXA][MAXB]; int a, b, n, ret; int solve(int a, int b){ if (a < MAXA && b < MAXB && set[a][b] != 0) return set[a][b] - 10; bool A = false, B = false; if (log(n)/log(a + 1) - eps > b) A = true; if (pow((double)a, b + 1) < n - eps) B = true; int ret = 0; if (!A && !B){ ret = -1; } else if (a == 1 && !A){ ret = 0; } else if (b == 1 && !B){ ret = ((n - a) & 1) ? -1 : 1; } else { ret = 1; if (B){ int tmp = solve(a, b + 1); if (tmp < ret) ret = tmp; } if (A){ int tmp = solve(a + 1, b); if (tmp < ret) ret = tmp; } if (ret != 0) ret = -ret; } if (a < MAXA && b < MAXB) set[a][b] = ret + 10; return ret; } void print() { if (ret == 1) printf("Alice\n"); else if (ret == -1) printf("Bob\n"); else printf("Draw\n"); } int main() { int T; scanf("%d", &T); while (T --){ scanf("%d%d%d", &a, &b, &n); memset(set, 0, sizeof(set)); ret = solve(a, b); print(); } return 0; }
T2:http://oj.cnuschool.org.cn/oj/home/problem.htm?problemID=498
题意很裸,数位DP。然而我这个蒟蒻在此之前只写过一次数位DP,写起来真是23333.
60分的做法:设f[len][front][sum1][sum2]表示长度为len,开头为front,奇数位之和为sum1,偶数位之和为sum2的方案,转移什么的自己YY或看我的code吧。
加快速度还可以用g[len][front][k]表示长度为len,开头为front,奇数位与偶数位的gcd不超过k的方案。(不过这显然没有什么卵用,我还在这里开小了数组调了1h)
询问考虑差分,将f(l--r)转成f(r)-f(l-1)。考虑之前确定的位对下面没确定的位造成的影响,这个可以看看我的code具体体会。
最后我cal(x)实际求的是[1,x)的幸运数,然后,然后,然后,然后,然后,然后就爆零了。
#include<cstdio> #include<cctype> #include<cstring> #include<algorithm> #define rep(i,s,t) for(int i=s;i<=t;i++) using namespace std; inline int read() { int x=0,f=1;char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } typedef long long LL; LL f[20][10][200][200],fx[20][200][200]; LL g[20][10][210],xp[20]; int preg[200][200]; int gcd(int a,int b) {return !b?a:gcd(b,a%b);} void init() { xp[0]=1;rep(i,1,18) xp[i]=xp[i-1]*10; rep(i,0,9) f[1][i][i][0]=1; rep(i,1,17) rep(j,0,9) rep(sum1,0,i*10) rep(sum2,0,i*10-sum1) { if(!f[i][j][sum1][sum2]) continue; if(!(i&1)) rep(k,0,9) f[i+1][k][sum1+k][sum2]+=f[i][j][sum1][sum2]; else rep(k,0,9) f[i+1][k][sum1][sum2+k]+=f[i][j][sum1][sum2]; } rep(sum1,1,180) rep(sum2,1,180) preg[sum1][sum2]=gcd(sum1,sum2); rep(i,1,18) rep(j,0,9) rep(sum1,1,i*10) rep(sum2,1,i*10-sum1) { int t=preg[sum1][sum2]; g[i][j][t]+=f[i][j][sum1][sum2]; } rep(i,1,18) rep(j,0,9) rep(k,2,100) g[i][j][k]+=g[i][j][k-1]; } int bit[21],k; LL cal(LL x) { memset(bit,0,sizeof(bit)); LL t=x,ans=0;int len=0,s1=0,s2=0; while(t) bit[++len]=t%10,t/=10; rep(i,1,len-1) rep(j,1,9) ans+=g[i][j][k]; for(int i=len;i;i--) { rep(j,0,bit[i]-1) { if(!j&&i==len) continue; rep(sum1,0,10*i) rep(sum2,0,10*i-sum1) if(sum1+s1&&sum2+s2&&preg[sum1+s1][sum2+s2]<=k) ans+=f[i][j][sum1][sum2]; } if(i&1) s1+=bit[i];else s2+=bit[i]; } return ans; } int main() { init();int T=read(); while(T--) { k=read();LL l,r; scanf("%lld%lld",&l,&r); printf("%lld\n",cal(r+1)-cal(l));//cal(r)-cal(l-1) -> WA0 + 2333333 } return 0; }
100分的做法:设f[len][k][sum1][sum2]表示还有i位没有填写,已填写的与len同奇偶的数位和为sum1,不同奇偶的数位和为sum2,gcd(sum1,sum2)<=k的方案。
f[len][k][sum1][sum2]=sigma{f[len-1][k][sum2-d][sum1]|d=[0,9]},这样询问时就很快了。
注意这里的cal求的是[1,x]
#include<cstdio> #include<cctype> #include<cstring> #include<algorithm> #define rep(i,s,t) for(int i=s;i<=t;i++) using namespace std; inline int read() { int x=0,f=1;char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } typedef long long LL; const int maxk=85; LL f[20][maxk][maxk][maxk]; int gcd(int a,int b) {return !b?a:gcd(b,a%b);} void init() { rep(i,1,maxk-1) rep(j,1,maxk-1) rep(k,gcd(i,j),maxk-1) f[0][k][i][j]=1; rep(i,0,18) rep(k,1,maxk-1) { int mx=(19-i)/2*9; rep(a,0,mx) rep(b,0,mx) rep(d,0,min(b,9)) f[i+1][k][b-d][a]+=f[i][k][a][b]; } } int bit[21],k; LL cal(LL x) {//[1,x] if(x<=10) return 0; LL t=x,ans=0;int len=0,s1=0,s2=0; while(t) bit[++len]=t%10,t/=10; for(int i=len;i;i--) { rep(j,0,bit[i]-1) if(i&1) ans+=f[i-1][k][s1][s2+j]; else ans+=f[i-1][k][s2][s1+j]; if(i&1) s2+=bit[i];else s1+=bit[i]; } return ans+f[0][k][s1][s2]; } int main() { init();int T=read(); while(T--) { k=min(read(),81);LL l,r; scanf("%lld%lld",&l,&r); printf("%lld\n",cal(r)-cal(l-1));//notice! } return 0; }
T3:http://oj.cnuschool.org.cn/oj/home/problem.htm?problemID=499
这是一个比较常规的数据结构题目,首先我们要会求一条链上有多少还没有被删去的点,这个我们可以用DFS序+线段树完成。其次我们发现这道题还带有可持久化操作,那就用可持久化线段树来维护每个版本的DFS序,那么求一条链上有多少还没有被删去的点就成为O(logn)的了。然后我们对于每个询问需要判断被询问点在lca--x上还是在lca--y上,然后二分判断,时间复杂度O(log^2)。
说起来简单写起来还是相当复杂的(我还没写完),先放上std与神犇lxt的AC代码。
std:
#include <vector> #include <cstdio> #include <algorithm> using namespace std; const int MAXD = 17; const int MAXN = 100005; vector <int> adj[MAXN]; bool type[MAXN]; int anc[MAXN][MAXD]; int n, m, root; void init() { scanf("%d", &n); for (int i = 1; i <= n; i ++){ scanf("%d", &anc[i][0]); if (!anc[i][0]) root = i; else adj[anc[i][0]].push_back(i); } } int lt[MAXN], rt[MAXN], dep[MAXN]; bool col[MAXN];//state int stack[MAXN]; void dfs() { int tail = 1, cnt = 0; stack[tail] = root; while (tail){ int u = stack[tail]; if (!col[u]){ lt[u] = ++ cnt; dep[u] = dep[anc[u][0]] + 1; int ts = adj[u].size(); for (int i = 0; i < ts; i ++) stack[++ tail] = adj[u][i]; for (int d = 1; d < MAXD; d ++) anc[u][d] = anc[anc[u][d-1]][d-1]; col[u] = 1; } else { rt[u] = ++ cnt; tail --; } } } struct Tseg{ Tseg *lc, *rc; int l, r, sum; } pool[MAXN << 5]; Tseg *nxt(){ static int cur = 0; return &pool[cur ++]; } Tseg *pos[MAXN], *cur, *lat; #define l(t) (t->lc) #define r(t) (t->rc) void mktree(Tseg *t, int ll, int rr){ t->l = ll, t->r = rr, t->sum = 0; if (ll == rr) return; int mid = (ll + rr) >> 1; l(t) = nxt(); mktree(l(t), ll, mid); r(t) = nxt(); mktree(r(t), mid+1, rr); } #define t_ch tree_change Tseg *t_ch(Tseg *t, int p, int c){ Tseg *ret = nxt(); *ret = *t; if (t->l == t->r){ ret->sum = c; return ret; } if (p <= l(t)->r) l(ret) = t_ch(l(t), p, c); else r(ret) = t_ch(r(t), p, c); ret->sum = l(ret)->sum + r(ret)->sum; return ret; } int fa(int x, int deep){ if (dep[x] == deep) return x; for (int d = MAXD-1; d >= 0; d --) if (dep[anc[x][d]] >= deep) x = anc[x][d]; return x; } int find_lca(int x, int y){ if (dep[x] > dep[y]){ int t = x; x = y; y = t; } if (dep[x] < dep[y]) y = fa(y, dep[x]); for (int d = MAXD-1; d >= 0; d --) if (anc[x][d] != anc[y][d]) x = anc[x][d], y = anc[y][d]; if (x == y) return x; return anc[x][0]; } #define t_s tree_sum int t_s(Tseg *t, int l, int r){ if (t->l == l && t->r == r) return t->sum; if (r <= l(t)->r) return t_s(l(t), l, r); if (l >= r(t)->l) return t_s(r(t), l, r); return t_s(l(t), l, l(t)->r) + t_s(r(t), r(t)->l, r); } int check(int x, int y){ return dep[y] - dep[x] + 1 - (t_s(cur, lt[x], lt[y]) - t_s(lat, lt[x], lt[y])); } int kth(int x, int y, int k){ if (check(x, y) < k) return -1; int l = dep[x], r = dep[y]; while (l < r){ int mid = (l + r) >> 1; int v = fa(y, mid); int s = check(x, v); if (s >= k) r = mid; else l = mid + 1; } return fa(y, l); } void print() { dfs(); pos[0] = nxt(); mktree(pos[0], 1, n << 1); scanf("%d", &m); int type, a, b, c, k, y; for (int i = 1; i <= m; i ++){ scanf("%d", &type); if (type == 1){ scanf("%d", &c); Tseg *tmp = t_ch(pos[i-1], lt[c], 1); pos[i] = t_ch(tmp, rt[c], -1); } else { scanf("%d%d%d%d", &a, &b, &k, &y); pos[i] = pos[i-1]; cur = pos[i]; lat = pos[y]; if (a == anc[b][0] || b == anc[a][0]){//no castles puts("-1"); continue; } int lca = find_lca(a, b), ans = -1; if (lca != a && lca != b){ int u = anc[a][0], v = anc[b][0]; int sum = check(lca, u); if (sum >= k) ans = kth(lca, u, sum - k + 1); else ans = v == lca ? -1 : kth(fa(v, dep[lca] + 1), v, k - sum); } else { int u, v; if (lca == a){ u = fa(b, dep[a]+1), v = anc[b][0]; } else { u = fa(a, dep[b]+1), v = anc[a][0]; int sum = check(u, v); if (sum < k) {puts("-1"); continue;} k = sum - k + 1; } ans = kth(u, v, k); } printf("%d\n", ans); } } } int main() { freopen("travel.in", "r", stdin); freopen("travel.out", "w", stdout); init(); print(); return 0; }
lxt:
#include <iostream> #include <cstdio> #include <cmath> #include <algorithm> #include <cstring> #include <map> #define MAXN 200005 #define MAXT 10000005 using namespace std; int read(){ int ret = 0; char c = getchar(); while(c < '0' || c > '9') c = getchar(); while(c >= '0' && c <= '9') {ret *= 10; ret += c - '0'; c = getchar();} return ret; } int Root, ee, head[MAXN], n, m, fat[MAXN], A, B; struct Edge{int to, next;}edge[MAXN]; inline void addedge(int x, int y){edge[++ ee].to = y; edge[ee].next = head[x]; head[x] = ee;} int dep[MAXN], din[MAXN], dout[MAXN], dd, skip[MAXN][20]; int dfs(int x){ dep[x] = dep[fat[x]] + 1; din[x] = ++ dd; skip[x][0] = fat[x]; for(int i = 1; i <= 16; i ++) skip[x][i] = skip[skip[x][i - 1]][i - 1]; for(int i = head[x]; i != -1; i = edge[i].next) dfs(edge[i].to); dout[x] = ++ dd; } int finddep(int x, int dd){ if(dep[x] == dd) return x; for(int i = 16; i >= 0; i --) if(dep[skip[x][i]] >= dd) x = skip[x][i]; return x; } int cnt, root[MAXN], cl[MAXT], cr[MAXT], s[MAXT]; void insert(int &x, int pre, int l, int r, int p, int c){ x = ++ cnt; cl[x] = cl[pre], cr[x] = cr[pre], s[x] = s[pre] + c; if(l == r) return; int mid = l + r >> 1; if(mid >= p) insert(cl[x], cl[pre], l, mid, p, c); else insert(cr[x], cr[pre], mid + 1, r, p, c); } int ask(int t, int l, int r, int L, int R){ if(L > R) return 0; if(l >= L && r <= R) return s[t]; int mid = l + r >> 1, ret = 0; if(L <= mid) ret = ask(cl[t], l, mid, L, R); if(R >= mid + 1) ret += ask(cr[t], mid + 1, r, L, R); return ret; } inline int LCA(int x, int y){ if(dep[x] < dep[y]) swap(x, y); for(int i = 16; i >= 0; i --) if(dep[skip[x][i]] >= dep[y]) x = skip[x][i]; for(int i = 16; i >= 0; i --) if(skip[x][i] != skip[y][i]) x = skip[x][i], y = skip[y][i]; if(x == y) return x; return skip[x][0]; } int count(int x, int y){ return dep[y] - dep[x] + 1 - ask(B, 1, 2 * n, din[x], din[y]) + ask(A, 1, 2 * n, din[x], din[y]); } int calck(int x, int y, int k){ if(count(x, y) < k) return -1; int l = dep[x], r = dep[y]; while(l != r){ int mid = l + r >> 1; if(count(x, finddep(y, mid)) >= k) r = mid; else l = mid + 1; } return finddep(y, l); } int main(){ freopen("travel.in", "r", stdin); freopen("travel.out", "w", stdout); n = read(); memset(head, -1, sizeof(head)); for(int i = 1; i <= n; i ++) {fat[i] = read(); if(fat[i]) addedge(fat[i], i); else Root = i;} dfs(Root); m = read(); for(int tt = 1; tt <= m; tt ++){ int key; key = read(); if(key == 1){ int c; scanf("%d", &c); int la; insert(la, root[tt - 1], 1, 2 * n, din[c], 1); insert(root[tt], la, 1, 2 * n, dout[c], -1); }else{ root[tt] = root[tt - 1]; int a, b, k, y; a = read(); b = read(); k = read(); y = read(); A = root[y], B = root[tt]; if(a == fat[b] || b == fat[a]) {puts("-1"); continue;} int lca = LCA(a, b); if(lca != a && lca != b){ int aa = count(lca, fat[a]); if(aa >= k) {printf("%d\n", calck(lca, fat[a], aa - k + 1)); continue;} if(fat[b] == lca) {puts("-1"); continue;} printf("%d\n", calck(finddep(b, dep[lca] + 1), fat[b], k - aa)); } else{ if(lca == a) { printf("%d\n", calck(finddep(b, dep[a] + 1), fat[b], k)); continue;} int la = finddep(a, dep[b] + 1); int aa = count(la, fat[a]); if(aa < k) {puts("-1"); continue;} printf("%d\n", calck(la, fat[a], aa - k + 1)); } } } // system("pause"); return 0; }