JZOJ 6273. 2019.8.4【NOIP提高组A】欠钱
题目
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
题解
- 简化一下题目:
- 有一个森林(多棵树),初始状态没有连边,给出
- (0):儿子(a)往父亲(b)连一条边权为(c)的单向边,
- (1):询问(x)到(y)的路径(有向)中,边权最小是多少,若不可到达则输出(0),
- 题目强制在线。。。(离线好像容易许多)
- 据说这是一道(LCT)的裸题(但会被卡),然而并不需要,而且时间还快得多~~~
- 先考虑暴力做法,
- 用倍增来进行询问求值,每次连边时暴力更新子树内倍增数组的值,还要更新子树内每个节点的深度,
- 同时还可以记录(h[i])表示倍增数组的(f[i][j])((i)节点向上(2^j)步)中的(j)已经更新过(0-h[i])的值了,之后再更新直接从(h[i])到(log_2 n),可以优化时间,
- 这样的时间复杂度还是(O(n^2))的,超时!!!
- 我们发现,倍增数组只是对询问有用(废话,更新不就是为了询问嘛——),
- 那么可以试试询问时再来修改,
- 但先需要在连边时用带权并查集维护节点的深度,否则无法倍增,
- 然后询问时直接按普通的倍增向上跳,有遇到没有更新的就递归更新,
- 也就是看(f[i][j-1])和(f[f[i][j-1]][j-1])分别有没有值,没有就继续递归下去,有就返回更新上一层的。
- 需要读入优化。
- 这道题运用到了一种很普遍的思想:
- 不急于每次修改就把所有的需要更新的更新,而是等到它需要使用时再来更新,这样可以一定程度上节约时间。
- 并查集类似如此(每次连边时儿子指向父亲,用到每个节点时再来压缩路径)。
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 100010
int f[N][20],g[N][20],dp[N],r[N],rs[N];
int get(int v)
{
if(r[v]==v)
{
rs[v]=0;
return v;
}
int rt=get(r[v]);
rs[v]+=rs[r[v]];
r[v]=rt;
return r[v];
}
void count(int v,int i)
{
if(i==0) return;
if(f[v][i]) return;
count(v,i-1);
count(f[v][i-1],i-1);
f[v][i]=f[f[v][i-1]][i-1];
g[v][i]=min(g[v][i-1],g[f[v][i-1]][i-1]);
}
int read()
{
int t=0;
char c=getchar();
while(c<'0'||c>'9') c=getchar();
while(c>='0'&&c<='9') t=t*10+c-'0',c=getchar();
return t;
}
int main()
{
int n,Q,i,k,x,y,c,ls=0;
scanf("%d%d",&n,&Q);
for(i=1;i<=n;i++) r[i]=i;
while(Q--)
{
k=read();
if(k)
{
x=read(),y=read();
x=(x+ls)%n+1;
y=(y+ls)%n+1;
get(x);
get(y);
int ans=1e+9;
for(i=19;i>=0;i--)
{
get(x);
if(rs[x]-(1<<i)>=rs[y])
{
count(x,i);
ans=min(ans,g[x][i]);
x=f[x][i];
}
}
if(x!=y) ans=0;
ls=ans;
printf("%d
",ls);
}
else
{
x=read(),y=read(),c=read();
x=(x+ls)%n+1;
y=(y+ls)%n+1;
c=(c+ls)%n+1;
f[x][0]=y;
g[x][0]=c;
r[x]=y;
rs[x]=1;
}
}
return 0;
}