您的位置:首页 > 移动开发

【Codeforces Round 329 (Div 2) D】【LCA+并查集路径压缩】Happy Tree Party 除上两点间路径全部权值

2015-11-06 20:42 579 查看
#include<stdio.h>
#include<string.h>
#include<ctype.h>
#include<math.h>
#include<iostream>
#include<string>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre(){freopen("c://test//input.in","r",stdin);freopen("c://test//output.out","w",stdout);}
#define MS(x,y) memset(x,y,sizeof(x))
#define MC(x,y) memcpy(x,y,sizeof(x))
#define MP(x,y) make_pair(x,y)
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T> inline void gmax(T &a,T b){if(b>a)a=b;}
template <class T> inline void gmin(T &a,T b){if(b<a)a=b;}
const int N=2e5+10,M=4e5+10,Z=1e9+7,ms63=1061109567;
int n,m,id;
int x,y,o;LL z;
int lca;LL W;
int first
,w[M],nxt[M];LL c[M];
int dep
,v
,f
;LL dis
;
int b[20],fa
[20];
void ins(int x,int y,LL z)
{
++id;
w[id]=y;
c[id]=z;
nxt[id]=first[x];
first[x]=id;
}
void dfs(int x)
{
for(int z=first[x];z;z=nxt[z])
{
int y=w[z];
if(y==fa[x][0])continue;
fa[y][0]=x;
v[z>>1]=y;//记录边所对应的点
dis[y]=c[z];//记录点到父节点的距离
dep[y]=dep[x]+1;
dfs(y);
}
}
void LCAinit()
{
for(int j=1;b[j]<n;j++)
{
for(int i=1;i<=n;i++)if(~fa[i][j-1])
{
fa[i][j]=fa[fa[i][j-1]][j-1];
}
}
}
int LCA(int x,int y)
{
int i,j;
if(dep[x]<dep[y])swap(x,y);
for(i=0;b[i]<=dep[x];i++);i--;
for(j=i;dep[x]>dep[y];j--)if(dep[x]-b[j]>=dep[y])x=fa[x][j];
if(x==y)return x;//★千万不可忘记这句话★
for(j=i;fa[x][0]!=fa[y][0];j--)if(fa[x][j]!=fa[y][j])
{
x=fa[x][j];
y=fa[y][j];
}
return fa[x][0];
}
int find(int x)
{
if(f[x]==-1)return x;
f[x]=find(f[x]);
return f[x];
}
void go(int x)
{
while(1)
{
x=find(x);
if(dep[x]<=dep[lca])return;
W/=dis[x];
if(W==0)return;
x=fa[x][0];
}
}
int main()
{
//fre();
for(int i=0;i<20;i++)b[i]=1<<i;
while(~scanf("%d%d",&n,&m))
{
MS(first,0);id=1;
for(int i=1;i<n;i++)
{
scanf("%d%d%lld",&x,&y,&z);
ins(x,y,z);
ins(y,x,z);
}
MS(fa,-1);dfs(1);LCAinit();//LCA初始化
MS(f,-1);
for(int i=2;i<=n;i++)if(dis[i]==1)f[i]=fa[i][0];
for(int i=2;i<=n;i++)find(i);
for(int i=1;i<=m;i++)
{
scanf("%d",&o);
if(o==1)
{
scanf("%d%d%lld",&x,&y,&W);
lca=LCA(x,y);
go(x);
go(y);
printf("%lld\n",W);
}
else
{
scanf("%d%lld",&x,&W);
x=v[x];
dis[x]=W;
if(W==1)f[x]=fa[x][0];
}
}
}
return 0;
}
/*
【trick&&吐槽】
这道题真是运气背>_<,明明早就可以AC的,然而却到最后都没有AC。
问题出现在:
1,LCA模板竟然出了错。
我们倍增到同样深度的时候,应该先判定是否已经x==y了。
如果x==y,我们直接返回LCA=x并退出。
如果x!=y,我们才一直向上,倍增祖先不同的时候,直到祖先相同。
2,这道题,路径压缩不应该只在后来才做。初始就应该把dis[]==1的压缩掉。
否则肯定TLE>_<
3,并查集的初始化有两种写法。
其一是f[i]=i,这种我用得多了,没有任何问题。
其二是f[i]=-1,这种的话,我们就不能修改根节点的权值。

【题意】
给你一棵n(2e5)个节点的树,每条边都带有一个权值。
然后有m(2e5)次操作,操作有2种类型:
1,选择一个数字W([1,1e18]),两个节点x,y。
对于(x,y)路径上的每一条边。假设该边的边权为x,那么就使得W/=x,并输出最后的W。
2,选择一个倍数W([1,1e18]),选择一条边x,把这条边的边权改小为原来的1/W。

【类型】
LCA 并查集

【分析】
我们发现,对于所有的输出,我们使得W连续除以边权。
就算W为最大值1e18,只要路径中的边权不为1,W也最多只会除log(W)次就变为0。
于是,我们可以考虑直接暴力从x走到y,如果更新到W==0的时候就退出?
但是,这里存在一个问题,正如上面所说,如果有一大段边权都为1,那么我们时间上就TLE了。
如何解决掉这种问题呢?
如果一条边的边权为1,我们可以忽略这条边的存在,把它予以压缩合并——
呀,没错,我们发现了。这个问题可以用并查集解决!

初始,我们记录每个点的父节点,以及它到父节点的距离。
初始状态,每个节点都指向自己,代表没有路径压缩。
如果它到父节点的距离为1,那么显然,我们可以把它合并到它的父节点上。
这就代表着,这段路程被我们压缩掉了,因为就算不压缩,除也相当于没除。

对于操作2,如果我们修改一条边的边权,我们可以提前记录每条边所对应的子节点,
于是就可以把修改某条边的边权,转变为修改某个点到其父节点的距离。
如果边权被修改为1,那我们就可以做路径压缩,把这个点直接指向父节点了。

对于操作1,我们求从x到y的距离,可以先求出x到y的最近公共祖先,
然后x一直爬到最近公共祖先,y也爬到最近公共祖先,就可以更新W输出了

注意细节,这道题目就做完啦,就可以AC了。

【时间复杂度&&优化】
O(Kn+64m)

*/


