[noip] 树形dp练习
P1270 “访问”美术馆
裸题。
#include<bits/stdc++.h>
#define IL inline
#define LL long long
using namespace std;
const int N=2e2+3,M=6e2+3;
int n,cnt,kin[N],f[N][M];
IL int in(){
char c;int f=1;
while((c=getchar())<'0'||c>'9')
if(c=='-') f=-1;
int x=c-'0';
while((c=getchar())>='0'&&c<='9')
x=x*10+c-'0';
return x*f;
}
void dfs(int u){
if(kin[u]) for(int i=0;i<=n;++i) f[u][i]=min(kin[u],i/5);
else{
int v=++cnt,x=in();kin[v]=in(),dfs(v);
for(int i=0;i<=n-2*x;++i) f[u][i+2*x]=f[v][i];
if(u==1) return;
v=++cnt,x=in(),kin[v]=in(),dfs(v);
for(int i=n;i>=2*x;--i)
for(int j=0;j<=i-2*x;++j)
f[u][i]=max(f[u][i],f[u][i-j-2*x]+f[v][j]);
}
}
int main()
{
n=in()-1,dfs(++cnt);
cout<<f[1][n]<<endl;
return 0;
}
P2585 [ZJOI2006]三色二叉树
裸题。
#include<bits/stdc++.h>
#define IL inline
#define LL long long
using namespace std;
const int N=5e5+10;
int n,num,cnt,ls[N],rs[N],f1[N][3],f2[N][3];
char s[N];
IL int in(){
char c;int f=1;
while((c=getchar())<'0'||c>'9')
if(c=='-') f=-1;
int x=c-'0';
while((c=getchar())>='0'&&c<='9')
x=x*10+c-'0';
return x*f;
}
void dfs(int i){
if(s[i]=='0') f1[i][0]=f2[i][0]=1;
else if(s[i]=='1'){
dfs(ls[i]=++cnt);
f1[i][0]=1+min(f1[ls[i]][1],f1[ls[i]][2]);
f1[i][1]=min(f1[ls[i]][0],f1[ls[i]][2]);
f1[i][2]=min(f1[ls[i]][0],f1[ls[i]][1]);
f2[i][0]=1+max(f2[ls[i]][1],f2[ls[i]][2]);
f2[i][1]=max(f2[ls[i]][0],f2[ls[i]][2]);
f2[i][2]=max(f2[ls[i]][0],f2[ls[i]][1]);
}
else{
dfs(ls[i]=++cnt),dfs(rs[i]=++cnt);
f1[i][0]=1+min(f1[ls[i]][1]+f1[rs[i]][2],f1[ls[i]][2]+f1[rs[i]][1]);
f1[i][1]=min(f1[ls[i]][0]+f1[rs[i]][2],f1[ls[i]][2]+f1[rs[i]][0]);
f1[i][2]=min(f1[ls[i]][0]+f1[rs[i]][1],f1[ls[i]][1]+f1[rs[i]][0]);
f2[i][0]=1+max(f2[ls[i]][1]+f2[rs[i]][2],f2[ls[i]][2]+f2[rs[i]][1]);
f2[i][1]=max(f2[ls[i]][0]+f2[rs[i]][2],f2[ls[i]][2]+f2[rs[i]][0]);
f2[i][2]=max(f2[ls[i]][0]+f2[rs[i]][1],f2[ls[i]][1]+f2[rs[i]][0]);
}
}
int main()
{
scanf("%s",s+1),dfs(cnt=1);
cout<<max(f2[1][0],max(f2[1][1],f2[1][2]))<<' '<<min(f1[1][0],min(f1[1][1],f1[1][2]))<<endl;
return 0;
}
P3174 [HAOI2009]毛毛虫
裸题,将度数转化为点权,求树的直径。
#include<bits/stdc++.h>
#define IL inline
#define M make_pair
#define LL long long
using namespace std;
const int N=3e5+3;
struct hh{
int to,nxt;
}e[N<<1];
int n,m,num,fir[N],deg[N],dis[N],vis[N];
map<pair<int,int>,int>mp;
queue<int>q;
IL int in(){
char c;int f=1;
while((c=getchar())<'0'||c>'9')
if(c=='-') f=-1;
int x=c-'0';
while((c=getchar())>='0'&&c<='9')
x=x*10+c-'0';
return x*f;
}
IL void add(int x,int y){e[++num]=(hh){y,fir[x]},fir[x]=num;}
int bfs(int s){
memset(vis,0,sizeof(vis)),dis[s]=0,q.push(s),vis[s]=1;
while(q.size()){
int u=q.front();q.pop();
for(int i=fir[u],v;v=e[i].to;i=e[i].nxt)
if(!vis[v]) vis[v]=1,dis[v]=dis[u]+deg[v]-1,q.push(v);
}
int t=1;
for(int i=2;i<=n;++i) if(dis[t]<dis[i]) t=i;
return t;
}
int main()
{
int x,y,L,R;
n=in(),m=in();
for(int i=1;i<n;++i){
x=in(),y=in();
if(x>y) swap(x,y);
if(mp[M(x,y)]) continue;
add(x,y),add(y,x),mp[M(x,y)]=1,
++deg[x],++deg[y];
}
L=bfs(1),R=bfs(L);
cout<<dis[R]+deg[L]+1<<endl;
return 0;
}
P3177 [HAOI2015]树上染色
发现当子树中黑点个数确定时,其连向父亲的边的对答案的贡献也确定了,并且我们不用关心这些点具体怎么分布。
所以定义 \(f_{i,j}\) 为子树 \(i\) 中有 \(j\) 个黑点时, \(i\) 中的边对答案的总贡献。
合并即可,复杂度 \(O(n^2)\)。
#include<bits/stdc++.h>
#define IL inline
#define LL long long
using namespace std;
const int N=2e3+3;
struct hh{
int to,nxt,w;
}e[N<<1];
int n,m,num,fir[N],siz[N];
LL f[N][N],g[N];
IL int in(){
char c;int f=1;
while((c=getchar())<'0'||c>'9')
if(c=='-') f=-1;
int x=c-'0';
while((c=getchar())>='0'&&c<='9')
x=x*10+c-'0';
return x*f;
}
IL void add(int x,int y,int z){e[++num]=(hh){y,fir[x],z},fir[x]=num;}
void dfs(int u,int fa){
siz[u]=1;
for(int i=fir[u],v;v=e[i].to;i=e[i].nxt)
if(v^fa){
dfs(v,u);
for(int j=siz[u];~j;--j)
for(int k=siz[v];~k;--k) if(j+k<=m)
f[u][j+k]=max(f[u][j+k],f[u][j]+f[v][k]+1ll*e[i].w*((m-k)*k+(n-m-(siz[v]-k))*(siz[v]-k)));
siz[u]+=siz[v];
}
}
int main()
{
int x,y,z;
n=in(),m=in();
for(int i=1;i<n;++i)
x=in(),y=in(),z=in(),
add(x,y,z),add(y,x,z);
dfs(1,0);
cout<<f[1][m]<<endl;
return 0;
}
P1453 城市环路
基环树裸题。
#include<bits/stdc++.h>
#define IL inline
#define LL long long
#define LF double
using namespace std;
const int N=1e5+10,inf=1e9;
struct hh{
int to,nxt;
}e[N<<1];
int n,m,num=1,fir[N],fa[N],val[N],bo[N<<1],dfn[N],f[N][2],s,t,ans;
LF k;
IL int in(){
char c;int f=1;
while((c=getchar())<'0'||c>'9')
if(c=='-') f=-1;
int x=c-'0';
while((c=getchar())>='0'&&c<='9')
x=x*10+c-'0';
return x*f;
}
IL void add(int x,int y){e[++num]=(hh){y,fir[x]},fir[x]=num;}
void find_loop(int u,int f){
fa[u]=f,dfn[u]=++num;
for(int i=fir[u],v;v=e[i].to;i=e[i].nxt)
if(!dfn[v]) find_loop(v,u);
else if(dfn[v]>dfn[u]){s=u,t=v,bo[i]=bo[i^1]=1;}
}
void dfs(int u,int fa,int op){
f[u][0]=0,f[u][1]=val[u];
for(int i=fir[u],v;v=e[i].to;i=e[i].nxt)
if(v^fa&&!bo[i])
dfs(v,u,op),f[u][0]+=max(f[v][0],f[v][1]),f[u][1]+=f[v][0];
if(op&&u==t) f[u][1]=-inf;
}
int main()
{
int x,y,z;
n=in();
for(int i=1;i<=n;++i) val[i]=in();
for(int i=1;i<=n;++i)
x=in()+1,y=in()+1,
add(x,y),add(y,x);
scanf("%lf",&k);
num=0,find_loop(1,0);
dfs(s,0,0),ans=f[s][0];
dfs(s,0,1),ans=max(ans,f[s][1]);
printf("%.1lf\n",ans*k);
return 0;
}
CF461B Appleman and Tree
定义 \(f_{i,0}\) 为在子树 \(i\) 中,\(i\) 所处的联通块无黑点的情况数,\(f_{i,1}\) 为有黑点的情况数。
懒得写了。
#include<bits/stdc++.h>
#define IL inline
#define LL long long
#define LF double
using namespace std;
const int N=1e5+10,p=1e9+7;
struct hh{
int to,nxt;
}e[N<<1];
int n,m,num,fir[N],col[N],f[N][2],fa[N];
IL int in(){
char c;int f=1;
while((c=getchar())<'0'||c>'9')
if(c=='-') f=-1;
int x=c-'0';
while((c=getchar())>='0'&&c<='9')
x=x*10+c-'0';
return x*f;
}
IL void add(int x,int y){e[++num]=(hh){y,fir[x]},fir[x]=num;}
IL int ksm(int a,int b){
int c=1;
while(b){
if(b&1) c=1ll*c*a%p;
a=1ll*a*a%p,b>>=1;
}
return c;
}
IL int mod(int x){return x>=p?x-p:x;}
void dfs(int u){
int sum=1;
for(int i=fir[u],v;v=e[i].to;i=e[i].nxt)
dfs(v),sum=1ll*sum*mod(f[v][0]+f[v][1])%p;
if(col[u]==1) f[u][1]=sum;
else{
f[u][0]=sum;
for(int i=fir[u],v;v=e[i].to;i=e[i].nxt)
f[u][1]=mod(f[u][1]+1ll*sum*ksm(mod(f[v][0]+f[v][1]),p-2)%p*f[v][1]%p);
}
}
int main()
{
n=in();
for(int i=2;i<=n;++i) add(fa[i]=in()+1,i);
for(int i=1;i<=n;++i) col[i]=in();
dfs(1);cout<<f[1][1]<<endl;
return 0;
}
P3647 [APIO2014]连珠线
以最开始的点为根生成树,发现蓝边必然是 \(son_u,u,fa_u\) 形式的,且点只可能处在一对生成的蓝线中。
定义 \(f_{u,0}\) 表示子树 \(u\) 中,且 \(u\) 不是被 \(insert\) 生成的最大得分,\(f_{u,1}\) 为其是由 \(insert\) 生成的最大得分。
\(f_{u,0}=\max\limits_{v \in son_u}{(f_{v,0},f_{v,1}+w)}\)
\(f_{u,1}=f_{u,0}+\max\limits_{v \in son_u}{(f_{v,0}+w-\max{(f_{v,0},f_{v,1}+w)})}\)
记录使 \(f_{u,1}\) 取得最大值及次大值的 \(v\) ,方便换根,换根时注意特判根节点。
#include<bits/stdc++.h>
#define IL inline
#define LL long long
using namespace std;
const int N=2e5+3,inf=1e9;
struct hh{
int to,nxt,w;
}e[N<<1];
int n,m,num,ans,fir[N],f[N][2],g[N][2],h[N][2];
IL int in(){
char c;int f=1;
while((c=getchar())<'0'||c>'9')
if(c=='-') f=-1;
int x=c-'0';
while((c=getchar())>='0'&&c<='9')
x=x*10+c-'0';
return x*f;
}
IL void add(int x,int y,int z){e[++num]=(hh){y,fir[x],z},fir[x]=num;}
void dfs1(int u,int fa){
for(int i=fir[u],v;v=e[i].to;i=e[i].nxt)
if(v^fa){
dfs1(v,u),f[u][0]+=max(f[v][0],f[v][1]+e[i].w);
int res=f[v][0]+e[i].w-max(f[v][0],f[v][1]+e[i].w);
if(res>h[u][0]) h[u][1]=h[u][0],h[u][0]=res;
else if(res>h[u][1]) h[u][1]=res;
}
f[u][1]=f[u][0]+h[u][0];
}
void dfs2(int u,int fa,int w){
for(int i=fir[u],v;v=e[i].to;i=e[i].nxt)
if(v^fa){
int Max=max(f[v][0],f[v][1]+e[i].w),M=max(g[u][0],g[u][1]+w);
g[v][0]=f[u][0]-Max+M;
if(f[v][0]+e[i].w-Max==h[u][0])
g[v][1]=g[v][0]+max(h[u][1],u==1?-inf:g[u][0]+w-M);
else g[v][1]=g[v][0]+max(h[u][0],u==1?-inf:g[u][0]+w-M);
dfs2(v,u,e[i].w);
}
ans=max(ans,f[u][0]+max(g[u][0],g[u][1]+w));
}
int main()
{
memset(h,-63,sizeof(h));
int x,y,z;
n=in();
for(int i=1;i<n;++i)
x=in(),y=in(),z=in(),
add(x,y,z),add(y,x,z);
dfs1(1,0),dfs2(1,0,0);
cout<<ans<<endl;
return 0;
}
P3523 [POI2011]DYN-Dynamite
不会啊。。。我太菜了呜呜呜。。。
很明显,外层要套个二分。
定义 \(f_u\) 为 \(u\) 子树中,未被覆盖的关键点与 \(u\) 距离的最大值,\(g_u\) 为 \(u\) 子树中选择的点距离 \(u\) 的最小值,贪心求解。
\(f_u=\max\limits_{v \in u}{f_v+1}\)
\(g_u=\min\limits_{v \in u}{g_v+1}\)
初值为 \(f_u=-inf\),\(g_u=inf\)。
先考虑若 \(u\) 为关键点时,\(f_u=\max{(f_u,0)}\)。
若 \(f_u+g_u<=mid\),则之前选择的点可以覆盖 \(u\) 中的关键点,\(f_u=-inf\)。
否则若 \(f_u=mid\),则 \(u\) 必须被选择了,计数器加 \(1\)。
#include<bits/stdc++.h>
#define IL inline
#define LL long long
using namespace std;
const int N=3e5+3,inf=0x3f3f3f3f;
struct hh{
int to,nxt;
}e[N<<1];
int n,m,num,fir[N],f[N],g[N],bo[N],tot,d;
IL int in(){
char c;int f=1;
while((c=getchar())<'0'||c>'9')
if(c=='-') f=-1;
int x=c-'0';
while((c=getchar())>='0'&&c<='9')
x=x*10+c-'0';
return x*f;
}
IL void add(int x,int y){e[++num]=(hh){y,fir[x]},fir[x]=num;}
void dfs(int u,int fa){
for(int i=fir[u],v;v=e[i].to;i=e[i].nxt)
if(v^fa){
dfs(v,u);
f[u]=max(f[u],f[v]+1),
g[u]=min(g[u],g[v]+1);
}
if(bo[u]&&g[u]>d) f[u]=max(0,f[u]);
if(f[u]+g[u]<=d) f[u]=-inf;
if(f[u]==d) f[u]=-inf,g[u]=0,++tot;
}
int pd(){
tot=0;
memset(f,-63,sizeof(f));
memset(g,63,sizeof(g));
dfs(1,0);
tot+=f[1]>=0;
return tot<=m;
}
int main()
{
int x,y;
n=in(),m=in();
for(int i=1;i<=n;++i) bo[i]=in();
for(int i=1;i<n;++i)
x=in(),y=in(),
add(x,y),add(y,x);
int l=0,r=n;
while(l<r){
d=l+r>>1;
if(pd()) r=d;
else l=d+1;
}
cout<<l<<endl;
return 0;
}
P3554 [POI2013]LUK-Triumphal arch
裸题。
#include<bits/stdc++.h>
#define IL inline
#define LL long long
using namespace std;
const int N=3e5+3;
struct hh{
int to,nxt;
}e[N<<1];
int n,m,k,num,f[N],fir[N];
IL int in(){
char c;int f=1;
while((c=getchar())<'0'||c>'9')
if(c=='-') f=-1;
int x=c-'0';
while((c=getchar())>='0'&&c<='9')
x=x*10+c-'0';
return x*f;
}
IL void add(int x,int y){e[++num]=(hh){y,fir[x]},fir[x]=num;}
void dfs(int u,int fa){
f[u]=-k;
for(int i=fir[u],v;v=e[i].to;i=e[i].nxt)
if(v^fa) dfs(v,u),f[u]+=f[v]+1;
if(f[u]<0) f[u]=0;
}
int pd(){
dfs(1,0);return !f[1];
}
int main()
{
int x,y;
n=in();
for(int i=1;i<n;++i)
x=in(),y=in(),
add(x,y),add(y,x);
int l=0,r=n;
while(l<r){
k=l+r>>1;
if(pd()) r=k;
else l=k+1;
}
cout<<l<<endl;
return 0;
}
P3565 [POI2014]HOT-Hotels
显然三个点中,必有两个点深度相同,三点到深度相同的两点的 \(lca\) 的距离相等。
于是定义 \(f_{i,j}\) 第三个点与 \(i\) 的距离应该为 \(j\) 的方案个数,\(g_{i,j}\) 为 \(i\) 子树中,到 \(i\) 距离为 \(j\) 的结点个数。
枚举子树转移即可。
但这题卡空间。。。
注意到边权为 \(1\),可以用长链剖分优化,注意 \(f\) 数组要开两倍,复杂度 \(O(n)\)。
#include<bits/stdc++.h>
#define IL inline
#define LL long long
using namespace std;
const int N=5e3+3;
struct hh{
int to,nxt;
}e[N<<1];
int n,num,fir[N],dep[N],d[N],son[N],s[N];
int *f[N],*g[N],p1[N<<1],p2[N],*o1=p1,*o2=p2;
LL ans;
IL int in(){
char c;int f=1;
while((c=getchar())<'0'||c>'9')
if(c=='-') f=-1;
int x=c-'0';
while((c=getchar())>='0'&&c<='9')
x=x*10+c-'0';
return x*f;
}
IL void link(int x,int y){e[++num]=(hh){y,fir[x]},fir[x]=num;}
void dfs1(int u,int fa){
dep[u]=dep[fa]+1;
for(int i=fir[u],v;v=e[i].to;i=e[i].nxt)
if(v^fa){
dfs1(v,u);
if(d[v]>d[son[u]]) son[u]=v;
}
d[u]=d[son[u]]+1;
}
void dfs2(int u,int fa){
if(son[u]) f[son[u]]=f[u]-1,g[son[u]]=g[u]+1,dfs2(son[u],u);
g[u][0]=1,ans+=f[u][0];
for(int i=fir[u],v;v=e[i].to;i=e[i].nxt)
if(v^fa&&v^son[u]){
o1+=d[v],f[v]=o1,o1+=d[v],g[v]=o2,o2+=d[v],dfs2(v,u);
for(int j=0;j<d[v];++j){
ans+=1ll*f[u][j+1]*g[v][j];
if(j) ans+=1ll*g[u][j-1]*f[v][j];
}
for(int j=0;j<d[v];++j){
f[u][j+1]+=g[u][j+1]*g[v][j];
if(j) f[u][j-1]+=f[v][j];
g[u][j+1]+=g[v][j];
}
}
}
int main()
{
int x,y;
n=in();
for(int i=1;i<n;++i)
x=in(),y=in(),
link(x,y),link(y,x);
dfs1(1,0),o1+=d[1],f[1]=o1,o1+=d[1],g[1]=o2,o2+=d[1],dfs2(1,0);
printf("%lld\n",ans);
return 0;
}
P3574 [POI2014]FAR-FarmCraft
转移贪心,按 \(f_u-2(dis_u+1)\) 降序排序,微扰法证明。
#include<bits/stdc++.h>
#define IL inline
#define LL long long
using namespace std;
const int N=5e5+3;
struct hh{
int to,nxt;
}e[N<<1];
struct kk{
int id;LL val;
bool operator<(const kk &a) const{
return val>a.val;}
}a[N];
int n,num,fir[N],dis[N];
LL f[N],c[N];
IL int in(){
char c;int f=1;
while((c=getchar())<'0'||c>'9')
if(c=='-') f=-1;
int x=c-'0';
while((c=getchar())>='0'&&c<='9')
x=x*10+c-'0';
return x*f;
}
IL void add(int x,int y){e[++num]=(hh){y,fir[x]},fir[x]=num;}
void dfs(int u,int fa){
for(int i=fir[u],v;v=e[i].to;i=e[i].nxt)
if(v^fa) dfs(v,u),dis[u]+=dis[v]+1;
int cnt=0,sum=0;
for(int i=fir[u],v;v=e[i].to;i=e[i].nxt)
if(v^fa) a[++cnt]=(kk){v,f[v]-2*(dis[v]+1)};
sort(a+1,a+cnt+1);
for(int i=1;i<=cnt;++i) ++sum,f[u]=max(f[u],f[a[i].id]+sum),sum+=2*dis[a[i].id]+1;
if(u^1) f[u]=max(c[u],f[u]);
else f[u]=max(c[u]+2*dis[u],f[u]);
}
int main()
{
int x,y;
n=in();
for(int i=1;i<=n;++i) c[i]=in();
for(int i=1;i<n;++i)
x=in(),y=in(),
add(x,y),add(y,x);
dfs(1,0);printf("%lld\n",f[1]);
return 0;
}
P3576 [POI2014]MRO-Ant colony
裸题,处理时依关键边分成两棵树。
#include<bits/stdc++.h>
#define IL inline
#define LL long long
using namespace std;
const int N=1e6+3;
LL inf=1e9+10;
struct hh{
int to,nxt;
}e[N<<1];
int n,m,k,num,s1,s2,fir[N],deg[N];
LL ans,g[N],dp[N];
IL int in(){
char c;int f=1;
while((c=getchar())<'0'||c>'9')
if(c=='-') f=-1;
int x=c-'0';
while((c=getchar())>='0'&&c<='9')
x=x*10+c-'0';
return x*f;
}
IL void add(int x,int y){e[++num]=(hh){y,fir[x]},fir[x]=num;}
void dfs(int u,int f){
dp[u]=min(inf,dp[f]*(deg[u]-1));
if(deg[u]==1){
LL vl=dp[f]*k,vr=dp[f]*(k+1)-1;
int l=lower_bound(g+1,g+m+1,vl)-g,r=upper_bound(g+1,g+m+1,vr)-g-1;
ans+=1ll*(r-l+1)*k;
}
for(int i=fir[u],v;v=e[i].to;i=e[i].nxt)
if(v^f) dfs(v,u);
}
int main()
{
int x,y;
n=in(),m=in(),k=in();
for(int i=1;i<=m;++i) g[i]=in();
sort(g+1,g+m+1);
for(int i=1;i<n;++i){
x=in(),y=in(),
++deg[x],++deg[y],
add(x,y),add(y,x);
if(i==1) s1=x,s2=y;
}
dp[s2]=1,dfs(s1,s2);
dp[s1]=1,dfs(s2,s1);
printf("%lld\n",ans);
return 0;
}
P4099 [HEOI2013]SAO
不会啊。。。
定义 \(f_{u,i}\) 为 \(u\) 在其子树中排名为 \(i\) 的方案数。
考虑合并 \(f_{v,k}\)。
若 \(v\) 在 \(u\) 前,枚举 \(v\) 中有 \(j\) 个点在 \(u\) 前,组合数转移。
因为至少有 \(k\) 个点在 \(u\) 前,所以枚举时 \(1 \leq k \leq j\)。
若 \(v\) 在 \(i\) 后,枚举 \(v\) 中有 \(j\) 个点在 \(u\) 前,组合数转移。
因为至多有 \(k-1\) 个点在 \(u\) 前,所以枚举时 \(j+1 \leq k \leq siz_v\)。
对 \(f_v\) 求前缀和优化一维,复杂度 \(O(n^2)\)。
#include<bits/stdc++.h>
#define IL inline
#define LL long long
using namespace std;
const int N=1e3+3,p=1e9+7;
struct hh{
int to,nxt,bo;
}e[N<<1];
int n,m,num,fir[N],f[N][N],g[N],siz[N],c[N][N];
IL int in(){
char c;int f=1;
while((c=getchar())<'0'||c>'9')
if(c=='-') f=-1;
int x=c-'0';
while((c=getchar())>='0'&&c<='9')
x=x*10+c-'0';
return x*f;
}
IL void add(int x,int y,int z){e[++num]=(hh){y,fir[x],z},fir[x]=num;}
IL int mod(int x){return x>=p?x-p:x;}
void dfs(int u,int fa){
siz[u]=1,f[u][1]=1;
for(int i=fir[u],v;v=e[i].to;i=e[i].nxt)
if(v^fa){
dfs(v,u),memset(g,0,sizeof(g));
if(e[i].bo){
for(int j=1;j<=siz[u];++j)
for(int k=0;k<siz[v];++k)
g[j+k]=mod(g[j+k]+1ll*c[j+k-1][k]*c[siz[u]+siz[v]-j-k][siz[u]-j]%p*mod(f[v][siz[v]]-f[v][k]+p)%p*f[u][j]%p);
}
else{
for(int j=1;j<=siz[u];++j)
for(int k=1;k<=siz[v];++k)
g[j+k]=mod(g[j+k]+1ll*c[j+k-1][k]*c[siz[u]+siz[v]-j-k][siz[u]-j]%p*f[v][k]%p*f[u][j]%p);
}
siz[u]+=siz[v],memcpy(f[u],g,sizeof(g));
}
for(int i=1;i<=siz[u];++i) f[u][i]=mod(f[u][i]+f[u][i-1]);
}
void solve(){
int x,y;char s[10];
memset(fir,num=0,sizeof(fir)),
memset(f,0,sizeof(f));
n=in();
for(int i=1;i<n;++i)
x=in()+1,scanf("%s",s+1),y=in()+1,
add(x,y,s[1]=='<'),add(y,x,s[1]=='>');
dfs(1,0);
printf("%d\n",f[1][n]);
}
void pre(int n){
for(int i=0;i<=n;++i){
c[i][0]=1;
for(int j=1;j<=i;++j)
c[i][j]=mod(c[i-1][j]+c[i-1][j-1]);
}
}
int main()
{
int T=in();
pre(1e3);
while(T--) solve();
return 0;
}
P4253 [SCOI2015]小凸玩密室
若以 \(u\) 开始,遍历完子树后,最后的点只可能是在叶子中。
所以定义开一个数组 \(q\),表示 \(u\) 中最后的点在哪个叶子时花费的最小值。
因为是完全二叉树,所以空间为 \(O(n\log n)\)。
枚举左右子树转移即可。
最后再遍历一遍树,按 \(q\) 开一个 \(f\) 数组统计答案,注意 \(u\) 子树中最后点可能是在叶子也可能在 \(u\)。
复杂度 \(O(n\log n)\)
#include<bits/stdc++.h>
#define IL inline
#define LL long long
using namespace std;
const int N=2e5+3;
const LL inf=1e18;
struct hh{
LL dis,val;
}*q[N],*f[N];
int n,m,ls[N],rs[N],lw[N],rw[N],siz[N],val[N];
LL ans=inf;
IL int in(){
char c;int f=1;
while((c=getchar())<'0'||c>'9')
if(c=='-') f=-1;
int x=c-'0';
while((c=getchar())>='0'&&c<='9')
x=x*10+c-'0';
return x*f;
}
void dfs1(int u){
if(ls[u]) dfs1(ls[u]);
if(rs[u]) dfs1(rs[u]);
siz[u]=siz[ls[u]]+siz[rs[u]];
if(!ls[u]&&!rs[u]) siz[u]=1;
q[u]=new hh [siz[u]+1];
f[u]=new hh [siz[u]+1];
if(!ls[u]&&!rs[u]) q[u][1]=(hh){0,0};
else if(!rs[u]){
for(int i=1;i<=siz[ls[u]];++i)
q[u][i]=(hh){q[ls[u]][i].dis+lw[u],q[ls[u]][i].val+1ll*lw[u]*val[ls[u]]};
}
else{
LL lm=inf,rm=inf;
for(int i=1;i<=siz[rs[u]];++i)
rm=min(rm,q[rs[u]][i].val+(q[rs[u]][i].dis+lw[u]+rw[u])*val[ls[u]]+1ll*rw[u]*val[rs[u]]);
for(int i=1;i<=siz[ls[u]];++i) q[u][i]=(hh){q[ls[u]][i].dis+lw[u],rm+q[ls[u]][i].val};
for(int i=1;i<=siz[ls[u]];++i)
lm=min(lm,q[ls[u]][i].val+(q[ls[u]][i].dis+lw[u]+rw[u])*val[rs[u]]+1ll*lw[u]*val[ls[u]]);
for(int i=1;i<=siz[rs[u]];++i) q[u][i+siz[ls[u]]]=(hh){q[rs[u]][i].dis+rw[u],lm+q[rs[u]][i].val};
}
}
void dfs2(int u){
if(ls[u]) dfs2(ls[u]);
if(rs[u]) dfs2(rs[u]);
if(!ls[u]&&!rs[u]) f[u][0]=f[u][1]=(hh){0,0};
else if(!rs[u]){
LL Min=inf;
for(int i=0;i<=siz[ls[u]];++i)
Min=min(Min,f[ls[u]][i].val+1ll*(f[ls[u]][i].dis+lw[u])*val[u]);
f[u][0]=(hh){0,Min};
for(int i=1;i<=siz[u];++i) f[u][i]=q[u][i];
}
else{
LL lm=inf,rm=inf;f[u][0]=(hh){0,inf};
for(int i=1;i<=siz[u];++i) f[u][i]=q[u][i];
for(int i=0;i<=siz[rs[u]];++i)
rm=min(rm,f[rs[u]][i].val+1ll*(f[rs[u]][i].dis+rw[u])*val[u]+1ll*lw[u]*val[ls[u]]);
for(int i=1;i<=siz[ls[u]];++i)
if(q[ls[u]][i].val+rm<f[u][i].val)
f[u][i].val=q[ls[u]][i].val+rm;
for(int i=0;i<=siz[ls[u]];++i)
lm=min(lm,f[ls[u]][i].val+1ll*(f[ls[u]][i].dis+lw[u])*val[u]+1ll*rw[u]*val[rs[u]]);
for(int i=1;i<=siz[rs[u]];++i)
if(q[rs[u]][i].val+lm<f[u][i+siz[ls[u]]].val)
f[u][i+siz[ls[u]]].val=q[rs[u]][i].val+lm;
}
}
int main()
{
n=in();
for(int i=1;i<=n;++i) val[i]=in();
for(int i=2;i<=n;++i)
if(i&1) rs[i>>1]=i,rw[i>>1]=in();
else ls[i>>1]=i,lw[i>>1]=in();
dfs1(1),dfs2(1);
for(int i=0;i<=siz[1];++i) ans=min(ans,f[1][i].val);
printf("%lld\n",ans);
return 0;
}
P6213 「SWTR-04」Collecting Coins
被卡常惹 \(QwQ\)。。。
假设到了点 \(u\),显然只能再走 \(k_u-1\) 条边,于是转移时排序,注意儿子的子树中有 \(d\) 的话优先级是最大的。
再换根,注意先处理出能走出迷宫的点,只用它们的数据更新。
#include<bits/stdc++.h>
#define IL inline
#define LL long long
#define pb push_back
using namespace std;
const int N=1e5+3;
struct hh{
int to,nxt,w;
}e[N<<1];
int n,d,m,num,fir[N],s[N],st[N],ed[N],f[N],g[N],bo[N],ans;
struct kk{
int id,val,bo;
bool operator<(const kk &a) const{
if(bo) return 1;if(a.bo) return 0;
return val>a.val;
}
};
vector<kk>q[N];
IL int in(){
char c;int f=1;
while((c=getchar())<'0'||c>'9')
if(c=='-') f=-1;
int x=c-'0';
while((c=getchar())>='0'&&c<='9')
x=x*10+c-'0';
return x*f;
}
IL int bel(int x){return st[d]>=st[x]&&st[d]<=ed[x];}
IL void add(int x,int y,int z){e[++num]=(hh){y,fir[x],z},fir[x]=num;}
void dfs0(int u,int fa){
bo[u]=1;
for(int i=fir[u],v;v=e[i].to;i=e[i].nxt)
if(v^fa&&(u==d||s[u]>=2)) dfs0(v,u);
}
void dfs1(int u,int fa){
st[u]=++num;
for(int i=fir[u],v;v=e[i].to;i=e[i].nxt)
if(v^fa) dfs1(v,u),q[u].pb((kk){v,f[v]+e[i].w,bel(v)});
ed[u]=num;
sort(q[u].begin(),q[u].end());
for(int i=0;i<q[u].size()&&i<s[u]-1;++i) f[u]+=q[u][i].val;
}
void query(int u){
int res=0;
for(int i=0;i<q[u].size()&&i<s[u]-1;++i) res+=q[u][i].val;
ans=max(ans,res);
}
void dfs2(int u,int fa,int w){
if(!q[u].size()){if(s[u]^1&&bo[u]) ans=max(ans,g[u]+w);return;}
int v=q[u][0].id;q[u].pb((kk){0,g[u]+w,!bel(v)&&(u^d)});
sort(q[u].begin(),q[u].end());
for(int i=fir[u],v;v=e[i].to;i=e[i].nxt)
if(v^fa){
for(int j=0,k=1;j<q[u].size()&&k<s[u];++j)
if(q[u][j].id^v) g[v]+=q[u][j].val,++k;
dfs2(v,u,e[i].w);
}
if(bo[u]) query(u);
}
int main()
{
int x,y,z;
n=in(),d=in();
for(int i=1;i<n;++i)
x=in(),y=in(),z=in(),
add(x,y,z),add(y,x,z);
for(int i=1;i<=n;++i) s[i]=in();
num=0,dfs0(d,0),dfs1(1,0),dfs2(1,0,0);
printf("%d\n",ans);
return 0;
}
P6419 [COCI2014-2015#1] Kamp
裸题。
#include<bits/stdc++.h>
#define IL inline
#define LL long long
using namespace std;
const int N=5e5+3;
struct hh{
int to,nxt,w;
}e[N<<1];
int n,m,num,fir[N],b1[N],b2[N],b3[N];
LL f1[N],g1[N],f2[N],g2[N],p[N][2],ans[N];
IL int in(){
char c;int f=1;
while((c=getchar())<'0'||c>'9')
if(c=='-') f=-1;
int x=c-'0';
while((c=getchar())>='0'&&c<='9')
x=x*10+c-'0';
return x*f;
}
IL void add(int x,int y,int z){e[++num]=(hh){y,fir[x],z},fir[x]=num;}
void dfs1(int u,int fa){
b2[u]=b1[u];
for(int i=fir[u],v;v=e[i].to;i=e[i].nxt)
if(v^fa){
dfs1(v,u),b2[u]|=b2[v];
if(b2[v]){
f1[u]+=f1[v]+2*e[i].w;
if(f1[v]-g1[v]+e[i].w>p[u][0]) p[u][1]=p[u][0],p[u][0]=f1[v]-g1[v]+e[i].w;
else if(f1[v]-g1[v]+e[i].w>p[u][1]) p[u][1]=f1[v]-g1[v]+e[i].w;
}
}
g1[u]=f1[u]-p[u][0];
}
void dfs2(int u,int fa,int w){
int c=0,to=0;
if(b3[u]) ans[u]=f1[u]+f2[u]+2*w-max(p[u][0],f2[u]-g2[u]+w);
else ans[u]=g1[u];
for(int i=fir[u],v;v=e[i].to;i=e[i].nxt)
if(v^fa&&b2[v]) ++c,to=v;
if(b1[u]) c=2;if(b3[u]) ++c,to=fa;
for(int i=fir[u],v;v=e[i].to;i=e[i].nxt)
if(v^fa){
if((c>=2||to^v)&&c){
b3[v]=1;LL x=0;if(b3[u]) x=f2[u]-g2[u]+w;
f2[v]=f1[u]-f1[v]-2*b2[v]*e[i].w+f2[u]+2*b3[u]*w;
if(f1[v]-g1[v]+e[i].w==p[u][0]) g2[v]=f2[v]-max(p[u][1],x);
else g2[v]=f2[v]-max(p[u][0],x);
}
dfs2(v,u,e[i].w);
}
}
int main()
{
int x,y,z;
n=in(),m=in();
for(int i=1;i<n;++i)
x=in(),y=in(),z=in(),
add(x,y,z),add(y,x,z);
for(int i=1;i<=m;++i) b1[in()]=1;
dfs1(1,0),dfs2(1,0,0);
for(int i=1;i<=n;++i) printf("%lld\n",ans[i]);
return 0;
}
P4516 [JSOI2018]潜入行动
背包,设计好状态式子挺好推的。
#include<bits/stdc++.h>
#define IL inline
#define LL long long
#define re register
#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
using namespace std;
const int N=1e5+3,K=1e2+3,p=1e9+7;
struct hh{
int to,nxt;
}e[N<<1];
int n,m,num,siz[N],fir[N],f[N][K][2][2],g[K][2][2];
char buf[(1 << 21) + 1], *p1 = buf, *p2 = buf;
IL int in(){
char c;int f=1;
while((c=getchar())<'0'||c>'9')
if(c=='-') f=-1;
int x=c-'0';
while((c=getchar())>='0'&&c<='9')
x=x*10+c-'0';
return x*f;
}
IL void add(int x,int y){e[++num]=(hh){y,fir[x]},fir[x]=num;}
IL int mod(int x){return x>=p?x-p:x;}
void dfs(int u,int fa){
siz[u]=f[u][0][0][0]=f[u][1][1][0]=1;
for(re int i=fir[u],v;v=e[i].to;i=e[i].nxt)
if(v^fa){
dfs(v,u),siz[u]+=siz[v];
for(re int j=min(m,siz[u]);~j;--j)
g[j][0][0]=g[j][1][0]=g[j][0][1]=g[j][1][1]=0;
for(re int j=min(m,siz[u]);~j;--j)
for(re int k=min(j,siz[v]);~k;--k){
if(f[u][j-k][0][0]){
g[j][0][0]=mod(g[j][0][0]+1ll*f[u][j-k][0][0]*f[v][k][0][1]%p),
g[j][0][1]=mod(g[j][0][1]+1ll*f[u][j-k][0][0]*f[v][k][1][1]%p);
}
if(f[u][j-k][0][1]){
g[j][0][1]=mod(g[j][0][1]+1ll*f[u][j-k][0][1]*mod(f[v][k][0][1]+f[v][k][1][1])%p);
}
if(f[u][j-k][1][0]){
g[j][1][0]=mod(g[j][1][0]+1ll*f[u][j-k][1][0]*mod(f[v][k][0][0]+f[v][k][0][1])%p),
g[j][1][1]=mod(g[j][1][1]+1ll*f[u][j-k][1][0]*mod(f[v][k][1][0]+f[v][k][1][1])%p);
}
if(f[u][j-k][1][1]){
g[j][1][1]=mod(g[j][1][1]+1ll*f[u][j-k][1][1]*((1ll*f[v][k][0][0]+f[v][k][0][1]+f[v][k][1][0]+f[v][k][1][1])%p)%p);
}
}
for(re int j=min(m,siz[u]);~j;--j)
f[u][j][0][0]=g[j][0][0],
f[u][j][1][0]=g[j][1][0],
f[u][j][0][1]=g[j][0][1],
f[u][j][1][1]=g[j][1][1];
}
}
int main()
{
int x,y;
n=in(),m=in();
for(re int i=1;i<n;++i)
x=in(),y=in(),
add(x,y),add(y,x);
dfs(1,0);
printf("%d\n",mod(f[1][m][0][1]+f[1][m][1][1]));
return 0;
}
P3354 [IOI2005]Riv 河流
数组将其祖先最近的伐木场也存下来,方便计算代价。
#include<bits/stdc++.h>
#define IL inline
#define LL long long
using namespace std;
const int N=1e2+3;
struct hh{
int to,nxt,w;
}e[N<<1];
int n,m,k,num,fir[N],f[N][N][N],g[N][N][N],w[N],sta[N],dis[N];
IL int in(){
char c;int f=1;
while((c=getchar())<'0'||c>'9')
if(c=='-') f=-1;
int x=c-'0';
while((c=getchar())>='0'&&c<='9')
x=x*10+c-'0';
return x*f;
}
IL void add(int x,int y,int z){e[++num]=(hh){y,fir[x],z},fir[x]=num;}
void dfs(int u){
sta[++sta[0]]=u;
for(int i=fir[u],v;v=e[i].to;i=e[i].nxt){
dis[v]=dis[u]+e[i].w,dfs(v);
for(int j=1;j<=sta[0];++j)
for(int k=m;~k;--k){
f[u][sta[j]][k]+=f[v][sta[j]][0],
g[u][sta[j]][k]+=f[v][u][0];
for(int l=1;l<=k;++l)
f[u][sta[j]][k]=min(f[u][sta[j]][k],f[u][sta[j]][k-l]+f[v][sta[j]][l]),
g[u][sta[j]][k]=min(g[u][sta[j]][k],g[u][sta[j]][k-l]+f[v][u][l]);
}
}
for(int i=1;i<=sta[0];++i){
for(int j=1;j<=m;++j)
f[u][sta[i]][j]=min(f[u][sta[i]][j]+w[u]*(dis[u]-dis[sta[i]]),g[u][sta[i]][j-1]);
f[u][sta[i]][0]+=w[u]*(dis[u]-dis[sta[i]]);
}
--sta[0];
}
int main()
{
int x,y,z;
n=in(),m=in();
for(int i=1;i<=n;++i)
w[i]=in(),x=in(),y=in(),
add(x,i,y);
dfs(0);
cout<<f[0][0][m]<<endl;
return 0;
}