20240807学习

这回讲了点简单的动态规划,终于写的出来blog了

gym105239I Path And k Vertices

题面:有一个\(n\)个点的树,每个点有点权\(a_i\),可以在任意叶子节点到根节点的路径中选\(k\)个点,求点权和的最大值。
题解:DFS的时候使用数据结构分别维护该节点到根的最大的\(k\)个点和该节点到根的剩下点,本人使用std::set
复杂度\(\text O(n(\log k+\log n))\)
话说这真的是DP吗
代码:

#include<cstdio>
#include<set>
#define int long long
const int N=300005;
int n,head[N],pedge,root,now,ans,w[N],od[N],k;
std::set<int>s1,s2;
struct Edge{
    int to,next;
}edge[N];
inline void ins(int u,int v){edge[++pedge]={v,head[u]},head[u]=pedge;}
inline void Max(int&x,int y){x<y&&(x=y);}
inline bool cmp(int x,int y){return x>y;}
void dfs(int u){
    now+=w[u],s1.insert(w[u]);
    if(s1.size()>k)now-=*s1.begin(),s2.insert(*s1.begin()),s1.erase(*s1.begin());
    if(!od[u])Max(ans,now);
    for(int e=head[u];e;e=edge[e].next)dfs(edge[e].to);
    if(s1.find(w[u])!=s1.end()){
        now-=w[u],s1.erase(w[u]);
        if(s1.size()<k&&s2.size())now+=*s2.rbegin(),s1.insert(*s2.rbegin()),s2.erase(*s2.rbegin());
    }
    else s2.erase(w[u]);
}
signed main(){
    scanf("%lld%lld",&n,&k);
    for(int i=1,u,t;i<=n;i++){
        scanf("%lld%lld",&u,&t),w[i]=t;
        if(u)ins(u,i),od[u]++;
        else root=i;
    }
    dfs(root),printf("%lld\n",ans);
    return 0;
}

gym105239C Colored Tree

题面:有\(n\)个点,点有点权\(w_i\)和颜色\(c_i\),定义好的集合满足:点集中的点不能相邻,点集中的颜色互不相同。求好的集合的点权和最大值。
题解:定义\(f(u,S,t)\),表示节点\(u\)的状态为\(t\)\(t=0\)表示不选择\(u\)\(t=1\)表示选择\(u\)),且\(u\)的子树的选取的集合构成的颜色集合。
每次让\(u\)的字节点\(v\)更新\(u\)的答案有转移方程
\(f(u,S,1)=\max_{T\sube S}(f(v,T,0)+f(u,S\setminus T,1))\)
\(f(u,S,0)=\max_{T\sube S}(\max(f(v,T,0),f(v,T,1))+f(u,S\setminus T,1))\)
复杂度\(\text O(n3^{|C|})\)
代码:

#include<cstdio>
#define int long long
const int N=1005,A=1<<10,M=A+5;
int n,w[N],c[N],head[N],pedge,f[N][M][2],C,ans;
struct Edge{
    int to,next;
}edge[N<<1];
inline void Ins(int u,int v){edge[++pedge]={v,head[u]},head[u]=pedge;}
inline void ins(int u,int v){Ins(u,v),Ins(v,u);}
inline void Max(int&x,int y){x<y&&(x=y);}
inline int max(int x,int y){return x>y?x:y;}
void dfs(int u,int father){
    f[u][1<<c[u]][1]=w[u];
    for(int e=head[u];e;e=edge[e].next){
        int v=edge[e].to;
        if(v^father){
            dfs(v,u);
            for(int s=(1<<C)-1;s;s--)for(int t=s;t;t=(t-1)&s)Max(f[u][s][0],max(f[v][t][0],f[v][t][1])+f[u][s^t][0]),Max(f[u][s][1],f[v][t][0]+f[u][s^t][1]);
        }
    }
}
signed main(){
    scanf("%lld%lld",&n,&C);
    for(int i=1;i<=n;i++)scanf("%lld",w+i);
    for(int i=1;i<=n;i++)scanf("%lld",c+i),c[i]--;
    for(int i=1,u,v;i<n;i++)scanf("%lld%lld",&u,&v),ins(u,v);
    dfs(1,0);
    for(int i=0;i<1<<C;i++)Max(ans,max(f[1][i][0],f[1][i][1]));
    return printf("%lld\n",ans),0;
}

