算法学习:树上差分
初步学习了树上差分,这里主要是些简单的例题。
\(Part1\). 树上点的差分:
题目链接:P3128 [USACO15DEC]最大流Max Flow
还以为是网络流
点的差分很简单,就是树剖不配线段树了,一个差分数组就够了:
复杂度\(O(nlogn+k+n)\)(大概是),可以通过本题。
\(Code\):
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
const int MAXN=500005;
int f[MAXN],top[MAXN],tot[MAXN],son[MAXN],deep[MAXN];
int rt[MAXN];
int id[MAXN],c=0;
int n,m,l,r;
struct node
{
int to,nxt;
}e[MAXN<<1];
int head[MAXN],cnt=0;
void add(int u,int v)
{
e[++cnt].to=v;
e[cnt].nxt=head[u];
head[u]=cnt;
}
int dfs1(int cur,int fa,int step)
{
deep[cur]=step;
f[cur]=fa;
tot[cur]=1;
int maxn=-1;
for(int i=head[cur];i;i=e[i].nxt)
{
int j=e[i].to;
if(j!=fa)
{
tot[cur]+=dfs1(j,cur,step+1);
if(tot[j]>maxn) maxn=tot[j],son[cur]=j;
}
}
return tot[cur];
}
void dfs2(int cur,int topf)
{
top[cur]=topf;
id[cur]=++c;
if(!son[cur]) return;
dfs2(son[cur],topf);
for(int i=head[cur];i;i=e[i].nxt)
{
int j=e[i].to;
if(!id[j]) dfs2(j,j);
}
}
void D_in_tree(int l,int r)
{
while(top[l]!=top[r])
{
if(deep[top[l]]<deep[top[r]]) swap(l,r);
rt[id[l]+1]--;
rt[id[top[l]]]++;
l=f[top[l]];
}
if(deep[l]<deep[r]) swap(l,r);
rt[id[r]]++;
rt[id[l]+1]--;
return;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<n;i++)
{
scanf("%d%d",&l,&r);
add(l,r);
add(r,l);
}
dfs1(1,1,1);
dfs2(1,1);
for(int i=1;i<=n;i++) rt[i]=0;
for(int i=1;i<=m;i++)
{
scanf("%d%d",&l,&r);
D_in_tree(l,r);
//for(int i=1;i<=n;i++) cout<<rt[i]<<" ";
//cout<<endl;
}
int last=0,ans=-1;
/*for(int i=1;i<=n;i++) cout<<id[i]<<" ";
cout<<endl;
for(int i=1;i<=n;i++) cout<<top[i]<<" ";
cout<<endl;
for(int i=1;i<=n;i++) cout<<deep[i]<<" ";
cout<<endl;
for(int i=1;i<=n;i++) cout<<rt[i]<<" ";
cout<<endl;*/
for(int i=1;i<=n;i++)
{
rt[i]=last+rt[i];
last=rt[i];
ans=max(ans,last);
}
printf("%d\n",ans);
return 0;
}
/*
6 6
1 2
3 1
1 4
3 5
6 3
2 4
2 5
5 6
1 3
5 4
6 3
*/
当然有很多的调试语句,突出惨烈性,又附了组数据。
\(Part2\).树上边的差分
题目链接:CF191C Fools and Roads
大家都用的\(lca\),而我有一个好法子:
这棵树是无根树,但并不影响答案,所以我们认为她是有根树(以1为根),这样我们发现可对边重新编号:
即边的序号为连接深度较大的节点编号,显然为\(2,3......n\).
于是差分即可:
\(Code\):
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
const int MAXN=1000005;
int f[MAXN],top[MAXN],tot[MAXN],son[MAXN],deep[MAXN];
int rt[MAXN];
int id[MAXN],c=0;
int n,m,l,r;
struct node
{
int to,nxt,ans;
node()
{
ans=-1;
}
}e[MAXN<<1];
int head[MAXN],cnt=0;
void add(int u,int v)
{
e[++cnt].to=v;
e[cnt].nxt=head[u];
head[u]=cnt;
}
int dfs1(int cur,int fa,int step)
{
deep[cur]=step;
f[cur]=fa;
tot[cur]=1;
int maxn=-1;
for(int i=head[cur];i;i=e[i].nxt)
{
int j=e[i].to;
if(j!=fa)
{
tot[cur]+=dfs1(j,cur,step+1);
if(tot[j]>maxn) maxn=tot[j],son[cur]=j;
}
}
return tot[cur];
}
void dfs2(int cur,int topf)
{
top[cur]=topf;
id[cur]=++c;
if(!son[cur]) return;
dfs2(son[cur],topf);
for(int i=head[cur];i;i=e[i].nxt)
{
int j=e[i].to;
if(!id[j]) dfs2(j,j);
}
}
void D_in_tree(int l,int r)
{
while(top[l]!=top[r])
{
if(deep[top[l]]<deep[top[r]]) swap(l,r);
rt[id[l]+1]--;
rt[id[top[l]]]++;
l=f[top[l]];
}
if(deep[l]<deep[r]) swap(l,r);
rt[id[son[r]]]++;
rt[id[l]+1]--;
return;
}
void work(int cur,int fa)
{
for(int i=head[cur];i;i=e[i].nxt)
{
int j=e[i].to;
if(j==fa) continue;
e[i].ans=rt[id[j]];
work(j,cur);
}
}
int main()
{
//freopen("data.in","r",stdin);
//freopen("baoli.out","w",stdout);
scanf("%d",&n);
if(n==0)
{
cout<<0;
return 0;
}
for(int i=1;i<n;i++)
{
scanf("%d%d",&l,&r);
add(l,r);
add(r,l);
}
dfs1(1,1,1);
dfs2(1,1);
for(int i=1;i<=n;i++) if(!son[i]) son[i]=i;
scanf("%d",&m);
for(int i=1;i<=n;i++) rt[i]=0;
for(int i=1;i<=m;i++)
{
scanf("%d%d",&l,&r);
if(l!=r) D_in_tree(l,r);
}
for(int i=1;i<=cnt;i++) e[i].ans=-1;
work(1,1);
for(int i=1;i<=cnt;i++) if(e[i].ans>=0) printf("%d ",e[i].ans);
printf("\n");
return 0;
}
似乎和\(lys\)大佬数据对拍有锅,但我也不太确定是不是对拍器炸了(但愿是)。
事实证明,空间与时间都比\(lys\)大佬的差三倍\(qwq\)。
反正难写就是了,
再不会\(Crayon\)的前提下,\(K...\)算法随机生成出了锅,鸣谢一位大佬的程序,附上:
\(Code\):
#include<bits/stdc++.h>
using namespace std;
int n,m,cnt,fa[100015];
int find(int x){
return x==fa[x]?x:fa[x]=find(fa[x]);
}
int main()
{
freopen("data.in","w",stdout);
srand((unsigned)time(NULL));
//srand(time(0));
//int n=rand()%6+1;
int n=rand()%30+2;
cout<<n<<endl;
for(int i=1;i<=n;i++)fa[i]=i;
while(cnt<n-1){
int x=rand()*rand(),y=rand()*rand();
x=x%n+1;
y=y%n+1;
int x1=find(x),y1=find(y);
if(x1!=y1) fa[x1]=y1,cnt++,cout<<x<<" "<<y<<endl;
}
int q=rand()%75+1;
cout<<q<<"\n";
for(int i=1;i<=q;i++)
{
int a=rand()%n+1,b=rand()%n+1;
cout<<a<<" "<<b<<"\n";
}
return 0;
}
这样并查集就实现了随机生成树