线段树分治
在OIwiki上的称呼是线段树与离线询问。
C. 地理课
乘除法可以直接在原有答案上操作,其他的就比较板子了。
code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e5 + 3;
const int mod = 1e9 + 7;
typedef pair<int, int> pii;
map<pii, int> mp;
int ans[maxn], n, m, top;
ll inv[maxn];
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while(ch < '0' || ch > '9')
{
if(ch == '-')
{
f = -1;
}
ch = getchar();
}
while(ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch^48);
ch = getchar();
}
return x * f;
}
ll qpow(ll a, ll b)
{
ll ans = 1;
while(b)
{
if(b & 1) ans = ans * a % mod;
a = a * a % mod;
b >>= 1;
}
return ans;
}
struct stac
{
int s, f1, f2, id;//把f1合并到f2
}rem[maxn<<3];
struct SET
{
int f[maxn], size[maxn];
void pre()
{
for(int i=1; i<=n; i++) f[i] = i;
for(int i=1; i<=n; i++) size[i] = 1;
}
int fa(int x)
{
return f[x] == x ? x : fa(f[x]);
}
}s;
struct op
{
int op, x, y, r;
}o[maxn];
struct Tree
{
vector<int> v[maxn<<2];
void modify(int x, int l, int r, int L, int R, int now)
{
if(L <= l && r <= R)
{
v[x].push_back(now);
return;
}
int mid = (l + r) >> 1;
if(L <= mid) modify(x<<1, l, mid, L, R, now);
if(R > mid) modify(x<<1|1, mid+1, r, L, R, now);
}
ll nans = 1;
void LINK(int x)
{
for(int i : v[x])
{
int u = o[i].x, v = o[i].y;
u = s.fa(u), v = s.fa(v);
if(u == v) continue;
if(s.size[u] < s.size[v]) swap(u, v);
nans = nans * inv[s.size[u]] % mod * inv[s.size[v]] % mod;
rem[++top].f1 = v;
rem[top].f2 = u;
rem[top].s = s.size[v];
rem[top].id = x;
s.size[u] += s.size[v];
s.size[v] = 0;
nans = nans * s.size[u] % mod;
s.f[v] = u;
}
}
void CUT(int x)
{
while(rem[top].id == x)
{
stac &o = rem[top];
if(o.id != x) return;
s.f[o.f1] = o.f1;
s.f[o.f2] = o.f2;
nans = nans * inv[s.size[o.f2]] % mod;
s.size[o.f2] -= o.s;
s.size[o.f1] = o.s;
nans = nans * s.size[o.f1] % mod * s.size[o.f2] % mod;
--top;
}
}
void work(int x, int l, int r)
{
LINK(x);
if(l == r)
{
ans[l] = nans;
CUT(x);
return;
}
int mid = (l + r) >> 1;
work(x<<1, l, mid);
work(x<<1|1, mid+1, r);
CUT(x);
}
}t;
int main()
{
n = read(); m = read();
inv[1] = 1;
for(int i=2; i<=n; i++)
{
inv[i] = (mod-mod/i)*inv[mod%i]%mod;
}
s.pre();
for(int i=1; i<=m; i++)
{
o[i].op = read();
o[i].x = read();
o[i].y = read();
if(o[i].x > o[i].y) swap(o[i].x, o[i].y);
if(o[i].op == 2)
{
o[mp[pii(o[i].x, o[i].y)]].r = i - 1;
}
else mp[pii(o[i].x, o[i].y)] = i;
}
for(int i=1; i<=m; i++)
{
if(o[i].op == 1) t.modify(1, 1, m, i, o[i].r ? o[i].r : m, i);
}
t.work(1, 1, m);
for(int i=1; i<=m; i++) printf("%d\n", ans[i]);
return 0;
}
P5787 二分图 /【模板】线段树分治
用可撤销的东西判断二分图?那就是拓展域并查集!
code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 10101010;
const int mod = 1e9 + 7;
int n, m, k, f[maxn], siz[maxn], top;
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while(ch < '0' || ch > '9')
{
if(ch == '-')
{
f = -1;
}
ch = getchar();
}
while(ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch^48);
ch = getchar();
}
return x * f;
}
struct E {int x, y;}e[maxn];
struct Stack
{
int x, y, add;
}st[maxn];
int fa(int x) {return f[x] == x ? x : fa(f[x]);}
void merge(int x, int y)
{
x = fa(x), y = fa(y);
if(siz[x] > siz[y]) swap(x, y);
st[++top] = (Stack){x, y, siz[x] == siz[y]};
f[x] = y;
if(siz[x] == siz[y]) siz[y]++;
}
struct seg
{
vector<int> t[maxn];
void update(int x, int l, int r, int L, int R, int id)
{
if(L <= l && r <= R)
{
t[x].push_back(id);
return;
}
int mid = (l + r) >> 1;
if(L <= mid) update(x<<1, l, mid, L, R, id);
if(R > mid) update(x<<1|1, mid+1, r, L, R, id);
}
void solve(int x, int l, int r)
{
int ans = 1;
int lasttop = top;
for(int i : t[x])
{
int u = fa(e[i].x), v = fa(e[i].y);
if(u == v)
{
for(int k=l; k<=r; k++) printf("No\n");
ans = 0; break;
}
merge(e[i].x, e[i].y+n);
merge(e[i].y, e[i].x+n);
}
if(ans)
{
if(l == r) printf("Yes\n");
else
{
int mid = (l + r) >> 1;
solve(x<<1, l, mid);
solve(x<<1|1, mid+1, r);
}
}
//撤销操作不仅可以记录树的编号即点的来源,还可以记录top的初值
while(top > lasttop)
{
siz[f[st[top].x]] -= st[top].add;
f[st[top].x] = st[top].x;
top--;
}
return;
}
}t;
int main()
{
n = read(); m = read(); k = read();
for(int i=1; i<=m; i++)
{
e[i].x = read(); e[i].y = read();
int l = read()+1, r = read();//防止下标为0,本来r应该-1的
t.update(1, 1, k, l, r, i);
}
for(int i=1; i<=2*n; i++) f[i] = i, siz[i] = 1;
t.solve(1, 1, k);
return 0;
}
C. No Rest for the Wicked
https://www.cnblogs.com/Catherine2006/p/16785351.html CSP-S 模拟18
code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 4e5 + 5;
const int inf = 1e9 + 7;
int n, m, ls[maxn+maxn], f[maxn], siz[maxn], val[maxn], ans[maxn];
bool vis[maxn];
vector<int> sol[maxn+maxn];
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while(ch < '0' || ch > '9')
{
if(ch == '-')
{
f = -1;
}
ch = getchar();
}
while(ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch^48);
ch = getchar();
}
return x * f;
}
int fa(int x) {return f[x] == x ? x : fa(f[x]);}
struct edge
{
int u, v;
}e[maxn];
struct city//为什么国家变成了城市。。
{
int c, t, s, id;
}d[maxn];
struct sta
{
bool dir;
int fx, x, valfx, tim;
}st[maxn];
int top;
void add_edge(int tim, int id, bool dir)
{
if(dir)
{
int u = e[id].u, v = e[id].v;
u = fa(u), v = fa(v);
if(val[u] < ans[v])
{
++top; st[top].tim = tim;
st[top].dir = dir;
st[top].x = st[top].fx = u;
st[top].valfx = val[u];
val[u] = ans[v];
}
}
else
{
int u = e[id].u, v = e[id].v;
u = fa(u), v = fa(v);
if(u == v) return;
if(siz[u] < siz[v]) swap(u, v);
++top;
st[top].x = v;
st[top].fx = u;
st[top].valfx = val[u];
st[top].dir = dir;
st[top].tim = tim;
f[v] = u;
val[u] = max(val[u], val[v]);
siz[u] += siz[v];
}
}
void del(int tim)
{
while(top && st[top].tim == tim)
{
if(st[top].dir)
{
val[st[top].x] = st[top].valfx;
}
else
{
int u = st[top].fx, v = st[top].x;
siz[u] -= siz[v];
f[v] = v;
val[u] = st[top].valfx;
}
--top;
}
}
struct seg
{
struct node
{
vector<int> dir, nir;
}t[maxn<<2];
void insert(int x, int l, int r, int L, int R, int id, bool dir)
{
if(L <= l && r <= R)
{
if(dir) t[x].dir.push_back(id);
else t[x].nir.push_back(id);
return;
}
int mid = (l + r) >> 1;
if(L <= mid) insert(x<<1, l, mid, L, R, id, dir);
if(R > mid) insert(x<<1|1, mid+1, r, L, R, id, dir);
}
void solve(int x, int l, int r)
{
for(int v : t[x].dir) add_edge(x, v, 1);
for(int v : t[x].nir) add_edge(x, v, 0);
if(l == r)
{
for(int v : sol[l]) ans[v] = max(ans[v], val[fa(v)]);
del(x);
return;
}
int mid = (l + r) >> 1;
solve(x<<1|1, mid+1, r);
solve(x<<1, l, mid);
del(x);
}
}t;
int main()
{
n = read(); m = read();
for(int i=1; i<=n; i++) d[i].c = read(), d[i].t = read(), d[i].s = read();
for(int i=1; i<=m; i++) e[i].u = read(), e[i].v = read();
int cnt = 0;
for(int i=1; i<=n; i++) ls[++cnt] = d[i].c, ls[++cnt] = d[i].t;
sort(ls+1, ls+cnt+1); cnt = unique(ls+1, ls+cnt+1)-ls-1;
for(int i=1; i<=n; i++) d[i].c = lower_bound(ls+1, ls+cnt+1, d[i].c)-ls;
for(int i=1; i<=n; i++) d[i].t = lower_bound(ls+1, ls+cnt+1, d[i].t)-ls;
for(int i=1; i<=n; i++) sol[d[i].c].push_back(i);
for(int i=1; i<=m; i++)
{
int &u = e[i].u, &v = e[i].v;
if(d[u].c > d[v].c) swap(u, v);
if(d[v].c <= min(d[v].t, d[u].t)) t.insert(1, 1, cnt, d[v].c, min(d[v].t, d[u].t), i, 0);
if(d[u].c <= min(d[v].c-1, d[u].t)) t.insert(1, 1, cnt, d[u].c, min(d[v].c-1, d[u].t), i, 1);
}
for(int i=1; i<=n; i++) f[i] = i, siz[i] = 1, val[i] = d[i].s, ans[i] = d[i].s;
t.solve(1, 1, cnt);
for(int i=1; i<=n; i++) printf("%d ", ans[i]);
return 0;
}
%%%Chen_jr
P5227 [AHOI2013]连通图
我本来以为把边放到树上的时候,可以每次删掉当前“时刻”把前后直接全都覆盖上,但是集合没有保证每条边只出现一次,它可以继续被删掉,所以不能在输入时加边。
还有就是我本来想循环一遍n判断它们所属的fa是不是同一个来判断连通图,事实上联通块的大小就是现成的联通依据!!
code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 5;
const int inf = 1e9 + 7;
int ans[maxn], f[maxn], n, m, k, siz[maxn];
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while(ch < '0' || ch > '9')
{
if(ch == '-')
{
f = -1;
}
ch = getchar();
}
while(ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch^48);
ch = getchar();
}
return x * f;
}
struct edge
{
int u, v;
}e[maxn<<1];
vector<int> pos[maxn<<1];
vector<int> t[maxn<<2];
void insert(int x, int l, int r, int L, int R, int id)
{
if(L <= l && r <= R)
{
t[x].push_back(id); return;
}
int mid = (l + r) >> 1;
if(L <= mid) insert(x<<1, l, mid, L, R, id);
if(R > mid) insert(x<<1|1, mid+1, r, L, R, id);
}
int fa(int x)
{
while(f[x] != x) x = f[x];
return x;
}
struct Stack
{
int tim, x, fx;
}st[maxn<<3];
int top;
bool add_edge(int x, bool flag)
{
for(int i : t[x])
{
int u = e[i].u, v = e[i].v;
u = fa(u), v = fa(v);
if(u == v) continue;
if(siz[u] < siz[v]) swap(u, v);
top++;
st[top].x = v;
st[top].fx = u;
st[top].tim = x;
f[v] = u;
siz[u] += siz[v];
if(siz[u] == n) flag = true;
}
return flag;
}
void del(int x)
{
while(top && st[top].tim == x)
{
int u = st[top].fx, v = st[top].x;
f[v] = v;
siz[u] -= siz[v];
top--;
}
}
void solve(int x, int l, int r, bool flag)
{
flag = add_edge(x, flag);
if(l == r)
{
if(flag) printf("Connected\n");
else printf("Disconnected\n");
del(x);
return;
}
int mid = (l + r) >> 1;
solve(x<<1, l, mid, flag);
solve(x<<1|1, mid+1, r, flag);
del(x);
}
int main()
{
n = read(); m = read();
for(int i=1; i<=m; i++)
{
e[i].u = read(); e[i].v = read();
}
k = read();
for(int i=1; i<=m; i++)
{
pos[i].push_back(0);
}
for(int i=1; i<=k; i++)
{
for(int c=read(); c; c--)
{
pos[read()].push_back(i);
}
}
for(int i=1; i<=m; i++)
{
pos[i].push_back(k+1);
}
for(int i=1; i<=m; i++)
{
for(int j=1; j<(int)pos[i].size(); j++)
{
if(pos[i][j-1]+1 <= pos[i][j]-1)
{
insert(1, 1, k, pos[i][j-1]+1, pos[i][j]-1, i);
}
}
}
for(int i=1; i<=n; i++) f[i] = i, siz[i] = 1;
solve(1, 1, k, false);
return 0;
}
P4585 [FJOI2015]火星商店问题
我怀疑我没看懂它的日期是怎么划分的是题面的问题!!每个0是新的一天,我本来以为一串0和一串1一组然后模半天样例模不出来。。
这题刷新了我对线段树分治的的认知,我本来以为它只是个动态图连通性和可撤销按秩合并并查集,结果这个题既没有图也没有撤销。。Trie树好像不能撤销,解决方案居然是新建一个。
关于随时间消失的变量,我本来一直以为是物品,可是d又不是定值,鹤完才知道消失的是问题。找到每一个查询对应的时间范围(对于这个询问来说合法的加入时间)在线段树上的区间,如果一个区间被这个问题完全包含就把问题的编号放进去。如果这个点上有这个询问的编号,这个点内所有的物品的时间都满足条件。
查询时的最后两个变量放的是在这个区间内新加入的物品的数量,把所有物品先按位置排序,在线段树上依据时间把物品再次分组,原来按位置排的顺序没有被打乱。每一个节点在刚刚被找到的时候这里面的物品位置是有序的,这样就可以二分找到边界。现在位置满足了条件。
关于区间异或最大值,方案是建一个可持久化01Trie。
code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 3;
const int mod = 1e9 + 7;
int n, m, cnt1, cnt2, tot, top, rt[maxn], ans[maxn], st[maxn];
int ch[maxn*20][2], sz[maxn*20];
vector<int> a[maxn<<2];
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while(ch < '0' || ch > '9')
{
if(ch == '-')
{
f = -1;
}
ch = getchar();
}
while(ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch^48);
ch = getchar();
}
return x * f;
}
void insert(int &x, int u, int w)
{
int now; now = x = ++tot;
for(int i=17; i>=0; i--)
{
bool d = w & (1<<i);
ch[now][d^1] = ch[u][d^1]; ch[now][d] = ++tot;
now = ch[now][d]; u = ch[u][d];
sz[now] = sz[u] + 1;
}
}
int query(int l, int r, int w)
{
int res = 0;
for(int i=17; i>=0; i--)
{
bool d = w & (1<<i);
if(sz[ch[r][d^1]]-sz[ch[l][d^1]]>0)
{
l = ch[l][d^1], r = ch[r][d^1]; res += (1<<i);
}
else l = ch[l][d], r = ch[r][d];
}
return res;
}
struct buy
{
int s, v, t;
bool operator < (const buy &T) const
{
return s < T.s;
}
}q[maxn], t1[maxn], t2[maxn];
struct guest
{
int l, r, L, R, x;
}p[maxn];
void update(int x, int l, int r, int L, int R, int id)
{
if(L > R || r < L || l > R) return;
if(L <= l && r <= R) {a[x].push_back(id); return;}
int mid = (l + r) >> 1;
update(x<<1, l, mid, L, R, id); update(x<<1|1, mid+1, r, L, R, id);
}
void calc(int x, int L, int R)
{
top = tot = 0;
for(int i=L; i<=R; i++)
{
st[++top] = q[i].s;
insert(rt[top], rt[top-1], q[i].v);
}
for(int i=0,sz=a[x].size(); i<sz; i++)
{
int k = a[x][i];
int l = upper_bound(st+1, st+1+top, p[k].l-1)-st-1;
int r = upper_bound(st+1, st+1+top, p[k].r)-st-1;
ans[k] = max(ans[k], query(rt[l], rt[r], p[k].x));
}
}
void divide(int x, int l, int r, int L, int R)
{
if(L > R) return;
int cn1 = 0, cn2 = 0;
calc(x, L, R);
if(l == r) return;
int mid = (l + r) >> 1;
for(int i=L; i<=R; i++)
{
if(q[i].t <= mid) t1[++cn1] = q[i];
else t2[++cn2] = q[i];
}
for(int i=1; i<=cn1; i++) q[i+L-1] = t1[i];
for(int i=1; i<=cn2; i++) q[i+L-1+cn1] = t2[i];
divide(x<<1, l, mid, L, L+cn1-1);
divide(x<<1|1, mid+1, r, L+cn1, R);
}
int main()
{
n = read(); m = read();
for(int i=1; i<=n; i++) insert(rt[i], rt[i-1], read());
for(int i=1; i<=m; i++)
{
int tp = read();
if(!tp) {int s = read(), v = read(); q[++cnt1] = (buy){s, v, cnt1};}
else
{
int l = read(), r = read(), x = read(), d = read();
ans[++cnt2] = query(rt[l-1], rt[r], x);
p[cnt2] = (guest){l, r, max(1, cnt1-d+1), cnt1, x};
}
}
for(int i=1; i<=cnt2; i++) update(1, 1, cnt1, p[i].L, p[i].R, i);
sort(q+1, q+1+cnt1);
divide(1, 1, cnt1, 1, cnt1);
for(int i=1; i<=cnt2; i++) printf("%d\n", ans[i]);
return 0;
}
P3733 [HAOI2017]八纵八横
我到现在才知道:原来线段树的区间下标可以从0开始!!
关于查询异或和最大的环,可以找到所有的环把它们插入线性基,找环的方法是先建一棵生成树,记录下来所有的非树边并按时间放到线段树上。建设方案不会拆掉已有的高速公路,只可能改变计划,所以生成树始终不变,图始终是连通图。
为了不进行线性基的删除,把当前线性基的状态直接传入函数,这样不需要del()直接return就是回溯。
code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1200;
const int inf = 1e7;
#define bs bitset<1005>
int n, m, P, maxbit, tot, pos[maxn], tim;
bs dis[maxn];
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while(ch < '0' || ch > '9')
{
if(ch == '-')
{
f = -1;
}
ch = getchar();
}
while(ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch^48);
ch = getchar();
}
return x * f;
}
struct line
{
bs p[maxn];
void insert(bs x)
{
for(int i=maxbit; i>=0; i--)
{
if(!x[i]) continue;
if(!p[i].any()) {p[i] = x; break;}
else x ^= p[i];
}
}
bs query()
{
bs tmp;
tmp.reset();
for(int i=maxbit; i>=0; i--) if(!tmp[i]) tmp^=p[i];
return tmp;
}
}Ba;
void print(bs res)
{
bool flag = 0;
for(int i=maxbit; i>=0; i--)
{
if(!flag && res[i]) flag = 1;
if(flag) putchar(res[i]+'0');
}
if(!flag) putchar('0');
putchar('\n');
}
struct bcj
{
int f[maxn];
void init()
{
for(int i=1; i<=n; i++) f[i] = i;
}
int fa(int x)
{
while(f[x] != x) x = f[x] = f[f[x]];
return x;
}
void merge(int x, int y)
{
x = fa(x); y = fa(y);
f[x] = y;
}
}A;
struct node
{
int next, to;
bs w;
}a[maxn<<1];
int head[maxn], len;
void add(int x, int y, bs w)
{
a[++len].to = y; a[len].next = head[x]; a[len].w = w;
head[x] = len;
}
void dfs(int u, int fa)
{
for(int i=head[u]; i; i=a[i].next)
{
int v = a[i].to;
if(v == fa) continue;
dis[v] = dis[u]^a[i].w;
dfs(v, u);
}
}
struct Edge
{
int u, v, l, r;
bs w;
}E[maxn<<1];
struct seg
{
vector<int> t[maxn<<2];
bs ans[maxn];
void modify(int x, int l, int r, int L, int R, int id)
{
if(L <= l && r <= R)
{
t[x].push_back(id);
return;
}
int mid = (l + r) >> 1;
if(L <= mid) modify(x<<1, l, mid, L, R, id);
if(R > mid) modify(x<<1|1, mid+1, r, L, R, id);
}
void query(int x, int l, int r, line tmp)
{
for(int i : t[x])
{
tmp.insert(dis[E[i].u]^dis[E[i].v]^E[i].w);
}
if(l == r) {ans[l] = tmp.query(); return;}
int mid = (l + r) >> 1;
query(x<<1, l, mid, tmp); query(x<<1|1, mid+1, r, tmp);
}
}T;
int main()
{
n = read(); m = read(); P = read();
A.init();
for(int i=1; i<=m; i++)
{
int x = read(), y = read(); string st;
cin >> st;
maxbit = max(maxbit, (int)st.size());
if(A.fa(x) != A.fa(y)) A.merge(x, y), add(x, y, bs(st)), add(y, x, bs(st));
else E[++tot] = {x, y, 0, P, bs(st)};
}
dfs(1, 0);
for(int i=1; i<=P; i++)
{
string opt, st;
cin >> opt;
if(opt[0] == 'A')
{
int x = read(), y = read(); cin >> st;
maxbit = max(maxbit, (int)st.size());
E[++tot] = {x, y, i, P, bs(st)}; pos[++tim] = tot;
}
else if(opt[1] == 'a')
{
int x = read();
E[pos[x]].r = i-1;
}
else
{
int x = read(); cin >> st;
maxbit = max(maxbit, (int)st.size());
E[pos[x]].r = i-1;
E[++tot] = E[pos[x]]; E[tot].w = bs(st);
E[tot].l = i; E[tot].r = P;
pos[x] = tot;
}
}
for(int i=1; i<=tot; i++)
{
T.modify(1, 0, P, E[i].l, E[i].r, i);
}
T.query(1, 0, P, Ba);
print(T.ans[0]);
for(int i=1; i<=P; i++) print(T.ans[i]);
return 0;
}
CF576E Painting Edges
一条边一旦拥有颜色就不会再变回无色,所以加入的边都没有撤销,只是从上一次修改之后到下一次修改之前加入了很多次,颜色变化可以边查询边记录,pos一开始是询问的时间,后来变成了当前边的颜色,边的编号作为下标可以跨越线段树上的时间范围。
操作i能直接影响的范围是[i, j-1],它对染色操作[i+1, j-1]的影响取决于操作i能否被执行,于是就可以将操作i覆盖到[i+1, j-1]中,在分治到叶子节点时判断操作i能否被执行。操作i不需要覆盖自己在叶子上直接判断就好了。
交上去Waiting到现在了都没评出来,对不对我也不知道……
code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 5e5 + 7;
const int K = 55;
const int inf = 1e7;
int pos[maxn], a[maxn], c[maxn], n, m, k, q, f[K][maxn<<1], siz[K][maxn<<1];
vector<int> t[maxn<<2];
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while(ch < '0' || ch > '9')
{
if(ch == '-')
{
f = -1;
}
ch = getchar();
}
while(ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch^48);
ch = getchar();
}
return x * f;
}
struct edge
{
int u, v;
}e[maxn];
struct Stack
{
int tim, c, x, fx;
}st[maxn];
int top;
int find(int c, int x) {return f[c][x] == x ? x : find(c, f[c][x]);}
void merge(int c, int u, int v, int tim)
{
if(u == v) return;
if(siz[c][u] < siz[c][v]) swap(u, v);
st[++top].c = c;
st[top].x = v;
st[top].fx = u;
st[top].tim = tim;
siz[c][u] += siz[c][v]; f[c][v] = u;
}
void del(int tim)
{
while(st[top].tim == tim)
{
int v = st[top].x, u = st[top].fx, c = st[top].c;
siz[c][u] -= siz[c][v];
f[c][v] = v;
top--;
}
}
void update(int x, int l, int r, int L, int R, int id)
{
if(L <= l && r <= R)
{
t[x].push_back(id); return;
}
int mid = (l + r) >> 1;
if(L <= mid) update(x<<1, l, mid, L, R, id);
if(R > mid) update(x<<1|1, mid+1, r, L, R, id);
}
void add_edge(int x)
{
for(int i : t[x])
{
merge(c[i], find(c[i], e[a[i]].u), find(c[i], e[a[i]].v+n), x);
merge(c[i], find(c[i], e[a[i]].u+n), find(c[i], e[a[i]].v), x);
}
}
void solve(int x, int l, int r)
{
add_edge(x);
if(l == r)
{
if(find(c[l], e[a[l]].u) == find(c[l], e[a[l]].v)) puts("NO"), c[l] = pos[a[l]];
else puts("YES"), pos[a[l]] = c[l];
del(x);
return;
}
int mid = (l + r) >> 1;
solve(x<<1, l, mid);
solve(x<<1|1, mid+1, r);
del(x);
}
int main()
{
n = read(); m = read(); k = read(); q = read();
for(int j=1; j<=k; j++)
{
for(int i=1; i<=n; i++)
{
f[j][i] = i; f[j][i+n] = i+n;
siz[j][i] = siz[j][i+n] = 1;
}
}
for(int i=1; i<=m; i++)
{
e[i].u = read(); e[i].v = read(); pos[i] = q+1;
}
for(int i=1; i<=q; i++)
{
a[i] = read(); c[i] = read();
}
for(int i=q; i>=0; i--)
{
if(i+1<pos[a[i]]) update(1, 1, q, i+1, pos[a[i]]-1, i);
pos[a[i]] = i;
}
memset(pos, 0, sizeof(pos));
solve(1, 1, q);
return 0;
}
P5214 [SHOI2014]神奇化合物
非常的模板,我差点就切了。。交上去先爆个0才发现map映射边的时候居然没有交换大小***
code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e5 + 5;
typedef pair<int, int> pii;
int n, m, Q, f[5004], siz[5004];
bool vis[maxn];
map<pii, int> mp;
char op[2];
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while(ch < '0' || ch > '9')
{
if(ch == '-')
{
f = -1;
}
ch = getchar();
}
while(ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch^48);
ch = getchar();
}
return x * f;
}
struct Stack
{
int x, fx, tim;
}st[maxn<<2];
int top;
struct edge
{
int u, v, l, r;
}e[maxn];
int fa(int x) {return f[x] == x ? x : fa(f[x]);}
struct seg
{
vector<int> t[maxn<<2];
int res;
void update(int x, int l, int r, int L, int R, int id)
{
if(L <= l && r <= R)
{
t[x].push_back(id); return;
}
int mid = (l + r) >> 1;
if(L <= mid) update(x<<1, l, mid, L, R, id);
if(R > mid) update(x<<1|1, mid+1, r, L, R, id);
}
void del(int x)
{
while(st[top].tim == x)
{
int v = st[top].x, u = st[top].fx;
siz[u] -= siz[v];
f[v] = v;
res++; top--;
}
}
void add_edge(int x)
{
for(int i : t[x])
{
int u = fa(e[i].u), v = fa(e[i].v);
if(u == v) continue;
if(siz[u] < siz[v]) swap(u, v);
st[++top].x = v; st[top].fx = u;
st[top].tim = x;
siz[u] += siz[v];
f[v] = u; res--;
}
}
void solve(int x, int l, int r)
{
add_edge(x);
if(l == r)
{
if(vis[l]) printf("%d\n", res);
del(x); return;
}
int mid = (l + r) >> 1;
solve(x<<1, l, mid); solve(x<<1|1, mid+1, r);
del(x);
}
}t;
int main()
{
n = read(); m = read();
for(int i=1; i<=m; i++)
{
e[i].u = read(); e[i].v = read();
if(e[i].u > e[i].v) swap(e[i].u, e[i].v);
mp[pii(e[i].u, e[i].v)] = i;
e[i].l = 1;
}
Q = read(); Q++;
for(int i=2; i<=Q; i++)
{
scanf("%s", op);
if(op[0] == 'Q') vis[i] = 1;
else if(op[0] == 'D')
{
int u = read(), v = read();
if(u > v) swap(u, v);
int id = mp[pii(u, v)];
e[id].r = i-1;
}
else
{
e[++m].u = read(); e[m].v = read();
if(e[m].u > e[m].v) swap(e[m].u, e[m].v);
mp[pii(e[m].u, e[m].v)] = m;
e[m].l = i;
}
}
for(int i=1; i<=m; i++)
{
if(!e[i].r) e[i].r = Q;
t.update(1, 1, Q, e[i].l, e[i].r, i);
}
for(int i=1; i<=n; i++) f[i] = i, siz[i] = 1;
t.res = n;
t.solve(1, 1, Q);
return 0;
}
P4632 [APIO2018] 新家
如果你在做线段树分治的相关习题,建议不要点开这个题,因为它不是线段树分治而是线段树上二分,并且它是一个完完全全的鬼畜!!我至今不理解为什么离散化还不能去重,并且对线段树二分的过程表示十分的疑惑,总之就是我太菜了不配做这道题!!!
还有#define mid (l+r)/2 可以但是#define mid (l+r)>>1 就会RE!?我不知道为什么左端点莫名其妙的变成了0。。。
因为用define要加括号!!!!!包括对数组进行定义的时候,因为是直接替换,如果+几没有被括住并且在数组下标的时候还*2了,那最后相当于开的是加的那个数*2!!!!啊这为这玩意儿问了趟老师尴尬坏了。。。
来日方长吧这题我肯定得再做一遍。。直接鹤的连线段树都不想改了。。
code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 2;
const double eps = 1e-6;
#define mid (l+r)/2
#define inf 999999999
int n, K, Q, m, pb, dic[maxn], pdic, seg[maxn<<4], aans[maxn], nu[maxn];
multiset<int> se[300001];
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while(ch < '0' || ch > '9')
{
if(ch == '-')
{
f = -1;
}
ch = getchar();
}
while(ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch^48);
ch = getchar();
}
return x * f;
}
struct eve
{
int x, k, l, r;
}a[300001];
struct que
{
int x, t, bh;
}q0[300001];
struct thi
{
int ty, nx, t;
bool operator < (const thi &T) const
{
if(t == T.t) return ty < T.ty;
return t < T.t;
}
}b[maxn];
void putin()
{
n = read(); K = read(); Q = read();
for(int i=1; i<=n; i++) a[i].x = read(), a[i].k = read(), a[i].l = read(), a[i].r = read();
for(int i=1; i<=Q; i++) q0[i].x = read(), q0[i].t = read(), q0[i].bh = i;
}
void caldic()
{
for(int i=1; i<=n; i++) dic[++pdic] = a[i].x, b[++pb] = (thi){1, i, a[i].l}, b[++pb] = (thi){1, i, a[i].r+1};
for(int i=1; i<=Q; i++) dic[++pdic] = q0[i].x, b[++pb] = (thi){2, i, q0[i].t};
sort(dic+1, dic+pdic+1);
m = pdic + K; sort(b+1, b+pb+1);
for(int i=1; i<=n; i++)
{
int np = lower_bound(dic+1, dic+pdic+1, a[i].x)-dic;
a[i].x = np+(nu[np]++);
}
for(int i=1; i<=Q; i++)
{
int np = lower_bound(dic+1, dic+pdic+1, q0[i].x)-dic;
q0[i].x = np+(nu[np]++);
}
}
void build(int x, int l, int r)
{
if(l == r)
{
if(l > pdic) seg[x] = -1;
else seg[x] = inf;
}
else
{
build(x<<1, l, mid);
build(x<<1|1, mid+1, r);
seg[x] = min(seg[x<<1], seg[x<<1|1]);
}
}
void update(int x, int l, int r, int np, int nx)
{
if(l == r) seg[x] = nx;
else
{
if(np <= mid) update(x<<1, l, mid, np, nx);
else update(x<<1|1, mid+1, r, np, nx);
seg[x] = min(seg[x<<1], seg[x<<1|1]);
}
}
int query(int x, int l, int r, int L, int R)
{
if(l > R || r < L) return inf;
else if(L <= l && r <= R) return seg[x];
else return min(query(x<<1, l, mid, L, R), query(x<<1|1, mid+1, r, L, R));
}
int segef(int x, int l, int r, int nx, int nm)
{
if(l == r) return max(dic[l]-nx, nx-dic[nm]);
int ng = min(nm, seg[x<<1|1]);
if(ng!=-1&&(ng==inf||dic[ng]>=max(0,nx-(dic[mid+1]-1-nx)))) return segef(x<<1, l, mid, nx, ng);
else return segef(x<<1|1, mid+1, r, nx, nm);
}
int main()
{
putin();
caldic();
build(1, 1, m);
for(int i=1; i<=K; i++) se[i].insert(pdic+i), dic[pdic+i] = inf;
for(int i=1; i<=pb; i++)
{
if(b[i].ty == 1)
{
int ni = b[i].nx;
if(b[i].t > a[ni].r) se[a[ni].k].erase(se[a[ni].k].find(a[ni].x));
multiset<int>::iterator ii = se[a[ni].k].lower_bound(a[ni].x);
int np, nq;
np = *ii;
if(ii != se[a[ni].k].begin()) nq = *(--ii); else nq = -1;
if(b[i].t > a[ni].r) update(1, 1, m, np, nq), update(1, 1, m, a[ni].x, inf);
else update(1, 1, m, np, a[ni].x), update(1, 1, m, a[ni].x, nq), se[a[ni].k].insert(a[ni].x);
}
else
{
if(query(1, 1, m, pdic+1, m) == -1) aans[q0[b[i].nx].bh] = -1;
else aans[q0[b[i].nx].bh] = segef(1, 1, m, dic[q0[b[i].nx].x], inf);
}
}
for(int i=1; i<=Q; i++) printf("%d\n", aans[i]);
return 0;
}