[倍增][启发式合并] Jzoj P6273 欠钱

Description

Input

第一行两个整数 n 和 m,表示有 n 只企鹅,m 个操作。
接下来 m 行,有两种可能的格式:
- 0 a b c:修改操作,企鹅 a 向企鹅 b 借了 c 元钱。
- 1 a b:查询操作,询问假如 a 有了 +∞ 元钱,企鹅 b 会净收入多少钱。
本题强制在线,也就是说:对于每个操作输入的变量 a, b, c(如果没有c,那就只有 a, b)
都不是实际的 a, b, c,想获得实际的 a, b, c 应当经过以下操作:

a = (a + lastans) % n + 1;
b = (b + lastans) % n + 1;
c = (c + lastans) % n + 1;

其中,lastans 是上一次询问的答案。如果没有上一次询问,lastans 为0。

Output

对每个询问操作,输出一行一个数表示答案。

Sample Input

5 9
0 1 2 1
0 0 1 2
1 0 1
1 2 4
0 2 1 1
1 2 0
0 3 1 0
1 4 2
1 3 4

Sample Output

3
2
0
1
0

Data Constraint

题解

  • 用倍增求链上最小值,合并的时候启发式合并就可以了

代码

 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 }

猜你喜欢

转载自www.cnblogs.com/Comfortable/p/11307819.html
今日推荐