zoukankan      html  css  js  c++  java
  • dp

    黑白树

    一棵n个点的有根树,1号点为根,相邻的两个节点之间的距离为1。树上每个节点i对应一个值k[i]。每个点都有一个颜色,初始的时候所有点都是白色的。
    你需要通过一系列操作使得最终每个点变成黑色。每次操作需要选择一个节点i,i必须是白色的,然后i到根的链上(包括节点i与根)所有与节点i距离小于k[i]的点都会变黑,已经是黑的点保持为黑。问最少使用几次操作能把整棵树变黑。
    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N=5e5+100;
    
    vector<int>edge[N];
    int n,dp[N],ans,node[N],val[N],f[N];
    
    void dfs(int x,int fa) {
        dp[x]=val[x];
        for(auto &v:edge[x]) if(v!=fa) {
            dfs(v,x);
            dp[x]=max(dp[x],dp[v]-1);
        }
        if(!node[x]) {
            ans++;
            node[fa]=max(node[fa],dp[x]);
        }
        else node[fa]=max(node[fa],node[x]-1);
    }
    
    int main() {
        cin>>n;int x;
        for(int i=2;i<=n;i++) scanf("%d",&x),edge[x].push_back(i),f[i]=x;
        for(int i=1;i<=n;i++) scanf("%d",&val[i]),val[i]--;
        dfs(1,0);
        cout<<ans;
    }
    View Code

    F2 - Pictures with Kittens (hard version)

    题意

    给你一个数列aa,你需要选择mm个元素,使得连续的kk个元素都至少有一个被选中。

    需要你最大化选出来的所有数的和。

    题解

    开多个滑动窗口优化

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N=5e5+100;
    
    ll mod1=1238532465;
    ll mod2=2309412751;
    ll mod=1e9+7;
    map<pair<ll,ll>,int>mp;
    int u[N],v[N],vis[N];
    ll h1[N],h2[N];
    ll m1[N],m2[N];
    int main() {
        int cas;cin>>cas;
        m1[0]=m2[0]=1;
        for(int i=1;i<=N-10;i++) {
            m1[i]=m1[i-1]*1235463%mod1;
            m2[i]=m2[i-1]*1030007%mod2;
        }
        while(cas--) {
            int n,m;
            cin>>n>>m;
            mp.clear();
            for(int i=1;i<=n;i++) vis[i]=0,h1[i]=h2[i]=0;
            for(int i=1;i<=m;i++) {
                scanf("%d%d",&u[i],&v[i]);
                if(u[i]==v[i]) {
                    vis[u[i]]=1;
                    continue;
                }
                h1[u[i]]=(h1[u[i]]+m1[v[i]])%mod1;
                h2[u[i]]=(h2[u[i]]+m2[v[i]])%mod2;
                h1[v[i]]=(h1[v[i]]+m1[u[i]])%mod1;
                h2[v[i]]=(h2[v[i]]+m2[u[i]])%mod2;
            }
    
            ll ans=0;
            for(int i=1;i<=n;i++) if(!vis[i]) {
                ans+= mp[{h1[i],h2[i]}] ;
                mp[{h1[i],h2[i]}]++;
            }
            mp.clear();
            for(int i=1;i<=n;i++) if(vis[i]) {
                ans+= mp[{h1[i],h2[i]}] ;
                mp[{h1[i],h2[i]}]++;
            }
            ll a1,a2,b1,b2;
            for(int i=1;i<=m;i++) if(!vis[u[i]]&&!vis[v[i]]||vis[u[i]]&&vis[v[i]]) {
                if(u[i==v[i]) continue;
                a1=(h1[v[i]]-m1[u[i]]+mod1)%mod1;
                b1=(h2[v[i]]-m2[u[i]]+mod2)%mod2;
                a2=(h1[u[i]]-m1[v[i]]+mod1)%mod1;
                b2=(h2[u[i]]-m2[v[i]]+mod2)%mod2;
                if(a1==a2&&b1==b2) ans++;
            }
            cout<<ans<<endl;
        }
    }
    View Code

    E - The Unbearable Lightness of Weights

    题意

    给你 nn 个砝码,你知道这些砝码都有什么质量,但是你无法将质量和确切的砝码一一对应。

    你的朋友知道每个砝码各是什么质量,你现在可以问她一个问题。你指定一个 kk 和一个 ww,她会告诉你是否有 kk 个砝码质量总和为 ww,(并且给你指出那 kk 个砝码)。现在问你,在这次提问之后你可以确定几个砝码的质量。

    题解

    • 可以观察发现 ,当数的种类小于等于2时,答案为n
    • 否则,只能对同一种数字进行询问,可以使用计数dp
    • 计数dp用unordermap来写比较方便
    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    const int N=1e6+100;
    
    unordered_map<int,int> mp[105];
    
    int n,a[N],cnt[105],sum;
    
    int main() {
        cin>>n;
        for(int i=1;i<=n;i++) {
            cin>>a[i];
            if(++cnt[a[i]]==1) sum++;
        }
        if(sum<=2) return printf("%d",n),0;
        mp[0][0]=1;
        for(int i=1;i<=100;i++) if(cnt[i]) {
            for(int c=99;c>=0;c--) for(auto &v:mp[c]) {
                for(int j=1;j<=cnt[i];j++) {
                    if(j+c>100) break;
                    mp[j+c][v.first+i*j]+=v.second;
                }
            }
        }
        int ans=1;
        for(int i=1;i<=100;i++) if(cnt[i]) {
            for(int j=1;j<=cnt[i];j++)
                if(mp[j][j*i]==1) ans=max(ans,j);
        }
        cout<<ans;
    }
    View Code

    Tree

    题意:

    修修去年种下了一棵树,现在它已经有n个结点了。
    修修非常擅长数数,他很快就数出了包含每个点的连通点集的数量。
    澜澜也想知道答案,但他不会数数,于是他把问题交给了你,输出mod1e9+7,n=1e5。

    题解:

    • 如果题目只要求求出每个点的子树内联通点集数量,那很显然是 所有儿子dp值加一 的积
    • 当时往上衍生的方案数有点难处理,我们发现显然根结点的答案就是dp值
    • 假设我们当前处理结点x,我们已知x的父节点的答案,那么很显然为那么往上面衍生的答案数很显然为temp: ans[fa]/(dp[x]+1)  ,所以ans[x]=(temp+1)*dp[x]
    • 但是这题还有一个坑点 如果之前乘上一个dp[x]+1的时候,dp[x]+1%mod恰好为0,如今想要将其贡献消除,乘上一个逆元是做不到的
    • 可以采用暴力消除影响
    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N=1e6+100;
    
    const ll mod=1e9+7;
    
    ll fast(ll x,ll n) {
        ll ans=1;
        for(;n;n>>=1,x=x*x%mod)
            if(n&1) ans=ans*x%mod;
        return ans;
    }
    ll inv(ll x) {
        return fast(x,mod-2);
    }
    
    vector<int>edge[N];
    ll dp[N],ans[N],sta[N],f[N];
    
    void dfs(int x,int fa) {
        dp[x]=1;f[x]=fa;
        for(auto &v:edge[x]) if(v!=fa) {
            dfs(v,x);
            dp[x]*=(dp[v]+1);
            dp[x]%=mod;
        }
    }
    
    void dfs2(int x,int fa) {
        if(x!=1) {
            if((dp[x]+1)%mod) {
                sta[x]=ans[fa]*inv(dp[x]+1)%mod;
                ans[x]=dp[x]*(1+sta[x])%mod;
            }
            else {
                ll temp=sta[fa]+1;
                for(auto v:edge[fa]) if(v!=x&&v!=f[fa])
                    temp=temp*(dp[v]+1)%mod;
                sta[x]=temp;
                ans[x]=(temp+1)*dp[x]%mod;
            }
        }
        for(auto v:edge[x])
            if(v!=fa) dfs2(v,x);
    }
    
    int main() {
        int n;cin>>n;
        for(int i=1;i<n;i++) {
            int u,v;
            scanf("%d%d",&u,&v);
            edge[u].push_back(v);
            edge[v].push_back(u);
        }
        dfs(1,0);
        ans[1]=dp[1];
        dfs2(1,0);
        for(int i=1;i<=n;i++) {
            printf("%lld
    ",ans[i]);
        }
    }
    View Code

     变大!

     题解:

    注意长度为L的线段操作数为L/2,发现这一点就可以dp了

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N=50+10;
    
    int dp[N][N],n,a[N];
    int main() {
        int cas;cin>>cas;
        while(cas--) {
            cin>>n;
            for(int i=1;i<=n;i++) scanf("%d",&a[i]);
            memset(dp,0,sizeof dp);
            for(int i=1;i<=n;i++) {
                int maxx=a[i];
                for(int j=i-1;j>=0;j--) {
                    for(int k=(i-j)/2;k<=i;k++)
                        dp[i][k]=max(dp[i][k],dp[j][k-(i-j)/2]+maxx*(i-j));
                    maxx=max(maxx,a[j]);
                }
            }
            for(int i=1;i<=n;i++) printf("%d ",dp[n][i]);cout<<endl;
        }
    
    }
    View Code

    K重排列

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N=100+1000;
    const ll mod=998244353;
    
    ll fac[N],inv[N],inv1[N];
    ll fast(ll x,ll n) {
        ll ans=1;
        for(;n;n>>=1,x=x*x%mod)
            if(n&1) ans=ans*x%mod;
        return ans;
    }
    ll init(){
        fac[0]=fac[1]=1;
        for(int i=2;i<N;i++) fac[i]=fac[i-1]*i%mod;
        inv[N-1]=fast(fac[N-1],mod-2);
        for(int i=N-2;i>=0;i--)
            inv[i]=(inv[i+1]*(i+1))%mod;
        inv1[0]=inv1[1]=1;
        for(int i=2;i<N;i++)
            inv1[i]=(mod-mod/i)*inv1[mod%i]%mod;
    }
    
    ll C(ll n,ll m) {
        if(!m||m==n) return 1;
        return ((fac[n]*inv[m]%mod*inv[n-m])%mod);
    }
    vector<int>a;
    ll dp[60];
    int main() {
        init();
        int cas;cin>>cas;
        while(cas--) {
            a.clear();memset(dp,0,sizeof dp);
            ll n,k;cin>>n>>k;
            for(int i=2;i<=n&&i<=k;i++) if(k%i==0) a.push_back(i);
            dp[0]=1;
            for(int i=1;i<=n;i++) {
                dp[i]=dp[i-1];
                for(int v:a) if(i>=v)
                    dp[i]=(dp[i]+dp[i-v]%mod*C(i-1,v-1)%mod*fac[v]%mod*inv1[v]%mod)%mod;
    
            }
            cout<<dp[n]<<endl;
        }
    }
    View Code

    CF1093F Vasya and Array

     是大于等于len

    dp容斥一下就行了

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int mod=998244353;
    const int N=1e5;
    ll dp[N][101],s[N];
    
    int n,k,len;
    int cnt[N][101],a[N];
    int main() {
        cin>>n>>k>>len;
        if(len==1) return printf("0"),0;
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        for(int i=1;i<=n;i++) {
            for(int j=1;j<=k;j++)
                cnt[i][j]=cnt[i-1][j]+(a[i]==-1||a[i]==j);
        }
        s[0]=1;
        if(a[1]+1) dp[1][a[1]]=s[1]=1;
        else {
            for(int i=1;i<=k;i++) dp[1][i]=1;
            s[1]=k;
        }
        for(int i=2;i<=n;i++) {
            for(int j=1;j<=k;j++) {
                if(a[i]!=-1&&a[i]!=j) continue;
                dp[i][j]=s[i-1];
                if(i>=len) {
                    int l=i-len;
                    if(cnt[i][j]-cnt[l][j]==len) {
                        dp[i][j]=(dp[i][j]-(s[l]-dp[l][j]+mod)%mod+mod)%mod;
                    }
                }
                s[i]=(s[i]+dp[i][j])%mod;
            }
        }
        cout<<s[n];
    }
    View Code
  • 相关阅读:
    分分钟搞定Python之排序与列表
    分分钟搞定Python之排序与列表
    联系我
    联系我
    联系我
    联系表单 1_copy
    联系表单 1_copy
    联系表单 1_copy
    联系表单 1
    联系表单 1
  • 原文地址:https://www.cnblogs.com/bxd123/p/12200587.html
Copyright © 2011-2022 走看看