附上数据制造机——

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<ctype.h>
#include<math.h>
#include<map>
#include<set>
#include<vector>
#include<queue>
#include<string>
#include<algorithm>
#include<time.h>
#include<bitset>
using namespace std;
#define MS(x,y) memset(x,y,sizeof(x))
#define MC(x,y) memcpy(x,y,sizeof(x))
#define MP(x,y) make_pair(x,y)
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T> inline void gmax(T &a,T b){if(b>a)a=b;}
template <class T> inline void gmin(T &a,T b){if(b<a)a=b;}
const int N=2e5+10,M=0,Z=1e9+7,maxint=2147483647,ms31=522133279,ms63=1061109567,ms127=2139062143;const double eps=1e-8,PI=acos(-1.0);//.0
int casenum,casei;
void fre()
{
freopen("c://test//input.in","w",stdout);
}
struct edge
{
int x,y,z;
}b
;
int f
;
int find(int x)
{
if(f[x]==x)return f[x];
f[x]=find(f[x]);
return f[x];
}
void build_tree(int n)
{
for(int i=1;i<=n;i++)f[i]=i;
int cnt=1;
while(cnt<n)
{
int x=rand()%n+1;
int y=rand()%n+1;
int fx=find(x);
int fy=find(y);
if(fx==fy)continue;
f[fy]=fx;
b[cnt].x=x;
b[cnt].y=y;
b[cnt++].z=rand()%5==0?rand()%10+1:1;
}
}
int main()
{
fre();
srand(time(NULL));
casenum=100;//printf("%d\n",casenum);
for(casei=1;casei<=casenum;casei++)
{
int n=rand()%5+2;
int m=rand()%10+1;
build_tree(n);
printf("%d %d\n",n,m);
for(int i=1;i<n;i++)printf("%d %d %d\n",b[i].x,b[i].y,b[i].z);
for(int i=1;i<=m;i++)
{
int typ=rand()%2+1;
printf("%d ",typ);
if(typ==1)
{
int x=rand()%n+1;
int y=rand()%n+1;
int z=rand()%200;
printf("%d %d %d\n",x,y,z);
}
else
{
int x=rand()%(n-1)+1;
int z=rand()%b[x].z+1;
printf("%d %d\n",x,z);
}
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  ACM ICPC LCA 并查集 CF329