被自己之前的代码丑哭了:(
给出一个 n 个结点的树,每个结点都有一个权值 a ,请你选择一些结点,使得被选结点的权值和最大,且被选结点之间不能存在父子关系,即选了父亲不能选儿子,选了儿子不能选父亲。
- #include
- using namespace std;
- #define int long long
- const int N=1000010;
- int n,tot,f[N][2],ans,head[N],leader[N];
- bool vis[N];
- struct edge{int to,next;}e[N<<1];
- int read()
- {
- int x=0,f=1;
- char ch=getchar();
- while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
- while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
- return x*f;
- }
- void add_edge(int u,int v)
- {
- e[++tot].to=v;
- e[tot].next=head[u];
- head[u]=tot;
- }
- void dfs(int u)
- {
- vis[u]=1;
- for(int i=head[u];i;i=e[i].next)
- {
- int v=e[i].to;
- if(!vis[v])
- {
- dfs(v);
- f[u][1]+=f[v][0];
- f[u][0]+=max(f[v][0],f[v][1]);
- }
- }
- }
- signed main()
- {
- n=read();
- for(int i=1;i<=n;i++) f[i][1]=read();
- for(int i=1;i
- {
- int v=read();
- int u=read();
- leader[v]=1;
- add_edge(u,v);
- }
- for(int i=1;i<=n;i++)
- {
- if(leader[i]==0)
- {
- dfs(i);
- printf("%lld",max(f[i][0],f[i][1]));
- return 0;
- }
- }
- return 0;
- }
结点覆盖
给出一棵有根树,要求选出一些结点。若选中某个结点,则它本身、它的父亲结点和儿子结点被覆盖。选出第 i 个结点需要一定的花费 ,求覆盖这棵树所需的最小花费。
- #include
- using namespace std;
- #define int long long
- const int N=100010;
- int n,a[N],tot,head[N],f[N][3];
- struct edge{int to,next;}e[N<<1];
- int read()
- {
- int x=0,f=1;
- char ch=getchar();
- while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
- while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
- return x*f;
- }
- void add_edge(int u,int v)
- {
- e[++tot].to=v;
- e[tot].next=head[u];
- head[u]=tot;
- }
- void dfs(int u,int fa)
- {
- f[u][0]=a[u];
- bool flag=0;
- int res=1e18;
- for(int i=head[u];i;i=e[i].next)
- {
- int v=e[i].to;
- if(fa==v) continue;
- dfs(v,u);
- f[u][0]+=min(f[v][0],min(f[v][1],f[v][2]));
- f[u][2]+=min(f[v][0],f[v][1]);
- if(f[v][0]
1]) flag=1; - else res=min(res,f[v][0]-f[v][1]);
- f[u][1]+=min(f[v][0],f[v][1]);
- }
- if(!flag) f[u][1]+=res;
- }
- signed main()
- {
- n=read();
- for(int i=1;i<=n;i++)
- {
- int u=read();
- a[i]=read();
- int m=read();
- for(int j=1;j<=m;j++)
- {
- int v=read();
- add_edge(u,v);
- add_edge(v,u);
- }
- }
- dfs(1,0);
- printf("%lld",min(f[1][0],f[1][1]));
- return 0;
- }
最长距离
给出一个以 1 为根的 n 个结点的树,树边有权值,求出每个结点与相距最远结点间的距离 。
dfs1先只向下找,dfs2去取向下/先向上再向下的最大值,就是要特判一下u是否本身就在父结点的最长子链中。
- #include
- using namespace std;
- #define int long long
- struct edge{int to,next,w;}e[10010];
- int n,m,ans[10010],head[10010],f[10010][3];
- void add_edge(int u,int v,int w)
- {
- e[++m].to=v;
- e[m].w=w;
- e[m].next=head[u];
- head[u]=m;
- }
- void dfs1(int u)
- {
- int maxx1,maxx2;
- maxx1=maxx2=0;
- for(int i=head[u];i;i=e[i].next)
- {
- int v=e[i].to;
- dfs1(v);
- if(f[v][0]+e[i].w>maxx1)
- {
- ans[u]=v;
- maxx2=maxx1;
- maxx1=f[v][0]+e[i].w;
- }
- else if(f[v][0]+e[i].w>maxx2) maxx2=f[v][0]+e[i].w;
- }
- f[u][0]=maxx1;
- f[u][1]=maxx2;
- }
- void dfs2(int u)
- {
- for(int i=head[u];i;i=e[i].next)
- {
- int v=e[i].to;
- if(ans[u]==v) f[v][2]=max(f[u][1],f[u][2])+e[i].w;
- else f[v][2]=max(f[u][0],f[u][2])+e[i].w;
- dfs2(v);
- }
- }
- signed main()
- {
- while(scanf("%d",&n)!=EOF)
- {
- memset(head,0,sizeof head);
- m=f[1][0]=f[1][1]=f[1][2]=0;
- for(int i=2;i<=n;i++)
- {
- int fa,w;
- scanf("%d%d",&fa,&w);
- add_edge(fa,i,w);
- f[i][0]=f[i][1]=f[i][1]=0;
- }
- dfs1(1);dfs2(1);
- for(int i=1;i<=n;i++) printf("%d\n",max(f[i][0],f[i][2]));
- }
- return 0;
- }
选课方案
在大学里,每个学生为了达到一定的学分,必须从很多课程里选择一些课程来学习,有些课程必须在某些课程之前学习,如高等数学总是在其它课程之前学习。现在有 门功课,每门课有个学分,每门课有一门或没有直接先修课(若课程 a 是课程 b 的先修课即只有学完了课程 a ,才能学习课程 b )。一个学生要从这些课程里选择 m 门课程学习,问他能获得的最大学分是多少?
有一个点:森林无疑是难搞的,但是可以假设有一个点是所有根节点的父亲,那就好办了许多。 f[i][j] 表示对于节点 i , 一共选择 j 门课所能得到的最大学分(包括他本身)。选择他的所有子节点的前提是一定要选择这个节点,所 f[i][1] 一定等于 i 节点的权值
在枚举 j 的时候需要注意:由于 f[u][j] 由 f[u][j - k] 更新得到,那么就需要保证更新的时候 f[u][j - k] 没有被更新,由于 k 是一个正整数,所以我们倒序枚举 j 即可
- #include
- using namespace std;
- int n,m,f[1010][1010],head[100010],cnt;
- struct edge{int u,v; }e[100010];
- void add(int from,int to)
- {
- e[++cnt].v=head[from];
- e[cnt].u=to;
- head[from]=cnt;
- }
- void dp(int now)
- {
- for(int i=head[now];i;i=e[i].v)
- {
- int go=e[i].u;
- dp(go);
- for(int j=m+1;j>=1;j--)
- for(int k=0;k
- f[now][j]=max(f[now][j],f[go][k]+f[now][j-k]);
- }
- }
- int main()
- {
- scanf("%d%d",&n,&m);
- for(int i=1;i<=n;i++)
- {
- int fa;
- scanf("%d%d",&fa,&f[i][1]);
- add(fa,i);
- }
- dp(0);
- printf("%d",f[0][m+1]);
- return 0;
- }
路径求和
给出一棵树,求出所有至少一个端点为叶节点的有向路径的权值和的和。
- #include
- using namespace std;
- #define int long long
- const int N=5e5+10;
- int n,m,root,tot,ans,cnt;
- int head[N],du[N],son[N],num[N];
- struct edge{int to,next,w;}e[N*2];
- int read()
- {
- int x=0,f=1;
- char ch=getchar();
- while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
- while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
- return x*f;
- }
- void add_edge(int u,int v,int w)
- {
- e[++tot].to=v;
- e[tot].w=w;
- e[tot].next=head[u];
- head[u]=tot;
- }
- void dfs(int u,int fa)
- {
- num[u]=1;
- if(du[u]==1) cnt++,son[u]=1;
- for(int i=head[u];i;i=e[i].next)
- {
- int v=e[i].to;
- if(fa==v) continue;
- dfs(v,u);
- son[u]+=son[v];
- num[u]+=num[v];
- }
- }
- void find(int u,int fa)
- {
- for(int i=head[u];i;i=e[i].next)
- {
- int v=e[i].to;
- if(fa==v) continue;
- find(v,u);
- ans+=e[i].w*(son[v]*(n-num[v])+num[v]*(cnt-son[v]));
- }
- }
- signed main()
- {
- n=read(),m=read();
- for(int i=1;i<=m;i++)
- {
- int u,v,w;
- w=read(),u=read(),v=read();
- du[u]++;du[v]++;
- add_edge(u,v,w);
- add_edge(v,u,w);
- }
- dfs(1,0);find(1,0);
- printf("%lld",ans);
- return 0;
- }
树上移动
在给定的树上:
一个人从 s 出发,求经过所有点的最短长度。
两个人从 s 出发,走的路径没有限制,求经过所有点的最短长度。
- #include
- using namespace std;
- struct edge{int to,next,w;}e[500010];
- int n,m,sum,tot,f1[100010],f2[100010],head[100010];
- int read()
- {
- int x=0,f=1;
- char ch=getchar();
- while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
- while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
- return x*f;
- }
- void add_edge(int u,int v,int w)
- {
- e[++tot].to=v;
- e[tot].w=w;
- e[tot].next=head[u];
- head[u]=tot;
- }
- void dfs(int u,int fa)
- {
- for(int i=head[u];i;i=e[i].next)
- {
- int v=e[i].to;
- if(fa==v) continue;
- dfs(v,u);
- if(f1[v]+e[i].w>f1[u])
- {
- f2[u]=f1[u];
- f1[u]=f1[v]+e[i].w;
- }
- else if(f1[v]+e[i].w>f2[u]) f2[u]=f1[v]+e[i].w;
- }
- }
- int main()
- {
- n=read(),m=read();
- for(int i=1;i
- {
- int u,v,w;
- u=read(),v=read(),w=read();
- add_edge(u,v,w);
- add_edge(v,u,w);
- sum+=w;
- }
- dfs(m,0);
- printf("%d\n",sum*2-f1[m]);
- int maxx=0;
- for(int i=1;i<=n;i++)
- {
- f1[i]+=f2[i];
- maxx=max(maxx,f1[i]);
- }
- printf("%d",sum*2-maxx);
- return 0;
- }
块的计数
给定一棵 n 个节点的树,每个点有个喜欢程度,要求选一个联通块,并且这个联通块包含最大的喜欢程度的方案数,输出的方案数对 998244353 取模。
一个点也是联通块!!!正难则反,用联通块数减去不含最大值的联通块数,递归联通块数的时候用乘法原理就好。而且不保证点权为正,初始值赋0的话只有30pts/orz
- #include
- using namespace std;
- #define int long long
- const int N=100010;
- const int mod=998244353;
- int n,tot,mx,head[N],a[N],f[N],g[N],sum,ans;
- struct edge{int to,next;}e[N<<1];
- int read()
- {
- int x=0,f=1;
- char ch=getchar();
- while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
- while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
- return x*f;
- }
- void add_edge(int u,int v)
- {
- e[++tot].to=v;
- e[tot].next=head[u];
- head[u]=tot;
- }
- void dfs(int u,int fa)
- {
- g[u]=1;
- if(a[u]==mx) f[u]=0;
- else f[u]=1;
- for(int i=head[u];i;i=e[i].next)
- {
- int v=e[i].to;
- if(fa==v) continue;
- dfs(v,u);
- f[u]=f[u]*(f[v]+1)%mod;
- g[u]=g[u]*(g[v]+1)%mod;
- }
- sum=(sum+g[u])%mod;
- ans=(ans+f[u])%mod;
- }
- signed main()
- {
- n=read();mx=-1e18;
- for(int i=1;i<=n;i++)
- {
- a[i]=read();
- mx=max(mx,a[i]);
- }
- for(int i=1;i
- {
- int u=read();
- int v=read();
- add_edge(u,v);
- add_edge(v,u);
- }
- dfs(1,0);
- printf("%lld",(sum+mod-ans)%mod);
- return 0;
- }
权值统计
给出一个 n 个结点的无根树以及每个结点的权值,求出树的每一条路径的权值积的和,单独的一个结点也算作一条路径。
- #include
- using namespace std;
- #define int long long
- const int mod=10086;
- int n,ans,a[100010],f[100010],head[100010],tot;
- struct edge{int to,next;}e[500010];
- void add_edge(int u,int v)
- {
- e[++tot].to=v;
- e[tot].next=head[u];
- head[u]=tot;
- }
- void dfs(int u,int fa)
- {
- int sum,tot;
- sum=tot=0;
- for(int i=head[u];i;i=e[i].next)
- {
- int v=e[i].to;
- if(fa==v) continue;
- dfs(v,u);
- sum+=f[v];
- tot+=f[v]*f[v];
- }
- f[u]=(sum+1)*a[u]%mod;
- ans+=f[u]+(sum*sum-tot)*a[u]/2%mod;
- ans%=mod;
- }
- signed main()
- {
- scanf("%lld",&n);
- for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
- for(int i=1;i< n;i++)
- {
- int u,v;
- scanf("%lld%lld",&u,&v);
- add_edge(u,v);
- add_edge(v,u);
- }
- dfs(1,0);
- printf("%lld",ans);
- return 0;
- }
树的合并
给定两棵树,则总共有 n*m 种方案把这两棵树通过加一条边连成一棵树,那这 n*m 棵树的直径大小之和是多少呢?树的直径指的是树上的最长简单路径。
-
相关阅读:
测试进阶知识之零日攻击的发现和防御
VRRP虚拟路由器冗余协议
包载信使mRNA的多西环素纳米脂质体|雷公藤红素纳米脂质体RNA核糖核酸(实验原理)
java计算机毕业设计风情旅游网站源码+mysql数据库+系统+lw文档+部署
搭建一个通用监控告警平台,架构上需要有哪些设计
Workfine新手入门:分级预览
第10章_索引优化与查询优化
【ViT 微调时关于position embedding如何插值(interpolate)的详解】
Hexagon_V65_Programmers_Reference_Manual(41)
快速排序 sort
-
原文地址:https://blog.csdn.net/m0_60137414/article/details/126587686