题目描述
南极的企鹅王国大学中生活着 n 只企鹅,作为 21 世纪的优秀大学生, 企鹅们积极响应“大众创业,万众创新”的号召,纷纷创业。但是创业需要资 金,企鹅们最近手头比较紧,只能互相借钱。
企鹅的借钱行为是有规律可循的:每只企鹅只会借一次钱,并且只会 从一只企鹅那里借钱。借钱关系中不存在环(即不存在类似“金企鹅欠银企 鹅钱,银企鹅欠铜企鹅钱,铜企鹅欠金企鹅钱”这种情况)。
企鹅的还钱行为也是有规律可循的:每只企鹅一旦新获得了一笔钱,就会立刻用这笔钱尽可能偿还自己欠的债务,直到债务偿清或用光这笔钱。它只会使用新获得的这笔钱,至于以前它有没有钱、有多少钱,与还钱行为无关。
企鹅们经常会做美梦。在一只企鹅 A 的梦里,它梦见自己创业成功,一下子获得了 +∞ + ∞ 元钱,于是(按照上文的还钱规则)它赶快把钱用来还债, 接着拿到钱的那只企鹅也赶快把钱用来还债……如此往复,直到所有获得钱的企鹅都完成了还债操作。梦醒之后,它开心地把梦的内容告诉了另外一只企鹅 B,企鹅 B 听了,也很开心,于是它问道:在你的梦里,我获得了多少钱呢?(指 B 去还债之前手里的钱,包括后来用于还债的钱和还债后 B 手里剩下的钱。)
梦毕竟是梦,对实际的欠债情况没有影响。
输入格式
第一行两个整数 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。
输出格式
对每个询问操作,输出一行一个数表示答案。
样例数据
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
output
3
2
0
1
0
数据规模与约定
数据分为以下几种:
第一种:占 10%,n≤5000 且 m≤10000;
第二种:占 20%,所有借钱事件(0 开头的操作)发生在所有询问事件(1 开头的操作)之前;
第三种:占 30%,对于一只企鹅 A,最多只有一只企鹅向 A 借钱;
第四种:占 40%,没有特殊性质,n、m 大小有一定梯度。
对于所有数据,满足:n ≤ 105 10 5 ,m ≤ 106 10 6 ,0 ≤ a, b, c ≤ n 且 a ≠ b。
时间限制: 1s 1 s
空间限制: 512MB 512 MB
一种比较显然的做法是每次暴力维护倍增数组
在数据随机的时候会被卡成zz
考虑怎么优化这个过程
启发式合并。
我们把树看成无根树,额外维护一个倍增数组表示走
2i
2
i
的了路径上的边的方向
这样就可以合并了。
复杂度
O(nlog2n)
O
(
n
l
o
g
2
n
)
#include<bits/stdc++.h>
using namespace std;
#define rep(i,j,k) for(int i = j;i <= k;++i)
#define repp(i,j,k) for(int i = j;i >= k;--i)
#define rept(i,x) for(int i = linkk[x];i;i = e[i].n)
#define P pair<int,int>
#define Pil pair<int,ll>
#define Pli pair<ll,int>
#define Pll pair<ll,ll>
#define pb push_back
#define pc putchar
#define mp make_pair
#define file(k) memset(k,0,sizeof(k))
#define ll long long
const int INF = 1e9;
namespace fastIO{
#define BUF_SIZE 100000
#define OUT_SIZE 100000
bool IOerror = 0;
inline char nc(){
static char buf[BUF_SIZE],*p1 = buf+BUF_SIZE, *pend = buf+BUF_SIZE;
if(p1 == pend){
p1 = buf; pend = buf+fread(buf, 1, BUF_SIZE, stdin);
if(pend == p1){ IOerror = 1; return -1;}
}
return *p1++;
}
inline bool blank(char ch){return ch==' '||ch=='\n'||ch=='\r'||ch=='\t';}
inline void read(int &x){
bool sign = 0; char ch = nc(); x = 0;
for(; blank(ch); ch = nc());
if(IOerror)return;
if(ch == '-') sign = 1, ch = nc();
for(; ch >= '0' && ch <= '9'; ch = nc()) x = x*10+ch-'0';
if(sign) x = -x;
}
inline void read(ll &x){
bool sign = 0; char ch = nc(); x = 0;
for(; blank(ch); ch = nc());
if(IOerror) return;
if(ch == '-') sign = 1, ch = nc();
for(; ch >= '0' && ch <= '9'; ch = nc()) x = x*10+ch-'0';
if(sign) x = -x;
}
#undef OUT_SIZE
#undef BUF_SIZE
};
using namespace fastIO;
int lastans,n,m;
int f[101000][20] , mn[101000][20] , di[101000][20];
int size[101000] , bel[101000] , dep[101000];
int linkk[101000],t;
struct node{
int n , y , v , kind;
}e[201000];
int min(int a,int b) {return a>b?b:a;}
void add(int x,int y,int z)
{
e[++t].y = y;e[t].n = linkk[x];e[t].v = z;e[t].kind = 1;linkk[x] = t;
e[++t].y = x;e[t].n = linkk[y];e[t].v = z;e[t].kind = 2;linkk[y] = t;
}
void dfs(int x,int fa,int root)
{
bel[x] = root;
dep[x] = dep[fa] + 1;
rep(i,1,17)
f[x][i] = f[f[x][i-1]][i-1],
mn[x][i] = min(mn[x][i-1],mn[f[x][i-1]][i-1]),
di[x][i] = di[x][i-1] | di[f[x][i-1]][i-1];
rept(i,x)
if(e[i].y != fa)
{
int y = e[i].y;
f[y][0] = x;
mn[y][0] = e[i].v;
di[y][0] = e[i].kind ^ 3;
dfs(y,x,root);
}
}
void insert(int a,int b,int c)
{
add(a,b,c);
int d = (size[bel[a]] > size[bel[b]]) ? 2 : 1;
if(d == 2) swap(a,b);
size[bel[b]] += size[bel[a]];
f[a][0] = b;mn[a][0] = c;di[a][0] = d;
dfs(a,b,bel[b]);
}
int query(int x,int y)
{
int d = 1;
if(dep[x] < dep[y]) swap(x,y) , d=2;
int ans = INF;
repp(i,17,0)
if(dep[x] - (1<<i) >= dep[y])
{
if(di[x][i] != d) return 0;
ans = min(ans,mn[x][i]);
x = f[x][i];
}
if(x == y) return ans;
repp(i,17,0)
if(f[x][i] != f[y][i])
{
if(di[x][i] != d || di[y][i] != (d ^ 3)) return 0;
ans = min(ans , min(mn[x][i] , mn[y][i]));
x = f[x][i];y = f[y][i];
}
if(di[x][0] != d || di[y][0] != (d^3)) return 0;
ans = min(ans,min(mn[x][0],mn[y][0]));
return ans;
}
int main()
{
freopen("money.in","r",stdin);
freopen("money.out","w",stdout);
read(n);read(m);rep(i,1,n) bel[i] = i,size[i] = 1;
rep(i,1,m)
{
int kind,a,b,c;
read(kind);read(a);read(b);
a = (a + lastans)%n+1;
b = (b + lastans)%n+1;
if(kind == 0) read(c),c = (c + lastans)%n+1,insert(a,b,c);
else printf("%d\n",lastans=query(a,b));
}
return 0;
}
还有一种比较神的做法是用vector存同一深度的点再用链表串起来,再启发式合并,能优化掉一个 log l o g