C - Madoka and Formal Statement,D - Madoka and The Corruption Scheme,D-一人行者

141 阅读3分钟

C - Madoka and Formal Statement

首先a[i]>b[i]是不可以的,再有就是a[i]!=b[i]且b[i]>b[i+1]+1也是不可以的,因为a[i]最多增到a[i+1]+1。

重要的是证明:假设j=i+1,如果a[i]!=b[i],a[i]>a[j],那么a[j]要增长到a[i]可以变化才可以,只要没有不符合条件的情况,a[j]只要增到b[j]了那么a[i]也一定会增到b[i]。我们可以在j和i之间连一条j->i的有向边,表示j<i,那么按这种规则连下去一定不是环,所以总是可以的

Codeforces Round #818 (Div. 2) A~E - 知乎 (zhihu.com)

ll t,n,a[200005],b[200005];
int main(){
    cin>>t;
    while(t--){
        cin>>n;
        for(int i=1;i<=n;i++) cin>>a[i];
        for(int j=1;j<=n;j++) cin>>b[j];
        ll flag=1;
        for(int i=1;i<=n;i++){
            ll j=(i+1)%n;
            if(j==0) j=n;
            if(a[i]>b[i]){flag=0;break;}
            if(a[i]!=b[i]&&b[i]>b[j]+1){flag=0;break;}
        }
        if(flag) printf("yEs\n");
        else printf("no\n");
    }
    return 0;
}

D - Madoka and The Corruption Scheme

根据贪心策略,在k=0~n的过程中优先填较小的数,然后就可以推出n=6左右的情况,然后把k=1~n做差就可以发现,每个差值都是一个组合数(但是没发现是组合数啊啊啊啊啊!),然后这题就做完了,,还是对组合数不够敏感,对前缀和还挺敏感的一直在想如何用前缀和做,,,

Codeforces Round #818 (Div. 2) A~E - 知乎 (zhihu.com)

ll fac[100005];
ll C(ll a,ll b){
    return (fac[a]*getinv(fac[a-b])%mod)*getinv(fac[b])%mod;
}
ll n,k,a[100005];
int main(){
    fac[0]=1;
    for(ll i=1;i<=100000;i++) fac[i]=fac[i-1]*i%mod;
    scanf("%lld%lld",&n,&k);
    if(k>n) k=n;
    a[0]=1;
    for(int i=1;i<=k;i++){
        a[i]=(a[i-1]+C(n,i))%mod;
    }
    printf("%lld\n",a[k]);
    return 0;
}

D-一人行者_牛客练习赛102 (nowcoder.com) 换根dp,前后缀积

忘了不开这道题了,一下午没了,,,

f当前子树包含的所有连通块个数,g当前子树包含当前节点的连通块个数,当前节点的父亲去除当前节点后的连通块个数,删去当前节点与父亲的边的与父亲那颗树的连通块个数,看了几个题解,还不如一个代码来的清晰,,,为了防止除0用了前后缀积,感觉并不像是换根dp,只是一个比较难的树形dp,硬要说换根的话就是反过来求去除v之后的u有多少联通子集用到了,有些需要注意到了

代码查看 (nowcoder.com)

29. 牛客-一人行者 - Theophania - 博客园 (cnblogs.com)

ll n,m,k,a[500005],b[500005];
vector<ll>h[500005];
ll fa[500005],f[500005],g[500005];
// f当前子树包含的所有连通块个数
// g当前子树包含当前节点的连通块个数
void dfs(ll u,ll fat){
    if(fat) h[u].erase(find(h[u].begin(),h[u].end(),fat));
    fa[u]=fat;
    g[u]=1;
    for(auto v:h[u]){
        dfs(v,u);
        g[u]=g[u]*(g[v]+1)%mod;
        f[u]=(f[u]+f[v])%mod;
    }
    f[u]=(f[u]+g[u])%mod;
}
ll df[500005];//当前节点的父亲去除当前节点后的连通块个数
ll sf[500005];//删去当前节点与父亲的边的与父亲那颗树的连通块个数
void dfs2(ll u){
    ll len=h[u].size();
    vector<ll>pre(len+1,0),suf(len+2,0);
    for(int i=0;i<=len;i++) pre[i]=(i?pre[i-1]*(g[h[u][i-1]]+1)%mod:1);
    for(int i=len+1;i>=1;i--) suf[i]=(i>len?1:suf[i+1]*(g[h[u][i-1]]+1)%mod);
    //cout<<"sss "<<u<<endl;
    ll tmp=df[u]+1;
    for(int i=1;i<=len;i++){
        ll v=h[u][i-1];
        df[v]=(tmp*pre[i-1]%mod)*suf[i+1]%mod;
       sf[v]=((df[v]+sf[u]+f[u]-g[u]-f[v])%mod+mod)%mod;
        //u去除v后的连通块个数加上u的父亲不包含u的联通子集个数再加上u子树中不包含u的子集个数
        //再减去v子树所有的联通子集个数就是去除了v点的联通子集个数
       // cout<<"sss ssaaa "<<v<<" "<<u<<endl;
        dfs2(v);
    }
}
int main(){
    scanf("%lld",&n);
    for(int i=1;i<n;i++){
        scanf("%lld%lld",&a[i],&b[i]);
        h[a[i]].push_back(b[i]);
        h[b[i]].push_back(a[i]);
    }
    dfs(1,0);
   // cout<<" sss"<<endl;
    dfs2(1);
    //cout<<" sss"<<endl;
    for(int i=1;i<n;i++){
        if(fa[a[i]]==b[i]) printf("%lld %lld\n",f[a[i]],sf[a[i]]);
        else printf("%lld %lld\n",sf[b[i]],f[b[i]]);
    }
    return 0;
}