【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网址
- 1272 小希的迷宫
- 1272 小希的迷宫
- hdu 1250 大数相加并用数组储存
- 矩阵的乘法操作
- 蚂蚁爬行问题
- 蚂蚁爬行问题
- 求两个数的最大公约数【ACM基础题】
- 打印出二进制中所有1的位置
- 杭电题目---一只小蜜蜂
- HDOJ 1002 A + B Problem II (Big Numbers Addition)
- 初学ACM - 半数集(Half Set)问题 NOJ 1010 / FOJ 1207
- 初学ACM - 组合数学基础题目PKU 1833
- POJ ACM 1002
- POJ 2635 The Embarrassed Cryptographe
- POJ 3292 Semi-prime H-numbers
- POJ 2773 HAPPY 2006
- POJ 3090 Visible Lattice Points