T1
这题是个换根DP,但是没想到所以调了一万年
显然的,所有mexp不会超过29(10个质数),所以我们可以把权值随便改一下
我的做法是对于每个点处理出到根节点的mexp的值为i的个数
然后跑第二遍dfs的时候对于每个点权值比他小的祖先跑一个单调栈,然后对于单调栈中依次处理经过这一个点而不经过更早祖先的节点的权值和,还要删掉下一个的子树中的权值
#include<bits/stdc++.h>
#define N 200005
using namespace std;
int su[15]={0,2,3,5,7,11,13,17,19,23,29,31};
int x[N],tot[N],cnt[N][15],stk[N][15],w[N][15],to[N][15],ans[N],f[N][20],dis[N][20],dep[N];
vector<int>y[N];
int DEP(int u,int v){int go=dep[u]-dep[v],now=1e9;for(int i=18;i>=0;i--)if(go&(1<<i)){now=min(now,dis[u][i]);u=f[u][i];}return now;
}
int LCA(int u,int v){int go=dep[u]-dep[v]-1;for(int i=18;i>=0;i--)if(go&(1<<i))u=f[u][i];return u;
}
void dfs(int u,int fa){dep[u]=dep[fa]+1;f[u][0]=fa;dis[u][0]=x[fa];cnt[u][x[u]]=1;for(int i=0;i<y[u].size();i++)if(y[u][i]!=fa){dfs(y[u][i],u);for(int j=1;j<=11;j++)cnt[u][j]+=cnt[y[u][i]][j];}for(int i=x[u]+1;i<=11;i++){cnt[u][x[u]]+=cnt[u][i];cnt[u][i]=0;}return;
}
void solve(int u,int fa){for(int i=1;i<=tot[fa];i++)stk[u][i]=stk[fa][i];tot[u]=tot[fa];while(tot[u]&&x[stk[u][tot[u]]]>=x[u])tot[u]--;for(int i=1;i<=tot[u];i++)w[u][i]=LCA(u,stk[u][i]);for(int i=1;i<tot[u];i++){int v=x[stk[u][i+1]];for(int j=1;j<=11;j++)to[u][j]=0;for(int j=1;j<=v;j++)to[u][j]=cnt[w[u][i+1]][j];for(int j=v+1;j<=11;j++)to[u][v]+=cnt[w[u][i+1]][j];for(int j=1;j<=v;j++)ans[u]+=(cnt[w[u][i]][j]-to[u][j])*su[j];for(int j=v+1;j<=11;j++)ans[u]+=(cnt[w[u][i]][j]-to[u][j])*su[v];}int v=x[u];for(int j=1;j<=11;j++)to[u][j]=0;for(int j=1;j<=v;j++)to[u][j]=cnt[w[u][tot[u]]][j];for(int j=v+1;j<=11;j++)to[u][v]+=cnt[w[u][tot[u]]][j];for(int j=1;j<=v;j++)ans[u]+=to[u][j]*su[j];stk[u][++tot[u]]=u;for(int i=0;i<y[u].size();i++)if(y[u][i]!=fa)solve(y[u][i],u);return;
}
signed main(){int T;scanf("%d",&T);while(T--){int n;scanf("%d",&n);for(int i=1;i<=n;i++){scanf("%d",&x[i]);for(int j=1;;j++)if(x[i]%su[j]){x[i]=j;break;}}for(int i=1;i<n;i++){int u,v;scanf("%d%d",&u,&v);y[u].push_back(v);y[v].push_back(u);}tot[0]=1;stk[0][1]=0;dfs(1,0);for(int i=1;i<=18;i++)for(int j=1;j<=n;j++){f[j][i]=f[f[j][i-1]][i-1];dis[j][i]=min(dis[j][i-1],dis[f[j][i-1]][i-1]);}solve(1,0);for(int i=1;i<=n;i++)printf("%d ",ans[i]);printf("\n");for(int i=0;i<=n;i++){y[i].clear();dep[i]=ans[i]=tot[i]=x[i]=0;for(int j=1;j<=11;j++)w[i][j]=stk[i][j]=cnt[i][j]=0;for(int j=0;j<=18;j++)f[i][j]=dis[i][j]=0;}}return 0;
}
T2
对于最优决策博弈论题正常要倒推,因为只有后效性没有前效性
这题就是设计状态 \(dp_{i,j}\) 为还剩 \(i\) 个回合,需要做 \(j\) 次加法的情况结果
边界情况是 \(dp_{i,0}=0,dp_{i,i}=i\times k\)。
转移是 \(dp_{i,j}=\max\min(dp_{i-1,j}-x,dp_{i-1,j-1}+x)\),这是俩关于 \(x\) 一上一下的一次函数,所以当取到交点时最优,就是 \(\lfloor(dp_{i-1,j-1}+dp_{i-1,j})/2\rfloor\)
注意到这个式子中如果去掉除 \(2\) 那么 \(dp_{i,j}\) 对 \(dp_{n,m}\) 的贡献等价于他俩之间的路径数乘上值
那么我们就对于边界情况处理贡献,\(dp_{i,0}\) 不用管,\(dp_{i,i}\) 需要先向下走一步躲算重,再组合数算贡献,然后除 \(2^{n-i}\) 取总和,注意到这里的 \(1\le i\le m\)。
#include<bits/stdc++.h>
#define N 1000005
#define int long long
#define mod 1000000007
using namespace std;
int mpow(int a,int p){int ans=1;while(p){if(p%2)ans=ans*a%mod;a=a*a%mod;p=p/2;}return ans;
}
int P[N],inv[N];
int C(int u,int v){return P[u]*inv[v]%mod*inv[u-v]%mod;}
signed main(){P[0]=inv[0]=1;for(int i=1;i<=1e6;i++){P[i]=P[i-1]*i%mod;inv[i]=inv[i-1]*mpow(i,mod-2)%mod;}int T;scanf("%lld",&T);while(T--){int n,m,k,ans=0;scanf("%lld%lld%lld",&n,&m,&k);if(n==m){printf("%lld\n",n*k%mod);continue;}for(int i=1;i<=m;i++)ans=(ans+C(n-i-1,m-i)*mpow(mpow(2,n-i),mod-2)%mod*i%mod*k%mod)%mod;printf("%lld\n",ans);}return 0;
}