[倍增][启发式合并] Jzoj P6273 欠钱
题解
- 用倍增求链上最小值,合并的时候启发式合并就可以了
代码
1 #include <cstdio> 2 #include <iostream> 3 #include <cstring> 4 #define N 100010 5 using namespace std; 6 int n,m,ans,fa[N],p[N],deep[N],f[N][20],g[N][20]; 7 int find(int x) 8 { 9 if (fa[x]==x) return x; 10 int y=fa[x]; fa[x]=find(fa[x]),deep[x]+=deep[y]; return fa[x]; 11 } 12 void calc(int x,int y) 13 { 14 if (!f[x][y-1]) calc(x,y-1); 15 if (!f[f[x][y-1]][y-1]) calc(f[x][y-1],y-1); 16 f[x][y]=f[f[x][y-1]][y-1],g[x][y]=min(g[x][y-1],g[f[x][y-1]][y-1]); 17 } 18 int solve(int x,int y) 19 { 20 int u=find(x),v=find(y); 21 if (u!=v||deep[x]<deep[y]) return 0; 22 int k=deep[x]-deep[y],r=0x7fffffff; 23 while (k) 24 { 25 if (!f[x][p[k]]) calc(x,p[k]); 26 r=min(r,g[x][p[k]]),x=f[x][p[k]],k-=1<<p[k]; 27 } 28 if (x!=y) return 0; else return r; 29 } 30 int main() 31 { 32 freopen("money.in","r",stdin),freopen("money.out","w",stdout),scanf("%d%d",&n,&m),p[0]=-1; 33 for (int i=1;i<=n;i++) fa[i]=i; 34 for (int i=1;i<=n;i++) p[i]=p[i/2]+1; 35 for (int op,a,b,c;m;m--) 36 { 37 scanf("%d%d%d",&op,&a,&b),a=(a+ans)%n+1,b=(b+ans)%n+1; 38 if (op==0) scanf("%d",&c),c=(c+ans)%n+1,fa[a]=b,deep[a]++,f[a][0]=b,g[a][0]+=c; else printf("%d\n",ans=solve(a,b)); 39 } 40 }