zoukankan      html  css  js  c++  java
  • uoj Goodbye Dingyou

    uoj的题目都挺好的。

    A.新年的XOR

    观察性质,$O(1)$的。

    #include <bits/stdc++.h>
    #define for1(a,b,i) for(int i=a;i<=b;++i)
    #define FOR2(a,b,i) for(int i=a;i>=b;--i)
    using namespace std;
    typedef long long ll;
    inline int read() {
        int f=1,sum=0;
        char x=getchar();
        for(;(x<'0'||x>'9');x=getchar()) if(x=='-') f=-1;
        for(;x>='0'&&x<='9';x=getchar()) sum=sum*10+x-'0';
        return f*sum;
    }
    
    int main () {
        int Test_=read();
        while (Test_--) {
            ll n;
            cin>>n;
            if(n==0) puts("8 67");
            else if(n==1) puts("2 3");
            else if(n==2) puts("3 9");
            else if(n==3) puts("3 7");
            else if(n==4) puts("97 100");
            else if(n%4==0) printf("%lld %lld
    ",n-4,n);
            else if(n%4==1) printf("2 %lld
    ",n-1);
            else if(n%4==2) printf("2 %lld
    ",n);
            else printf("%lld %lld
    ",n-3,n-1);
        }
    }
    View Code

    B.新年的叶子

    需要分析树的直径的性质。

    我们需要证明树的每一条直径都会相交与某一条直径的中点。

    反证法证明长度会变长即可。

    设我们找到的点为$root$。

    显然我们可以将叶子分成$root$不同儿子的块。

    若直径的长度为偶数,则只有$dep$(点到$root$的距离)为$frac{len}{2}$的点有用。

    否则,$dep$为可以为$frac{len}{2}$或$frac{len}{2}-1$。

    对于第一种情况,我们可以分出$a_{i}$代表第$i$个儿子的有用节点个数,还剩下一些没用的节点。

    对于第二种情况,显然长度为$frac{len}{2}$的点属于一个儿子,那么我们可以合并所有长度为$frac{len}{2}-1$的点。

    这样就将模型转化成了给定$n$个$a_{i}$和$m$个没用的点,每次选出一个点标记为黑色。

    可以重复选,知道只剩下一个集合有未标记的点的期望步数。

    我们枚举最后剩下那个集合,计算贡献。

    设这个集合的$a_{i}$为$t$,$sum a_{i}=sum$,$m$个无用点,则:

    $sum_{i=1}^{t}sum_{j=0}^{m}(sum-i+j)!*frac{sum-t}{sum-i+j}inom{t}{i}*inom{m}{j}*f[sum-i+j]*frac{(m-j+i)!}{(sum+m)!}$

    $i,j$分别是这个集合剩下点的个数以及插入的无用的点的个数。

    $f[i]$代表从0走到$i$个点的期望步数,显然为$sum_{i=1}^{x}frac{n}{n-i+1}$。

    这样过不了,然后我们发现$j-i$是一个定值我们只需要化简如下式子:

    $sum_{i=0}^{n}inom{n}{i}*inom{m}{c+i}=sum_{i=0}^{n}inom{n}{n-i}*inom{m}{c+i}=inom{n+m}{n+c}$

    然后就变成线性的做法了!

    UPD:我傻了,上述我的推导是$O(n*sqrt(n))$的算法,只不过出题人没有卡,我自己hack了自己。。。

    我真的好傻啊,其实无用点是不用管的,因为他不管选没选都可以再选,这样就可以理解为选第useless+i个点。

    然后就把那个难处理的无用点个数就抛弃了!我好傻啊。注释掉的就是原来的代码。

    #include <bits/stdc++.h>
    #define for1(a,b,i) for(int i=a,end_=b;i<=end_;++i)
    #define FOR2(a,b,i) for(int i=a,end_=b;i>=end_;--i)
    using namespace std;
    typedef long long ll;
    
    #define M 1000005
    #define mod 998244353
    int n,cnt;
    int f[M],js[M],ni[M],inv[M];
    int pos,root,e_size,head[M],du[M],dep[M],Max[M];
    
    vector <int> sta;
    
    struct node {int v,nxt;}e[M*2];
    
    inline void e_add(int u,int v) {
        e[++e_size]=(node){v,head[u]};
        head[u]=e_size;
    }
    
    inline int qpow(int x,int ci) {
        int sum=1;
        for(;ci;ci>>=1,x=1ll*x*x%mod)
            if(ci&1) sum=1ll*x*sum%mod;
        return sum;
    }
    
    inline int get_C(int x,int y) {
        if(x<y) return 0;
        return 1ll*js[x]*ni[y]%mod*ni[x-y]%mod;
    }
    
    inline void inc(int &x,int y) {x+=y,x-=x>=mod?mod:0;}
    
    inline void dfs(int x,int fa,int now) {
        dep[x]=Max[x]=dep[fa]+1;
        cnt+=now==dep[x]&&du[x]==1;
        for(int i=head[x];i;i=e[i].nxt) {
            int v=e[i].v;
            if(v==fa) continue;
            dfs(v,x,now);
            Max[x]=max(Max[x],Max[v]);
        }
    }
    
    int main () {
        //freopen("a.in","r",stdin);
        scanf("%d",&n);
        for1(2,n,i) {
            int x,y;
            scanf("%d%d",&x,&y);
            ++du[x],++du[y];
            e_add(x,y),e_add(y,x);
        }
        dfs(1,0,0);
        for1(1,n,i) if(dep[i]>dep[pos]) pos=i;
        dfs(pos,0,0);
        for1(1,n,i) if(Max[i]>Max[root]&&(Max[i]+1>>1)==dep[i]) root=i;
        
        if(Max[pos]&1) {
            dfs(root,0,0);
            pos=0;
            for1(1,n,i) if(dep[i]>dep[pos]) pos=i;
            for(int i=head[root];i;i=e[i].nxt) {
                int v=e[i].v;
                cnt=0;
                dfs(v,root,dep[pos]);
                if(cnt) sta.push_back(cnt);
            }
        }
        else {
            dfs(root,0,0);
            pos=0;
            for1(1,n,i) if(dep[i]>dep[pos]) pos=i;
            
            cnt=0;
            dfs(root,0,dep[pos]);
            sta.push_back(cnt);
            cnt=0;
            dfs(root,0,dep[pos]-1);
            sta.push_back(cnt);
        }
        
        js[0]=1;
        for1(1,n,i) js[i]=1ll*i*js[i-1]%mod;
        ni[n]=qpow(js[n],mod-2);
        FOR2(n,1,i) ni[i-1]=1ll*i*ni[i]%mod;
        for1(1,n,i) inv[i]=1ll*ni[i]*js[i-1]%mod;
        
        /*
        int ans=0;
        int sum=0,tot_x=0;
        for1(1,n,i) tot_x+=du[i]==1;
        for1(1,sta.size(),i) sum+=sta[i-1];
        for1(1,tot_x,i) inc(f[i]=f[i-1],1ll*tot_x*inv[tot_x-i+1]%mod);
        tot_x-=sum;
        
        for1(1,sta.size(),i) {
            int sel=0;
            int t=sta[i-1];
            for1(-t,tot_x-1,j) {
                sel=get_C(t+tot_x,t+j);
                if(j>=0) inc(sel,mod-get_C(tot_x,j));
                inc(ans,1ll*(sum-t)*js[sum+j]%mod*inv[sum+j]%mod*f[sum+j]%mod*js[tot_x-j]%mod*sel%mod);
            }
        }
        ans=1ll*ans*ni[sum+tot_x]%mod;
        cout<<ans<<endl;
        */
        
        int ans=0;
        int sum=0,tot_x=0;
        for1(1,n,i) tot_x+=du[i]==1;
        for1(1,sta.size(),i) sum+=sta[i-1];
        
        for1(1,sum,i) inc(f[i]=f[i-1],1ll*tot_x*inv[sum-i+1]%mod);
        
        for1(1,sta.size(),i) {
            int t=sta[i-1];
            for1(1,t,j) 
                inc(ans,1ll*js[sum-j-1]*(sum-t)%mod*f[sum-j]%mod*get_C(t,j)%mod*js[j]%mod*ni[sum]%mod);
        }
        cout<<ans<<endl;
    }
    View Code

     C.新年的叶子

    一眼上去simpson积分,然后只拿了50分。

    random_shuffle了一下之后拿了80分。

    感觉还有很多优化的余地,比如说最后一个点就不用积分了,直接解出区间即可。

    正解非常巧妙,将一个小数分成了整数部分和小数部分。

    对于$li=ri$我们直接令他为一个确定的解,否则在$[li,ri-1]$中枚举整数部分。

    另一个数为$p_{i}+q_{i}$,$p_{i}$为整数部分。则限制条件如下:

    $q_{i}-q_{j}geq a_{i,j}-p_{i}+p_{j}$

    设后面的整数部分为$c$。

    若$c>0$显然无解。

    若$c<0$显然没有限制。

    若$c=0$才会有$q_{i}>q_{j}$的限制。

    这样就好做了,直接dp出满足条件的排列个数即可。

    #include <bits/stdc++.h>
    #define for1(a,b,i) for(int i=a,end_=b;i<=end_;++i)
    #define FOR2(a,b,i) for(int i=a,end_=b;i>=end_;--i)
    using namespace std;
    typedef long long ll;
    inline int read() {
        int f=1,sum=0;
        char x=getchar();
        for(;(x<'0'||x>'9');x=getchar()) if(x=='-') f=-1;
        for(;x>='0'&&x<='9';x=getchar()) sum=sum*10+x-'0';
        return f*sum;
    }
    
    #define M 10
    #define N 40
    int n,p[M];
    double ans;
    int f[N],l[M],r[M],a[M][M];
    
    inline double calc_() {
        int to[M]={0};
        for1(1,n,i) for1(1,n,j) if(i!=j) {
            int x=a[i][j]-p[i]+p[j];
            bool t[2]={l[i]==r[i],l[j]==r[j]};
            if(x<0) continue;
            if(x>0) return 0;
            if(t[0]&&!t[1]) return 0;
            if(!t[0]&&!t[1]) to[i]|=1<<j-1;
        }
        
        memset(f,0,sizeof(f));
        f[0]=1;
        for1(1,(1<<n)-1,i) {
            bool vis=0;
            for1(1,n,j) if((i>>j-1&1)&&(l[j]==r[j])) vis=1;
            if(vis) continue;
            for1(1,n,j) if(i>>j-1&1) {
                int t=i^(1<<j-1);
                if((t&to[j])!=to[j]) continue;
                f[i]+=f[t];
            }
        }
        
        int cnt=0,all=(1<<n)-1;
        for1(1,n,i) if(l[i]==r[i]) ++cnt,all^=1<<i-1;
        
        double sum=f[all];
        for1(1,n-cnt,i) sum/=i;
        return sum;
    }
    
    inline void dfs(int x) {
        if(x==n+1) return ans+=calc_(),void();
        if(l[x]==r[x]) {
            p[x]=l[x];
            dfs(x+1);
        }
        else {
            for1(l[x],r[x]-1,i) {
                p[x]=i;
                dfs(x+1);
            }
        }
    }
    
    int main() {
        n=read();
        for1(1,n,i) l[i]=read(),r[i]=read();
        for1(1,n,i) for1(1,n,j) a[i][j]=read();
        for1(1,n,i) if(a[i][i]>0) return puts("0.00000000"),0;
        
        dfs(1);
        for1(1,n,i) if(l[i]!=r[i]) ans/=r[i]-l[i];
        printf("%.8f
    ",ans);
    }
    View Code

    D.新年的代码

    想到了AGC027E的思路。

    但是无法证明为什么分段一定是最优的以及没有发现一段的贡献为$n-1$或$n$。

    首先我们考虑变换在模3意义下是不变的。

    性质1:对于一个串S,我们可以花费最多n步使得前n-1个字符变成目标字符串。

    数学归纳法,花费n步都是因为最后剩下两个字符花费了两步,例如$ab$到$ba$。

    这时我们可以省下这两步,让最后三个花费3的代价,总步数为$n-1-2+3=n$。

    性质2:对于串$s1s2,t1t2$。若$s1=t1,s2=t2$,则合并不优。

    这里$=$定义为长度相等且$mod$3之后相等。

    因为合并我们至少要在交界处操作一次,次数:$1+n1+n2-1=n1+n2$。

    然后我们就可以check一段是否可以用n-1次操作来弄,否则为n次。

    因为操作为n-1次,所以$forall i,i+1$都要被操作一次。

    这个非常好证,如果不是这样则说明可以分更小的段,与前提不符。

    这样影响的就只是操作顺序了。

    然后就可以直接dp了,$f[i][a][b]$代表$1-i-1$都相等,第$i$个字符为$a,b$是否可以。

    #include <bits/stdc++.h>
    #define for1(a,b,i) for(int i=a,end_=b;i<=end_;++i)
    #define FOR2(a,b,i) for(int i=a,end_=b;i>=end_;--i)
    using namespace std;
    typedef long long ll;
    inline int read() {
        int f=1,sum=0;
        char x=getchar();
        for(;(x<'0'||x>'9');x=getchar()) if(x=='-') f=-1;
        for(;x>='0'&&x<='9';x=getchar()) sum=sum*10+x-'0';
        return f*sum;
    }
    
    #define M 500005
    int n;
    char s[M],t[M];
    bool f[M][3][3];
    
    inline int st(char x) {
        if(x=='G') return 1;
        return x=='B'?0:2;
    }
    
    inline bool check(int l,int r) {
        memset(f[l],0,sizeof(f[l]));
        f[l][s[l]][t[l]]=1;
        for1(l,r-1,i) {
            memset(f[i+1],0,sizeof(f[i]));
            for1(0,2,j) for1(0,2,k) {
                if(!f[i][j][k]) continue;
                if(s[i+1]!=k) f[i+1][(j+s[i+1]-k+3)%3][t[i+1]]=1;
                if(t[i+1]!=j) f[i+1][s[i+1]][(k+t[i+1]-j+3)%3]=1;
            }
        }
        for1(0,2,i) if(f[r][i][i]) return 1;
        return 0;
    }
    
    int main() {
        //freopen("a.in","r",stdin);
        n=read();
        scanf("%s%s",s+1,t+1);
        for1(1,n,i) s[i]=st(s[i]),t[i]=st(t[i]);
        
        int ans=0;
        for(int l=1,r;l<=n;l=r+1) {
            r=l;
            int c[2]={s[l],t[l]};
            while (c[0]!=c[1]) {
                ++r;
                c[0]=(c[0]+s[r])%3;
                c[1]=(c[1]+t[r])%3;
            }
            if(l==r) continue;
            ans+=r-l+1-check(l,r);
        }
        printf("%d
    ",ans);
    }
    View Code
  • 相关阅读:
    洛谷 P1226 【模板】快速幂||取余运算 题解
    洛谷 P2678 跳石头 题解
    洛谷 P2615 神奇的幻方 题解
    洛谷 P1083 借教室 题解
    洛谷 P1076 寻宝 题解
    洛谷 UVA10298 Power Strings 题解
    洛谷 P3375 【模板】KMP字符串匹配 题解
    Kafka Shell基本命令
    Mybatis与Hibernate的详细对比
    MyBatis简介
  • 原文地址:https://www.cnblogs.com/asd123www/p/9892716.html
Copyright © 2011-2022 走看看