# owo
# 题目描述
有两组玩家进行游戏,第一组有 个人,第二组有 个人,两组之间玩家两两进行比赛,共 场。
第一组玩家的能力值为 ,第二组玩家的能力值为 。其中能力值为 之间的浮点数。两个能力值为 和 的玩家进行比赛时,能力值为 的玩家获胜的概率是 。
请求出所有比赛结束后,第一组每个・玩家获胜场次的期望。()
输出共 行,每行一个实数,第 行的实数表示第一组第 个人获胜的期望场次,要求绝对误差在 以内。
# 解题思路
# 方法一
这是个发扬人类智慧的时刻,由于精度的要求在 以内,我们就能把 的实数值域转化成 的整数值域,开个桶存一下每个数的出现次数,然后对着值域搞就可以了。
# 方法二
当方法一被更高的精度要求卡住后,就轮到正解出场了。
首先我们要知道 泰勒展开
函数 在 泰勒展开的公式为:
我们转化一下问题,用所有场次减去期望输的场次,问题变成了
求得 的 阶导数为 (可以用归纳法求得)
定义 。
将 的泰勒展开式带入得
由于,我们取 使 ,则 ,在 时上述式子的取值已经小于 所有在这个题的精度下泰勒展开的前 项已经满足精度需要了。
最后就是
我们将 带入,求得每一个 的 储存起来,对于每一个询问就可以 处理了(带一个 的小常数)。
# Code
#define Aniciry Meteorshower_Y | |
#include<iostream> | |
#include<cstdio> | |
using namespace std; | |
using namespace Octane; | |
const int A = 1e4+0; | |
const int B = 2e4+0; | |
const int N = 1e6+10; | |
const int M = 2e4+10; | |
struct Meteorshower_Y{ | |
Meteorshower_Y(){ | |
freopen("owo.in", "r", stdin); | |
freopen("owo.out", "w", stdout); | |
}};Meteorshower_Y Aniciry; | |
int n, m, ta[M], tb[M]; | |
double a[N], b[N], f[M]; | |
int main() | |
{ | |
read(n, m); | |
for(int i = 1; i <= n; i += 1) | |
read(a[i]), ta[(int)(a[i]*A)] += 1; | |
for(int i = 1; i <= m; i += 1) | |
read(b[i]), tb[(int)(b[i]*A)] += 1; | |
for(int i = A; i <= B; i += 1) | |
{ | |
if(!ta[i]) continue; | |
for(int j = A; j <= B; j += 1) | |
{ | |
if(!tb[j]) continue; | |
f[i] += tb[j]*((double)i/(i+j)); | |
} | |
} | |
setpcs(4); | |
for(int i = 1; i <= n; i += 1) | |
print(f[(int)(a[i]*A)], '\n'); | |
return 0; | |
} |
# tvt
# 题目描述
个城市形成了树形结构,每条边都有长度,经过长度为 的边需要的时间为 。对于一组询问, 会在 时刻出发,从 走向 , 会在 时刻出发,从 走向 。请问两人是否会有超过 的时间在同一条边上 (顶点并不被包括在边内)。
# 解题思路
第一步我们需要判断两条路径是不是有交,无交或者交仅为一个节点就直接输出 。
判断有无交的方法:树上路径求交
然后我们需要判断他们在链(路径交的链)上的方向。
- 如果是同向,那么路径上存在一条边的长度大于两人到路径起点的时间差,那么就会有在同一条边上的情况出现,否则则无。
- 如果是相向,需要找出他们在路径上相遇的位置,可以树剖然后重链上二分,或者直接倍增向上跳。
# Code
#define Aniciry Meteorshower_Y | |
#include<algorithm> | |
#include<iostream> | |
#include<cstring> | |
#include<cstdio> | |
using namespace std; | |
const int N = 1e5+10; | |
#define int long long | |
struct Meteorshower_Y{ | |
Meteorshower_Y(){ | |
freopen("tvt.in", "r", stdin); | |
freopen("tvt.out", "w", stdout); | |
}};Meteorshower_Y Aniciry; | |
struct node{int s, t, T;}; | |
namespace HLDT | |
{ | |
struct edge{ | |
int from, to; | |
int dis, next; | |
}a[N<<1]; | |
int head[N], cnt, tim; | |
int f[N], w[N], siz[N], son[N]; | |
int id[N], aid[N], dep[N], top[N], dis[N]; | |
inline auto LCA(int x, int y) -> int; | |
inline auto dist(int x, int y) -> int; | |
inline auto dfs1(int x, int fa) -> void; | |
inline auto dfs2(int x, int tp) -> void; | |
inline auto solve(node a, node b) -> int; | |
inline auto querydis(int x, int y) -> int; | |
inline auto add_edge(int from, int to, int dis) -> void; | |
} | |
namespace Stree | |
{ | |
struct tree{ | |
int l, r, mx; | |
}tr[N<<2]; | |
inline auto build(int l, int r, int i) -> void; | |
inline auto query_max(int l, int r, int i) -> int; | |
}using namespace Stree; | |
namespace binary_lifting | |
{ | |
const int M = 1<<17; | |
int to[18][M*2], d[18][M*2]; | |
inline auto init() -> void; | |
inline auto up(int x, int dis) -> int; | |
} | |
int n, q, u, v, w; | |
inline auto _abs(int x) -> int; | |
auto main() -> signed | |
{ | |
scanf("%lld%lld", &n, &q); | |
for(int i = 1; i < n; i += 1) | |
{ | |
scanf("%lld%lld%lld", &u, &v, &w); | |
HLDT::add_edge(u, v, w); | |
HLDT::add_edge(v, u, w); | |
} | |
HLDT::dfs1(1, 0); | |
HLDT::dfs2(1, 1); | |
Stree::build(1, n, 1); | |
binary_lifting::init(); | |
node x, y; | |
for(int i = 1; i <= q; i += 1) | |
{ | |
scanf("%lld%lld%lld", &x.s, &x.t, &x.T); | |
scanf("%lld%lld%lld", &y.s, &y.t, &y.T); | |
printf(HLDT::solve(x, y)?"YES\n":"NO\n"); | |
} | |
return 0; | |
} | |
inline auto _abs(int x) -> int {return x >= 0 ? x : -x;} | |
namespace HLDT | |
{ | |
inline auto solve(node a, node b) -> int | |
{ | |
static int lca[4]; | |
lca[0] = LCA(a.s, b.s); | |
lca[1] = LCA(a.t, b.t); | |
lca[2] = LCA(a.s, b.t); | |
lca[3] = LCA(a.t, b.s); | |
sort(lca, lca+4, [](int a, int b){return dep[a] > dep[b];}); | |
if(lca[0] == lca[1]) return false; | |
int u = lca[0], v = lca[1]; | |
static int dir_1, dir_2; | |
static int dis1_u, dis1_v; | |
static int dis2_u, dis2_v; | |
dis1_u = dist(a.s, u)+a.T; | |
dis1_v = dist(a.s, v)+a.T; | |
dis2_u = dist(b.s, u)+b.T; | |
dis2_v = dist(b.s, v)+b.T; | |
dir_1 = (dis1_u < dis1_v) ? (0) : (1); | |
dir_2 = (dis2_u < dis2_v) ? (0) : (1); | |
if(dir_1 == dir_2) | |
{ | |
static int deltaT; | |
deltaT = _abs((dir_1)?(dis1_v-dis2_v):(dis1_u-dis2_u)); | |
if(querydis(u, v) > deltaT) return true; | |
else return false; | |
} | |
else if(!dir_1 and dir_2) | |
{ | |
if(dis1_v < dis2_v) return false; | |
if(dis2_u < dis1_u) return false; | |
static int p, t1, t2; | |
static int deltaT, walking; | |
t1 = dis1_u, t2 = dis2_v; | |
deltaT = t1-t2; | |
walking = dist(u, v)-_abs(deltaT); | |
if(walking&1) return true; walking /= 2; | |
p = LCA(u, v); | |
if(t1 <= t2) | |
{ | |
if(walking <= dist(v, p)) | |
{ | |
if(binary_lifting::up(v, walking)) return true; | |
else return false; | |
} | |
else | |
{ | |
if(binary_lifting::up(u, walking+_abs(deltaT))) return true; | |
else return false; | |
} | |
} | |
else | |
{ | |
if(walking <= dist(u, p)) | |
{ | |
if(binary_lifting::up(u, walking)) return true; | |
else return false; | |
} | |
else | |
{ | |
if(binary_lifting::up(v, walking+_abs(deltaT))) return true; | |
else return false; | |
} | |
} | |
} | |
else | |
{ | |
if(dis1_u <= dis2_u) return false; | |
if(dis1_v >= dis2_v) return false; | |
static int p, t1, t2; | |
static int deltaT, walking; | |
t1 = dis1_v, t2 = dis2_u; | |
deltaT = t1-t2; | |
walking = dist(u, v)-_abs(deltaT); | |
if(walking&1) return true; walking /= 2; | |
p = LCA(u, v); | |
if(t1 <= t2) | |
{ | |
if(walking <= dist(u, p)) | |
{ | |
if(binary_lifting::up(u, walking)) return true; | |
else return false; | |
} | |
else | |
{ | |
if(binary_lifting::up(v, walking+_abs(deltaT))) return true; | |
else return false; | |
} | |
} | |
else | |
{ | |
if(walking <= dist(v, p)) | |
{ | |
if(binary_lifting::up(v, walking)) return true; | |
else return false; | |
} | |
else | |
{ | |
if(binary_lifting::up(u, walking+_abs(deltaT))) return true; | |
else return false; | |
} | |
} | |
} | |
} | |
inline auto dist(int x, int y) -> int | |
{ | |
return dis[x]+dis[y]-2*dis[LCA(x, y)]; | |
} | |
inline auto LCA(int x, int y) -> int | |
{ | |
while(top[x] != top[y]) | |
{ | |
if(dep[top[x]] > dep[top[y]]) x = f[top[x]]; | |
else y = f[top[y]]; | |
} | |
return dep[x] < dep[y] ? x : y; | |
} | |
inline auto querydis(int x, int y) -> int | |
{ | |
int ans = 0; | |
while(top[x] != top[y]) | |
{ | |
if(dep[top[x]] < dep[top[y]]) swap(x, y); | |
ans = max(ans, query_max(id[top[x]], id[x], 1)); | |
x = f[top[x]]; | |
} | |
if(id[x] == id[y]) return ans; | |
if(id[x] > id[y]) swap(x, y); | |
return max(ans, query_max(id[x]+1, id[y], 1)); | |
} | |
inline auto dfs1(int x, int fa) -> void | |
{ | |
dep[x] = dep[fa]+1; | |
f[x] = fa; siz[x] = 1; | |
for(int i = head[x], y; i; i = a[i].next) | |
{ | |
if(y = a[i].to, y == fa) continue; | |
w[y] = a[i].dis; dis[y] = dis[x]+a[i].dis; | |
dfs1(y, x); siz[x] += siz[y]; | |
if(siz[y] > siz[son[x]]) son[x] = y; | |
} | |
} | |
inline auto dfs2(int x, int tp) -> void | |
{ | |
top[x] = tp; id[x] = ++tim; aid[tim] = x; | |
if(son[x]) dfs2(son[x], tp); | |
for(int i = head[x], y; i; i = a[i].next) | |
{ | |
if(y = a[i].to, y == f[x] or y == son[x]) continue; | |
dfs2(y, y); | |
} | |
} | |
inline auto add_edge(int from, int to, int dis) -> void | |
{ | |
cnt += 1; | |
a[cnt].from = from; | |
a[cnt].to = to; | |
a[cnt].dis = dis; | |
a[cnt].next = head[from]; | |
head[from] = cnt; | |
} | |
} | |
namespace Stree | |
{ | |
#define ls(i) (i<<1) | |
#define rs(i) (i<<1|1) | |
inline auto build(int l, int r, int i) -> void | |
{ | |
tr[i].l = l; | |
tr[i].r = r; | |
if(l == r) return (void)(tr[i].mx = HLDT::w[HLDT::aid[l]]); | |
int mid = (l+r)>>1; | |
build(l, mid, ls(i)); | |
build(mid+1, r, rs(i)); | |
tr[i].mx = max(tr[ls(i)].mx, tr[rs(i)].mx); | |
} | |
inline auto query_max(int l, int r, int i) -> int | |
{ | |
if(tr[i].l > r or tr[i].r < l) return 0; | |
if(tr[i].l >= l and tr[i].r <= r) return tr[i].mx; | |
return max(query_max(l, r, ls(i)), query_max(l, r, rs(i))); | |
} | |
} | |
namespace binary_lifting | |
{ | |
inline auto init() -> void | |
{ | |
memset(d, 0x3f, sizeof(d)); | |
for(int i = 2; i <= n; i += 1) | |
to[0][i] = HLDT::f[i]; | |
for(int i = 1; i <= n; i += 1) | |
d[0][i] = HLDT::w[i]; | |
for(int k = 1; k <= 17; k += 1) | |
for(int i = 1; i <= n; i += 1) | |
{ | |
to[k][i] = to[k-1][to[k-1][i]]; | |
d[k][i] = min(d[k][i], d[k-1][i]+d[k-1][to[k-1][i]]); | |
} | |
} | |
inline auto up(int x, int dis) -> int | |
{ | |
for(int i = 17; i >= 0; i -= 1) | |
if(d[i][x] <= dis) dis -= d[i][x], x = to[i][x]; | |
return dis; | |
} | |
} |
# 环
# 题目描述
对于一个长度为 的 01
串,下标从 到 。
定义两种类型的操作:
- 选择一个 ,将这个
01
序列循环右移 位,原序列的第 位移动到 位。 - 选择一个 ,满足序列第 个位置为
1
,且 第 个位置为0
,然后交换这两个位置。
给定 ,, ,请尝试构造 个长度为 的 01
序列 (下标从 开始算),该序列中恰好有 个 1
。而且要满足 , 既可以通过一次操作一变成 ,也可以通过一次操作二变成 。若无解则输出 NO
,有解则第一行输出 YES
,并在接下来 行输出你的方案 (如果解法不唯一,输出任意一种即可)。
# 解题思路
对于每个 ,记录其 1
所处下标和为 ,对于每一次操作一,相当于将 加上 ;对于每一次操作二,相当于将 加上 。
于是我们得出了以下式子:
所以得到:
显然有解的前提是 和 互质,下面我们就开始构造这个序列。
另
这样每次右移 位之后就能和下一位对上,操作一的要求这样就满足了。
对于操作二,对于 来说将 和 交换位置, 位置上的 1
就到了 的位置上, 也就变成了 操作二也满足,构造正确。
# Code
#define Aniciry Meteorshower_Y | |
#include<algorithm> | |
#include<iostream> | |
#include<cstring> | |
#include<cstdio> | |
using namespace std; | |
typedef long long ll; | |
int T, n, k, l, x, S[110]; | |
auto work() -> void; | |
auto Ex_Gcd(int a, int b, int &x, int &y) -> void; | |
int main() | |
{ | |
scanf("%d", &T); | |
while(T--) work(); | |
} | |
auto work() -> void | |
{ | |
scanf("%d%d%d", &n, &k, &l); | |
static int d, x, y; | |
if((d = __gcd(n, k)) != 1) return (void)(printf("NO\n")); | |
printf("YES\n"); | |
Ex_Gcd(k, n, x, y); | |
x -= ((x-(n-1))/n)*n; | |
for(int i = 0; i < l; i += 1) | |
{ | |
for(int j = 0; j < k; j += 1) S[(j+i)*x%n] = 1; | |
for(int j = 0; j < n; j += 1) printf("%d", S[j]); | |
printf("\n"); memset(S, 0, n*4); | |
} | |
} | |
auto Ex_Gcd(int a, int b, int &x, int &y) -> void | |
{ | |
if(!b) return (void)(x = 1, y = 0); | |
Ex_Gcd(b, a%b, y, x); | |
y -= (a/b)*x; | |
} |
# 探寻
# 题目描述
给定一棵 个点, 条边的树,边带权,节点编号为 ,其中根的编号为 ,一开始你在根上,第一次走一条边需要花与边权相同数目的钱,走过一次这条边后再走这条边就不需要花钱,如果钱不够则不能走,第一次走到一个点 可以获得 的钱,之后再到达 就不会获得钱,标记其中一个叶子为目标节点,求出到达目标节点所需的最少的初始钱数。
# 解题思路
考虑贪心做法,每次走花费小的,收益大的,但 显然 是错的,因为没有考虑走完这一步之后的情况,所以在贪心时需要考虑上子树内的情况。
这启发我们从叶子开始向上合并信息,对于每个点记录最低的启动资金 和最大收益 在保证收益为正的情况下使启动资金尽可能的少,然后每个点维护一个堆 (可并堆),当然不可并的话启发式合并也是可以的,维护子树内收益非负的联通块,向上合并到根,根的最小启动资金就是答案。
# Code
#define Aniciry Meteorshower_Y | |
#include<bits/stdc++.h> | |
#include<bits/extc++.h> | |
using namespace std; | |
using namespace __gnu_pbds; | |
typedef long long ll; | |
const ll inf = 1e18; | |
const int N = 2e5+10; | |
struct node{ ll need, gain; | |
friend bool operator<(node a, node b){ | |
return a.need < b.need; | |
} | |
friend bool operator>(node a, node b){ | |
return a.need > b.need; | |
} | |
}; | |
int n, cnt, head[N]; | |
int to[N], nxt[N]; | |
ll val[N], cost[N]; | |
__gnu_pbds::priority_queue<node,greater<node> > q[N]; | |
inline auto dfs(int x) -> void; | |
inline auto add_edge(int from, int to) -> void; | |
int main() | |
{ | |
read(n); cost[1] = inf; | |
for(int i = 2, fa, v; i <= n; i += 1) | |
{ | |
read(fa, val[i], cost[i]); | |
add_edge(fa+1, i); | |
if(val[i] == -1) val[i] = inf<<1; | |
} | |
dfs(1); print(q[1].top().need-inf); | |
return 0; | |
} | |
inline auto dfs(int x) -> void | |
{ | |
for(int i = head[x]; i; i = nxt[i]) | |
dfs(to[i]), q[x].join(q[to[i]]); | |
ll need = cost[x], gain = val[x]-cost[x]; node now; | |
while(!q[x].empty() and (gain < 0 or need >= q[x].top().need)) | |
{ | |
now = q[x].top(); q[x].pop(); | |
if(need+gain < now.need) | |
need += now.need-(need+gain); | |
gain += now.gain; | |
} | |
if(gain > 0) q[x].push({need, gain}); | |
} | |
inline auto add_edge(int from, int to) -> void | |
{ | |
cnt += 1; | |
::to[cnt] = to; | |
nxt[cnt] = head[from]; | |
head[from] = cnt; | |
} |
# GCD 和 LCM
# 题目描述
现在有 询问,每个询问给三个整数 ,,,求对于所有 的 ,, 的值。
结果对 取模。
# 解题思路
其实就是推式子
先把 的限制放到一边,然后就是莫反的经典套路
记 , , ,
如果没有 的限制,这道题就做完了,但是他有 。
将询问离线下来,按照 的从小到大排序,然后不断放宽 的限制, 每增大 ,就会多一个可以被统计到答案里的 ,所以就可以枚举 的倍数,将它的贡献统计上,因为整除分块要计算前缀和,可以用树状数组维护一下(常数小),修改一次是 的,枚举倍数是 ,整除分块算上查询是 ,共 组询问,总时间复杂度是 。
# Code
#define Aniciry Meteorshower_Y | |
#include<algorithm> | |
#include<iostream> | |
#include<cstdio> | |
using namespace std; | |
typedef long long ll; | |
const int N = 1e5+01; | |
const int mod = 1e9+7; | |
namespace BIT{ | |
long long tr[N]; | |
inline auto add(int x, int k) -> void; | |
inline auto query(int x) -> long long; | |
inline auto query(int l, int r) -> long long; | |
} | |
int T, cnt, ans[N]; | |
int p[N], mu[N], edge[N]; | |
struct node{int n, m, a, id;}q[N]; | |
inline auto add_h(int d) -> void; | |
inline auto prework(int n) -> void; | |
inline auto S(long long x) -> long long; | |
inline auto work(int n, int m) -> long long; | |
int main() | |
{ | |
scanf("%d", &T); prework(1e5); | |
for(int i = 1; i <= T; i += 1) | |
{ | |
scanf("%d%d%d", &q[i].n, &q[i].m, &q[i].a); | |
q[i].id = i; | |
} | |
sort(q+1, q+T+1, [](node a, node b){ | |
return a.a <= b.a; | |
}); | |
for(int x = 1, i = 1; x <= 1e5; x += 1) | |
{ | |
add_h(x); while(i <= T and q[i].a == x) | |
ans[q[i].id] = work(q[i].n, q[i].m), i += 1; | |
} | |
for(int i = 1; i <= T; i += 1) | |
printf("%d\n", ans[i]); | |
return 0; | |
} | |
inline auto prework(int n) -> void | |
{ | |
mu[1] = 1; | |
for(int i = 2; i <= n; i += 1) | |
{ | |
if(!edge[i]) p[++cnt] = i, mu[i] = -1; | |
for(int j = 1; j <= cnt and i*p[j] <= n; j += 1) | |
{ | |
edge[i*p[j]] = 1; | |
if(i%p[j] == 0){mu[i*p[j]] = 0; break;} | |
mu[i*p[j]] = -mu[i]; | |
} | |
} | |
} | |
inline auto work(int n, int m) -> long long | |
{ | |
int A = min(n, m); | |
long long ans = 0; | |
for(int l = 1, r; l <= A; l = r+1) | |
{ | |
r = min(n/(n/l), m/(m/l)); | |
ans += S(n/l) * S(m/l)%mod * BIT::query(l, r)%mod; | |
} | |
return (ans%mod+mod)%mod; | |
} | |
inline auto add_h(int d) -> void | |
{ | |
for(ll T = d; T <= 1e5; T += d) | |
BIT::add(T, (ll)T*(T/d)%mod*mu[T/d]); | |
} | |
inline auto S(long long x) -> long long{return (x*(x+1)>>1)%mod;} | |
namespace BIT | |
{ | |
#define lowbit(x) (x&-x) | |
inline auto add(int x, int k) -> void | |
{ | |
for(; x <= 1e5; x += lowbit(x)) | |
tr[x] += k; | |
} | |
inline auto query(int x) -> long long | |
{ | |
long long ans = 0; | |
for(; x; x -= lowbit(x)) | |
ans += tr[x]; | |
return ans%mod; | |
} | |
inline auto query(int l, int r) -> long long | |
{ | |
return (query(r)-query(l-1))%mod; | |
} | |
} |
# 平面图
# 题目描述
给一个 个点 组成的联通的 平面图,接下来有 次操作:
- 操作
-
,删去边x <-> y
,并询问删完之后有几个联通块。 - 操作
?
,询问x
和y
是否在一个联通块内。
输入第一行 ,()。
接下来 行,每行先读入 ,表示点的度数,再读入 个数,表示该点顺时针依次与哪些点相连。
接下来 行读入 , 表示一组询问。
询问强制在线, 需要异或上一次的答案 , 初始为 。
# 解题思路
考虑第一个问题,对于一个图的删边并不好维护,由于给的是平面图,我们可以将平面图的删边操作转化为对偶图的加边操作。考虑将对偶图的点建出,先不连边,每次删一条边,将这条边在对偶图中相邻的两个点(原图中的平面)连起来,若围成了一个环,这说明他们之间包住的那个平面图中的点周围的边都被切断,这时候联通块个数加一。并查集维护一下就好。
对于第二个问题,对同一个联通块染色。每次删边之后若将原联通块分割成了两个,我们就按照类似于启发式合并的思想,启发式分裂,每次将较小的联通块重新染色,这样就可以保证时间复杂度了。
# Code
#define Aniciry Meteorshower_Y | |
#include<unordered_map> | |
#include<algorithm> | |
#include<iostream> | |
#include<utility> | |
#include<cstdio> | |
#include<vector> | |
#include<map> | |
#define Pair pair<int,int> | |
using namespace std; | |
// 此处省略快读快写 | |
const int N = 4e5+10; | |
int n, q, cnt, col, cnt_edge, lstans; | |
int belong[N], fa[N*3], vis[N], cur[N]; | |
vector<int> to[N]; | |
map<Pair, int> id; | |
map<Pair, int> edgebelong; | |
inline auto _find(int x) -> int; | |
inline auto extend(vector<int> &v, int &p) -> int; | |
int main() | |
{ | |
read(n, q); | |
for(int i = 1, k; i <= n; i += 1) | |
{ | |
read(k); int x; | |
for(int j = 1; j <= k; j += 1) | |
{ | |
read(x); | |
to[i].push_back(x); | |
id[Pair(i,x)] = ++cnt_edge; | |
fa[cnt_edge] = cnt_edge; | |
} | |
} | |
for(int i = 1; i <= n; i += 1) | |
{ | |
int k = to[i].size(); | |
for(int j = 1; j < k; j += 1) | |
fa[_find(id[Pair(i,to[i][j-1])])] = fa[_find(id[Pair(to[i][j],i)])]; | |
fa[_find(id[Pair(i,to[i][k-1])])] = fa[_find(id[Pair(to[i][0],i)])]; | |
} | |
for(int i = 1; i <= n; i += 1) | |
{ | |
int k = to[i].size(); | |
for(int j = 0; j < k; j += 1) | |
edgebelong[Pair(i,to[i][j])] = _find(id[Pair(i,to[i][j])]); | |
} | |
for(int i = 1; i <= n; i += 1) | |
sort(to[i].begin(), to[i].end()); | |
for(int i = 1; i <= cnt_edge; i += 1) fa[i] = i; | |
char op; int x, y, idx, idy; cnt = 1; | |
for(int i = 1; i <= q; i += 1) | |
{ | |
read(op, x, y), x ^= lstans, y ^= lstans; | |
if(op == '-') | |
{ | |
idx = _find(edgebelong[Pair(x,y)]); | |
idy = _find(edgebelong[Pair(y,x)]); | |
to[x].erase(lower_bound(to[x].begin(), to[x].end(), y)); | |
to[y].erase(lower_bound(to[y].begin(), to[y].end(), x)); | |
if(idx == idy) | |
{ | |
int p1 = 0, p2 = 0; cnt += 1; | |
vector<int> v1; v1.push_back(x); | |
vector<int> v2; v2.push_back(y); | |
while(20051014) | |
{ | |
if(!extend(v1, p1)) {col += 1; | |
for(auto y: v1) belong[y] = col; break;} | |
if(!extend(v2, p2)) {col += 1; | |
for(auto y: v2) belong[y] = col; break;} | |
} | |
for(auto y: v1) vis[y] = cur[y] = 0; | |
for(auto y: v2) vis[y] = cur[y] = 0; | |
} | |
else fa[idx] = idy; lstans = cnt; | |
} | |
else lstans = (belong[x] == belong[y]); | |
printf("%d\n", lstans); | |
} | |
return 0; | |
} | |
inline auto _find(int x) -> int | |
{ | |
while(x != fa[x]) x = fa[x] = fa[fa[x]]; | |
return x; | |
} | |
inline auto extend(vector<int> &v, int &p) -> int | |
{ | |
for(; p < v.size(); p += 1) | |
{ | |
int x = v[p]; | |
for(int i = cur[x]; i < to[x].size(); cur[x] = ++i) | |
{ | |
int y = to[x][i]; | |
if(vis[y]) continue; | |
vis[y] = true; v.push_back(y); | |
return true; | |
} | |
} | |
return false; | |
} |