• 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
    ",dp(n,m)?"Alice":"Bob");
        }
        return 0;
    }
    View Code

    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
    ");
            else if (ret == -1) printf("Bob
    ");
            else printf("Draw
    ");
    }
    
    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;
    }
    View Code

    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
    ",cal(r+1)-cal(l));//cal(r)-cal(l-1)  -> WA0 + 2333333
        }
        return 0;
    }
    View Code

    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
    ",cal(r)-cal(l-1));//notice!
        }
        return 0;
    }
    View Code

    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
    ", ans);
            }
        }
    }
    
    int main()
    {
        freopen("travel.in", "r", stdin);
        freopen("travel.out", "w", stdout);
        init();
        print();
        return 0;
    }
    View Code

    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
    ", calck(lca, fat[a], aa - k + 1)); continue;}
                    if(fat[b] == lca) {puts("-1"); continue;}
                    printf("%d
    ", calck(finddep(b, dep[lca] + 1), fat[b], k - aa));
                } else{
                    if(lca == a) { printf("%d
    ", 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
    ", calck(la, fat[a], aa - k + 1));
                }
            }
        }
      //  system("pause");
        return 0;    
    }
    View Code
  • 相关阅读:
    【水】希望之花
    如何不用狄利克雷卷积证明莫比乌斯函数性质二
    【数学】gcd
    挂分宝典
    [luogu P6042]「ACOI2020」学园祭 题解
    [luogu P6041]「ACOI2020」布丁暗杀计划 题解
    11.19模拟
    「CSP-S2020」题解
    11.11模拟
    「洛谷P1445」樱花
  • 原文地址:https://www.cnblogs.com/wzj-is-a-juruo/p/4643077.html
Copyright © 2020-2023  润新知