gym105244G Evolutionary Tree Weights

题面:有\(G\)个由AGCT组成长度为\(m\)的字符串,\(T\)个询问,每次询问给出一棵树和树的叶节点分别对应哪些字符串,定义边权为两条边所对应的字符串不同的位置的个数。你可以在除了叶子节点的每一个点填上一个由AGCT组成长度为\(m\)的字符串,求字符串树的边权和的最小值。
题解:发现每一位对答案的影响独立,分别讨论每一位,令\(f(u,c)\)表示节点\(u\)\(c\)后子树的边权和,那么对\(u\)的子节点\(v\),\(f(u,c)=\sum_{v\in son(u)}\max_{k=0}^3(f(v,k)+[k\neq c])\)
代码:

#include<cstdio>
#include<cstring>
#define int long long
const int N=505,INF=0x3f3f3f3f;
char s[N][N];
int g,k,T,n,m,head[N],pedge,a[N],b[N],f[N][4],ans;
bool od[N];
struct Edge{
    int to,next;
}edge[N];
inline void ins(int u,int v){edge[++pedge]={v,head[u]},head[u]=pedge;}
inline void Min(int&x,int y){y<x&&(x=y);}
inline int min(int x,int y){return x<y?x:y;}
inline int min(int x,int y,int z,int w){return min(min(x,y),min(z,w));}
inline int trans(char c){
    return c=='A'?0:c=='G'?1:c=='C'?2:3;
}
void dfs(int u){
    if(od[u])return;
    f[u][0]=f[u][1]=f[u][2]=f[u][3]=0;
    for(int e=head[u];e;e=edge[e].next){
        int v=edge[e].to;
        dfs(v);
        for(int c=0;c<4;c++){
            int minn=INF;
            for(int k=0;k<4;k++)Min(minn,f[v][k]+(k!=c));
            f[u][c]+=minn;
        }
    }
}
signed main(){
    scanf("%lld",&g);
    for(int i=1;i<=g;i++)scanf("%s",s[i]+1);
    for(k=strlen(s[1]+1),scanf("%lld",&T);T--;){
        scanf("%lld%lld",&n,&m);
        if(n==0){
            puts("0");
            continue;
        }
        pedge=ans=0;
        for(int i=1;i<=n;i++)head[i]=od[i]=0;
        for(int i=2,p;i<=n;i++)scanf("%lld",&p),ins(p,i);
        for(int i=1;i<=m;i++)scanf("%lld%lld",a+i,b+i),od[a[i]]=1;
        for(int i=1;i<=k;i++){
            for(int j=1;j<=m;j++)f[a[j]][0]=f[a[j]][1]=f[a[j]][2]=f[a[j]][3]=INF,f[a[j]][trans(s[b
            [j]][i])]=0;
            dfs(1),ans+=min(f[1][0],f[1][1],f[1][2],f[1][3]);
        }
        printf("%lld\n",ans);
    }
    return 0;
}

gym105173I Password

题面:一个长度为\(n\)的序列\(a\),每一项都是\(1\)\(k\)的整数,对于每一个长度为\(k\)的子区间,如果内容为\(1\)\(k\)的排列,那么是好的子区间。问有多少种序列满足每个数至少被包含在一个子区间中。
题解:
\(f(i)\)表示长度为\(i\)的满足所有位置被至少一个好的子区间包含的方案数。\(g(i)\)表示在一个\(1\)\(k\)的排列插入\(i\)个数后最后\(k\)个数仍然是一个\(1\)\(k\)的排列,且插入\(i-1\)个数后不是一个\(1\)\(k\)的排列的方案数。

