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;
}
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\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;
}
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\n",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\n",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\n", 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\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;    
}
View Code

 

posted @ 2015-07-13 15:40  wzj_is_a_juruo  阅读(225)  评论(0编辑  收藏  举报