树形dp来一个Maximum Weight Subset大部分其实都是cf的题了。
看看看。考虑从dp入手后大部分的树形dp都要记录两维,那么再看看不妨记 f[ x ][ i ]表示以x为根的子树中最近的那个选择了的点深度为j。
那么明显可以从子节点转移,如何?通过枚举深度即可。
#include<bits/stdc++.h>
using namespace std;
int n,m,k,len=0,last[100001],f[1001][1001],a[10001],tmp[1001];
struct pp
{
int x,y,next;
};pp p[100001];
void ins(int x,int y)
{
int now=++len;
p[now]={x,y,last[x]};last[x]=now;
return ;
}
void dfs(int x,int fa)
{
f[x][0]=a[x];
for(int i=last[x];i!=-1;i=p[i].next)
{
int y=p[i].y;if(y==fa) continue ;dfs(y,x);
for(int j=0;j<=n;j++) tmp[j]=f[x][j];
for(int j=0;j<=n;j++)
{
for(int kk=max(k-j,0);kk<=n;kk++) tmp[min(j,kk+1)]=max(tmp[min(j,kk+1)],f[x][j]+f[y][kk]);
}
for(int j=0;j<=n;j++) f[x][j]=tmp[j];
}
return ;
}
int main()
{
memset(last,-1,sizeof(last));
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n-1;i++)
{
int x,y;scanf("%d%d",&x,&y);
ins(x,y);ins(y,x);
}
dfs(1,1);int ans=0;
for(int i=0;i<=n;i++) ans=max(ans,f[1][i]);
printf("%d",ans);
return 0;
}
1-Trees and Queries,对于一个询问,可能的结果会有三种:
a->b, a -> x -> y -> b, a -> y -> x -> b。
那么考虑如何处理明显可以通过深度+lca爆杀。
#include<bits/stdc++.h>
using namespace std;
int n,m,len=0,last[100001],db[100001][30],dep[100001];
struct pp
{
int x,y,next;
};pp p[400001];
void ins(int x,int y)
{
int now=++len;
p[now]={x,y,last[x]};last[x]=now;
return ;
}
void dfs(int x,int fa)
{
dep[x]=dep[fa]+1,db[x][0]=fa;
for(int i=1;(1<<i)<=dep[x];i++) db[x][i]=db[db[x][i-1]][i-1];
for(int i=last[x];i!=-1;i=p[i].next)
{
int y=p[i].y;if(y==fa) continue ;
dfs(y,x);
}
return ;
}
int getlca(int x,int y)
{
if(dep[x]>dep[y]) swap(x,y);
for(int i=20;i>=0;i--)
{
if(dep[y]-(1<<i)>=dep[x]) y=db[y][i];
}
if(x==y) return x;
for(int i=20;i>=0;i--)
{
if(db[x][i]!=db[y][i]) x=db[x][i],y=db[y][i];
}
return db[x][0];
}
int getdep(int x,int y)
{
return dep[x]+dep[y]-dep[getlca(x,y)]*2;
}
bool check(int x,int k)
{
if(x>k||(x%2!=k%2)) return false;
return true;
}
int main()
{
memset(last,-1,sizeof(last));
scanf("%d",&n);
for(int i=1;i<=n-1;i++)
{
int x,y;scanf("%d%d",&x,&y);
ins(x,y);ins(y,x);
}dfs(1,1);
scanf("%d",&m);while(m--)
{
int a,b,x,y,k;scanf("%d%d%d%d%d",&x,&y,&a,&b,&k);
int ab=getdep(a,b),ax=getdep(a,x),ay=getdep(a,y),bx=getdep(b,x),by=getdep(b,y),xy=getdep(x,y);
if(check(ab,k)||check(ax+by+1,k)||check(ay+bx+1,k)) printf("YES\n");
else printf("NO\n");
}
return 0;
}
Tree Queries,转化一下题面,发现每一个点其实都与它的父亲有关(又名若是要合法则父亲是必须的),那么题目可以转化成能否有一条从1到不知道哪个点的路径包含一些点,那么可以直接做了吧~好吧提一下我没想出来的点,也就是如何判断一个点是否在另一个点的子树内,怎么办呢,sb你树剖没学过嘛?
#include<bits/stdc++.h>
using namespace std;
int n,m,len=0,last[4000001],dfs_x=0,dfsx[4000001],siz[4000001],fa[4000001];
int a[4000001],dep[4000001];
struct pp
{
int x,y,next;
};pp p[4000001];
void ins(int x,int y)
{
int now=++len;
p[now]={x,y,last[x]};last[x]=now;
return ;
}
void dfs(int x,int ff)
{
dfsx[x]=++dfs_x;siz[x]=1;fa[x]=ff;dep[x]=dep[ff]+1;
for(int i=last[x];i!=-1;i=p[i].next)
{
int y=p[i].y;if(ff==y) continue ;
dfs(y,x);siz[x]+=siz[y];
}
return ;
}
bool cmp(const int &x,const int &y)
{
return dep[x]<dep[y];
}
bool check(int x,int y)
{
if(dfsx[x]<=dfsx[y]&&dfsx[x]+siz[x]>dfsx[y]) return true;
return false;
}
int main()
{
memset(last,-1,sizeof(last));
int q;scanf("%d%d",&n,&q);
for(int i=1;i<=n-1;i++)
{
int x,y;scanf("%d%d",&x,&y);
ins(x,y);ins(y,x);
}dfs(1,1);
while(q--)
{
int k;scanf("%d",&k);
for(int i=1;i<=k;i++) scanf("%d",&a[i]),a[i]=fa[a[i]];
sort(a+1,a+k+1,cmp);int pd=1;
for(int i=1;i<=k-1;i++)
{
if(!check(a[i],a[i+1])) pd=0;
}
if(pd) printf("YES\n");
else printf("NO\n");
}
return 0;
}
#include<bits/stdc++.h>
using namespace std;
int n,m,len=0,last[1000001],f[1000001],a[1000001];
struct pp
{
int x,y,next;
};pp p[1000001];
void ins(int x,int y)
{
int now=++len;
p[now]={x,y,last[x]};last[x]=now;
return ;
}
void dfs1(int x,int fa)
{
for(int i=last[x];i!=-1;i=p[i].next)
{
int y=p[i].y;if(y==fa) continue ;
dfs1(y,x);f[x]+=max(0,f[y]);
}
if(a[x]==0) f[x]--;
else f[x]++;
return ;
}
void dfs2(int x,int fa)
{
if(f[x]>0) f[x]=max(f[x],f[fa]);
else f[x]=max(f[x],f[x]+f[fa]);
for(int i=last[x];i!=-1;i=p[i].next)
{
int y=p[i].y;if(y==fa) continue ;
dfs2(y,x);
}
return ;
}
int main()
{
memset(last,-1,sizeof(last));
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n-1;i++)
{
int x,y;scanf("%d%d",&x,&y);
ins(x,y);ins(y,x);
}
dfs1(1,1);dfs2(1,1);
for(int i=1;i<=n;i++) printf("%d ",f[i]);
return 0;
}
Construct the Binary Tree有意思的构造题,不妨做做看?
//考虑构造,若是一颗二叉树,则距离最大应该是不断向下衍生的,所以链状的时候距离最长。
//那么最小的时候呢?便是满二叉树的时候,其实若是难以理解,你可以尝试从每一个点的贡献考虑
//说出了判断方法,从判断得到启发,那么考虑构造,想了两种:
//第一种首先我们可以假设整一个链,然后再向上移节点,不过实现很麻烦?
//第二种其实我们可以直接看大佬的。嗯不是我懒,主要是大佬nb
//总结下伟大的思想,化简的方式很简单,并且发现其对二进制的应用:i&(i-1)来判断其是否到下层
//其次将最左的1 2 4 8作为链使用我是非常认可的。
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m,sum,cnt=0,sumdep;
int dep[200011],fa[200011];
signed main()
{
int t;scanf("%lld",&t);
while(t--)
{
int pos ;memset(fa,0,sizeof(fa));memset(dep,0,sizeof(dep));
scanf("%lld%lld",&n,&sumdep);dep[0]=-1;cnt=0;
for(int i=1;i<=n;i++)
{
fa[i]=i>>1;dep[i]=dep[i>>1]+1;cnt+=dep[i];
if((i&(i-1))==0) pos=i;//如果是更下一层,pos记录的是最低端
}
sumdep-=cnt;
if(sumdep<0)
{
printf("NO\n");
continue ;
}
if(sumdep==0)
{
printf("YES\n");
for(int i=2;i<=n;i++) printf("%lld ",fa[i]);
printf("\n");continue ;
}
for(int i=n;i;i--)
{
if((i&(i-1))==0) continue ;
sumdep-=dep[pos]-dep[i]+1;
if(sumdep<=0)
{
while(sumdep) pos=fa[pos],sumdep++;
sumdep=0;fa[i]=pos;break;
}
fa[i]=pos,dep[i]=dep[pos]+1,pos=i;
}
if(sumdep) printf("NO\n");
else
{
printf("YES\n");
for(int i=2;i<=n;i++) printf("%lld ",fa[i]);
printf("\n");
}
}
return 0;
}
Village (Minimum),Village (Maximum)欸这两道题怎么说呢我们先做最大值看看。
//这题我们应该考虑从边的贡献入手,考虑每条边最多能被经过几次,则对于左右两个点而言
//那么应该是min(siz[x],siz[y])*2,那么考虑如何达到理论上的最大值,发现min的值最多为n/2
//也就是说找一个点使其子树的最大值不超过n/2,这不禁让人想到一个东西——重心!
//那么明显肯定要从重心入手,想维护。
//呃呃呃考虑了半天,看了大佬的题解,发现因为找到了重心,将子树按dfsx扔入队列中
//那么发现i与i+n/2是绝不在同一颗子树中的,所以直接这样维护即可,再想想证明,发现每个点都做到了最优贡献,所以这样维护是行的
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m,len=0,last[400001],maxx[400001],siz[400001],q[400001],tot=0;
int root,ans[400001],sum=0,dep[400001];
struct pp
{
int x,y,next;
};pp p[400001];
void ins(int x,int y)
{
int now=++len;
p[now]={x,y,last[x]};last[x]=now;
return ;
}
void dfs1(int x,int fa)
{
siz[x]=1;
for(int i=last[x];i!=-1;i=p[i].next)
{
int y=p[i].y;if(y==fa) continue ;
dfs1(y,x);siz[x]+=siz[y];
maxx[x]=max(maxx[x],siz[y]);
}
maxx[x]=max(maxx[x],n-siz[x]);
if(!root||maxx[root]>maxx[x]) root=x;
return ;
}
void dfs2(int x,int fa)
{
dep[x]=dep[fa]+1;q[++tot]=x;
for(int i=last[x];i!=-1;i=p[i].next)
{
int y=p[i].y;if(y==fa) continue ;
dfs2(y,x);
}
return ;
}
main()
{
memset(last,-1,sizeof(last));
scanf("%lld",&n);root=0;
for(int i=1;i<=n-1;i++)
{
int x,y;scanf("%lld%lld",&x,&y);
ins(x,y);ins(y,x);
}
dfs1(1,1);dep[root]=-1;dfs2(root,root);
for(int i=1;i<=n/2;i++)
{
int x=q[i],y=q[i+n/2];
sum+=(dep[x]+dep[y])*2;
ans[x]=y;ans[y]=x;
}
if(n%2==1)
{
int u=q[n];
int v=q[1];
int w=q[1+n/2];
sum+=dep[u]*2;
ans[u]=v;
ans[v]=w;
ans[w]=u;
// ans[q[n]]=q[n];sum+=dep[q[n]]*2;
// swap(ans[1],ans[n]);
}
printf("%lld\n",sum);
for(int i=1;i<=n;i++) printf("%lld ",ans[i]);
return 0;
}
最小值是这个:感觉这个还麻烦点,因为需要一个重要结论就是先满足儿子再满足自己。
#include<bits/stdc++.h>
using namespace std;
int n,m,len=0,last[200001],ans[200001],sum=0;
bool v[200001];
struct pp
{
int x,y,next;
};pp p[400001];
void ins(int x,int y)
{
int now=++len;
p[now]={x,y,last[x]};last[x]=now;
return ;
}
void dfs(int x,int fa)
{
for(int i=last[x];i!=-1;i=p[i].next)
{
int y=p[i].y; if(y==fa) continue ;
dfs(y,x);
if(!ans[x]&&!ans[y]) ans[x]=y,ans[y]=x;
}
return ;
}
int main()
{
memset(last,-1,sizeof(last));memset(v,false,sizeof(v));
scanf("%d",&n);
for(int i=1;i<=n-1;i++)
{
int x,y;scanf("%d%d",&x,&y);
ins(x,y);ins(y,x);
}
dfs(1,1);
for(int i=1;i<=n;i++)
{
if(ans[i])
{
sum++;
continue ;
}
int tmp=p[last[i]].y;
ans[i]=ans[tmp];
ans[tmp]=i;sum+=2;
}
printf("%d\n",sum);
for(int i=1;i<=n;i++) printf("%d ",ans[i]);
return 0;
}
P3354 [IOI2005]Riv 河流上一题人给我做麻了。这题dp还好说,不想说
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m,K,len=0,last[100001],dep[100001],f[101][101][101],g[101][101][101],wood[100001];
int q[100001],tot=0;
struct pp
{
int x,y,c,next;
};pp p[1000001];
void ins(int x,int y,int c)
{
int now=++len;
p[now]={x,y,c,last[x]};last[x]=now;
return ;
}
void dfs(int x)
{
q[++tot]=x;
for(int i=last[x];i!=-1;i=p[i].next)
{
int y=p[i].y;//if(y==fa) continue ;
dep[y]=dep[x]+p[i].c;dfs(y);
for(int j=1;j<=tot;j++)
{
for(int k=K;k>=0;k--)
{
f[x][q[j]][k]+=f[y][q[j]][0];
g[x][q[j]][k]+=f[y][x][0];
for(int kk=0;kk<=k;kk++)
{
f[x][q[j]][k]=min(f[x][q[j]][k],f[x][q[j]][k-kk]+f[y][q[j]][kk]);
g[x][q[j]][k]=min(g[x][q[j]][k],g[x][q[j]][k-kk]+f[y][x][kk]);
}
}
}
}
for(int i=1;i<=tot;i++)
{
for(int k=0;k<=K;k++)
{
if(k>=1) f[x][q[i]][k]=min(f[x][q[i]][k]+wood[x]*(dep[x]-dep[q[i]]),g[x][q[i]][k-1]);
else f[x][q[i]][k]+=wood[x]*(dep[x]-dep[q[i]]);
}
}
tot--;
return ;
}
signed main()
{
memset(last,-1,sizeof(last));
scanf("%lld%lld",&n,&K);
for(int i=1;i<=n;i++)
{
int x=i,y,c;scanf("%lld%lld%lld",&wood[i],&y,&c);
ins(y,x,c);//ins(x,y,c);
}
dfs(0);printf("%lld",f[0][0][K]);
return 0;
}
#include<bits/stdc++.h>
using namespace std;
int n,m,len=0,last[2000001];
bool v[2000001];
struct pp
{
int x,y,next;
};pp p[2000001];
struct node
{
int t,id;
};node e[2000001];
void ins(int x,int y)
{
int now=++len;
p[now]={x,y,last[x]};last[x]=now;
return ;
}
bool cmp(const node &x,const node &y)
{
return x.t<y.t;
}
int main()
{
memset(last,-1,sizeof(last));
scanf("%d%d",&n,&m);int pd=1;
for(int i=1;i<=m;i++)
{
int x,y;scanf("%d%d",&x,&y);
ins(x,y);ins(y,x);
}
for(int i=1;i<=n;i++) scanf("%d",&e[i].t),e[i].id=i;
for(int i=1;i<=n;i++)
{
memset(v,true,sizeof(v));
for(int j=last[i];j!=-1;j=p[j].next) v[e[p[j].y].t]=false ;
for(int j=1;j<e[i].t-1;j++) if(v[j]) printf("-1"),pd=-1;if(pd==-1) break;
if(!v[e[i].t]) printf("-1"),pd=-1;if(pd==-1) break;
}
if(pd==-1) return 0;sort(e+1,e+n+1,cmp);
for(int i=1;i<=n;i++) printf("%d ",e[i].id);
return 0;
}
告一段落!
还没有呢!
P5958 [POI2017]Sabotaż
//明显的,因为一开始只有一个点,那么处于叶子节点是最多黑色节点的,考虑树形dp。
//令f[i]表示的是令i不是叛徒的最小的x,再设siz[x]表示子树大小,考虑转移。
//那么令f[i]表示的是令i不是叛徒的最小的x,其实就是令f[i]成为叛徒的最大值
//那么我要是让一个点变黑色,需要的是要不就是子树的黑嘛?所以我们在(f[y],siz[x]/siz[y])取其小
//让其满足自己被染黑,还能连带上父亲一起被染。所以直接这样转移就好啦
#include<bits/stdc++.h>
using namespace std;
int n,m,k,len=0,last[500001],siz[500001];double f[500001],ans=0;
struct pp
{
int x,y,next;
};pp p[1000001];
void ins(int x,int y)
{
int now=++len;
p[now]={x,y,last[x]};last[x]=now;
return ;
}
void dfs(int x,int fa)
{
siz[x]=1;
for(int i=last[x];i!=-1;i=p[i].next)
{
int y=p[i].y;if(y==fa) continue ;
dfs(y,x);siz[x]+=siz[y];
}
if(siz[x]==1) f[x]=1.0;
for(int i=last[x];i!=-1;i=p[i].next)
{
int y=p[i].y;if(y==fa) continue ;//printf("%lf %lf %lf\n",f[x],f[y],(double)(siz[x]-1)/siz[y]);
f[x]=max(f[x],min(f[y],(double)(siz[y])/(siz[x]-1)));
}
if(siz[x]>k) ans=max(ans,f[x]);//printf("%d %d\n",x,siz[x]);
return ;
}
int main()
{
memset(last,-1,sizeof(last));
scanf("%d%d",&n,&k);
for(int i=2;i<=n;i++)
{
int x,y;scanf("%d",&x);
ins(x,i);ins(i,x);
}
dfs(1,1);printf("%.8lf",ans);
return 0;
}