\[f(i)=\sum_{j=1}^kf(i-j)g(j) \]

\[g(i)=i!-\sum_{j=1}^{i-1}(j!g(i-j)) \]

时间复杂度\(\text O(nk)\)
代码:

#include<cstdio>
#define int long long
const int N=100005,mod=998244353;
int n,k,fac[N],f[N],g[N];
inline int mul(int x,int y){return x*y%mod;}
inline void Sub(int&x,int y){if((x-=y)<0)x+=mod;}
inline void Add(int&x,int y){if((x+=y)>=mod)x-=mod;}
signed main(){
    scanf("%lld%lld",&n,&k),fac[0]=1;
    for(int i=1;i<=k;i++)fac[i]=mul(fac[i-1],i);
    for(int i=1;i<=k;i++){
        g[i]=fac[i];
        for(int j=1;j<i;j++)Sub(g[i],mul(fac[j],g[i-j]));
    }
    f[k]=fac[k];
    for(int i=k;i<=n;i++)for(int j=1;j<=k&&i+j<=n;j++)Add(f[i+j],mul(f[i],g[j]));
    printf("%lld\n",f[n]);
    return 0;
}

gym105139K Points on the Number Axis B

题面:有\(n\)个数,每次等概率的选择一对相邻的数对\((x_i,x_{i+1})\),替换成\((x_i+x_{i+1})/2\),求最后的数的期望。
题解:现将求期望转化成求所有方案的总和除以总方案数\((n-1)!\),令第\(i\)个数对方案总和的贡献为\(a_i\times f(i-1,n-i)\),其中\(f(i,j)\)表示一个数左边有\(i\)个右边有\(j\)个数后对所有方案总和的贡献。

\[f(i,j)=\left(i-\frac12\right)f(i-1,j)+\left(j-\frac12\right)f(i,j-1) \]

类似与网格图路径计数可以得出:

\[f(i,j)={i+j\choose i}\prod_{k=1}^i\left(k-\frac12\right)\prod_{k=1}^j\left(k-\frac12\right) \]

预处理后复杂度\(\text O(n+\log p)\)
代码:

#include<cstdio>
#define int long long 
const int N=1000005,mod=998244353,inv2=(mod+1)/2;
int n,fac[N],pro[N],finv[N],a[N],ans;
inline int mul(int x,int y){return x*y%mod;}
inline int mul(int x,int y,int z){return mul(mul(x,y),z);}
inline int mul(int x,int y,int z,int w){return mul(mul(x,y),mul(z,w));}
inline void Mul(int&x,int y){x=mul(x,y);}
inline void Add(int&x,int y){if((x+=y)>=mod)x-=mod;}
inline void Sub(int&x,int y){if((x-=y)<0)x+=mod;}
inline int sub(int x,int y){return Sub(x,y),x;}
inline int pow(int x,int y){
    int ret=1;
    for(;y;Mul(x,x),y>>=1)if(y&1)Mul(ret,x);
    return ret;
}
inline int inv(int x){return pow(x,mod-2);}
inline int C(int x,int y){return mul(fac[x],finv[y],finv[x-y]);}
signed main(){
    scanf("%lld",&n),fac[0]=pro[0]=1;
    for(int i=1;i<=n;i++)scanf("%lld",a+i),fac[i]=mul(fac[i-1],i),pro[i]=mul(pro[i-1],sub(i,inv2));
    finv[n]=inv(fac[n]);
    for(int i=n;i;i--)finv[i-1]=mul(finv[i],i);
    for(int i=1;i<=n;i++)Add(ans,mul(a[i],pro[i-1],pro[n-i],C(n-1,i-1)));
    return printf("%lld\n",mul(ans,finv[n-1])),0;
}
posted @ 2024-08-08 15:33  junjunccc  阅读(7)  评论(0编辑  收藏  举报