2021-07-14 集训题解
货币
Description
Solution
假设 \(\text{nxt}_i\) 为与 \(i\) 同块的下一个点的位置,那么设 \(f_l\) 表示左端点在 \(l\) 时最靠右的合法右端点,那么可以得到:
特殊的,我们定义 \(\text{nxt}_0\) 为每个块第一次出现位置的最大值。
那么,答案就是 \(\min\{f_l-l+1\}\) 。
可以发现,每次将两个块合并可以启发式合并,并且操作相当于将一部分元素的 \(\text{nxt}_i\) 减小,考虑如何 \(\Theta(\log n)\) 实现该操作。
你发现我们并不需要知道具体的 \(f_i\),我们只需要实时更新答案,而答案是不增的,所以你可以只考虑当前产生的贡献。然后你发现似乎直接暴力更新即可,均摊复杂度就可以做到 \(\Theta(n\log^2 n)\)。
Code
#include <bits/stdc++.h>
using namespace std;
#define Int register int
#define MAXN 200005
template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c != ' ' && c != '\n') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}
template <typename T> void chkmax (T &a,T b){a = max (a,b);}
template <typename T> void chkmin (T &a,T b){a = min (a,b);}
set <int> st[MAXN];
int n,m,type,ans,fa[MAXN],nxt[MAXN];
int findSet (int x){return fa[x] == x ? x : fa[x] = findSet (fa[x]);}
struct Segment{
int maxn[MAXN << 2];
void pushup (int x){maxn[x] = max (maxn[x << 1],maxn[x << 1 | 1]);}
void build (int x,int l,int r){
if (l == r) return maxn[x] = nxt[l],void ();
int mid = l + r >> 1;
build (x << 1,l,mid),build (x << 1 | 1,mid + 1,r);
pushup (x);
}
void modify (int x,int l,int r,int pos,int k){
if (l == r) return maxn[x] = k,void ();
int mid = (l + r) >> 1;
if (pos <= mid) modify (x << 1,l,mid,pos,k);
else modify (x << 1 | 1,mid + 1,r,pos,k);
pushup (x);
}
int query (int x,int l,int r,int ql,int qr){
if (l >= ql && r <= qr) return maxn[x];
int mid = l + r >> 1,res = 0;
if (ql <= mid) chkmax (res,query (x << 1,l,mid,ql,qr));
if (qr > mid) chkmax (res,query (x << 1 | 1,mid + 1,r,ql,qr));
return res;
}
int findit (int x,int l,int r,int k){
if (maxn[x] <= k) return r;
int mid = (l + r) >> 1;
if (maxn[x << 1] > k) return findit (x << 1,l,mid,k);
else return findit (x << 1 | 1,mid + 1,r,k);
}
}T;
void modify (int x,int k){//将nxt[x]修改为k
if (nxt[x] == k) return ;
int l = x,r = T.findit (1,0,n,nxt[x]);
T.modify (1,0,n,x,nxt[x] = k);
while (l < r){
int t = T.query (1,0,n,0,l),p = T.findit (1,0,n,t);
chkmin (ans,t - p + 1),l = p + 1;
}
}
void unionSet (int x,int y){
x = findSet (x),y = findSet (y);if (x == y) return ;
if (st[x].size() > st[y].size()) swap (x,y);fa[x] = y;
if (*st[x].begin() < *st[y].begin()) st[0].erase (*st[y].begin());
else st[0].erase (*st[x].begin());
modify (0,*st[0].rbegin());
for (Int k : st[x]) st[y].insert (k);
for (Int k : st[x]){
set<int>::iterator it = st[y].find (k);
if (it != st[y].begin()) -- it,modify (*it,k),++ it;
++ it;if (it != st[y].end()) modify (k,*it),-- it;
}
}
signed main(){
freopen ("currency.in","r",stdin);
freopen ("currency.out","w",stdout);
read (n,m,type),ans = nxt[0] = n;
for (Int i = 1;i <= n;++ i) fa[i] = i,nxt[i] = 1e9 + i,st[i].insert (i),st[0].insert (i);
T.build (1,0,n);
while (m --> 0){
int u,v;read (u,v),u = (u + type * ans - 1) % n + 1,v = (v + type * ans - 1) % n + 1;
unionSet (u,v),write (ans),putchar ('\n');
}
return 0;
}
比赛
Description
Solution
可以发现,设 \(x=(i-a_i)\mod n\),在 \(x<i\) 的时候将 \(i\) 设为 \(x\) 的儿子,那么一个点是必胜态,当且仅当它的所有儿子都是必败态。
那么,可以发现答案不是 \(0\) 就是 \(0\) 的儿子(实际上在原问题上考虑也可以得到相同的结论)。考虑如何快速维护这个东西。
可以使用 \(\text{LCT}\),每次 link,cut 之后都将根的儿子加入堆里面,然后状态可以设 \(v_{0/1}\) 储存,表示在 Splay 中以一个点为根的子树后加入 必败态/必胜态 之后是什么状态,然后你发现这个可以合并,答案也就是 \(v_0\)。
复杂度 \(\Theta(n\log n)\) 的。
Code
#include <bits/stdc++.h>
using namespace std;
#define Int register int
#define MAXN 300005
template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c != ' ' && c != '\n') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}
template <typename T> void chkmax (T &a,T b){a = max (a,b);}
template <typename T> void chkmin (T &a,T b){a = min (a,b);}
int n,m,a[MAXN],p[MAXN];
struct node{
bool v[2];
bool & operator [] (const int key){return v[key];}
node operator + (const node &p)const{return node{v[p.v[0]],v[p.v[1]]};}
};
priority_queue <int,vector <int>,greater<int> > q;
struct LCT{
#define ls(x) son[x][0]
#define rs(x) son[x][1]
node val[MAXN],sum[MAXN];
LCT(){val[0] = sum[0] = node{0,1};}
int fa[MAXN],son[MAXN][2],cnts[MAXN],isnt[MAXN];
bool rnk (int x){return son[fa[x]][1] == x;}
bool isroot (int x){return son[fa[x]][rnk(x)] != x;}
void pushup (int x){sum[x] = sum[ls(x)] + val[x] + sum[rs(x)];}
void rotate (int x){
int y = fa[x],z = fa[y],k = rnk (x),w = son[x][!k];
if (!isroot (y)) son[z][rnk(y)] = x;son[x][!k] = y,son[y][k] = w;
if (w) fa[w] = y;fa[y] = x,fa[x] = z;
pushup (y),pushup (x);
}
void Splay (int x){
while (!isroot (x)){
int y = fa[x];
if (!isroot (y)) rotate (rnk(x) == rnk(y) ? y : x);
rotate (x);
}
}
void List (int &x){
if (!x) return ;
Splay (x);while (ls(x)) x = ls(x);Splay (x);
}
bool calc (int x){
return Splay (x),(val[x] + sum[rs(x)])[0];
}
void Access (int x){
for (Int y = 0,z;x;x = fa[y = x]){
List (y),Splay (x),z = rs(x),rs(x) = 0,List (z);
if (z) cnts[x] += (isnt[z] = calc (z));
cnts[x] -= isnt[y],isnt[y] = 0,rs(x) = y,val[x] = node{!cnts[x],0},pushup (x);
}
}
void Link (int x,int y){
Access (y),Splay (y),Splay (x),son[fa[x] = y][1] = x,pushup (y);
List (y);
if (y == 1){
y = rs(y);while (ls(y)) y = ls(y);
q.push (y - 1);
}
}
void Cut (int x,int y){
Access (x),List (x),x = rs(x);
while (ls(x)) x = ls(x);
q.push (x - 1),Splay (y),x = son[y][1],son[y][1] = fa[x] = 0,pushup (y);
}
}T;
void makeit (){
while (!q.empty()){
int u = q.top();
if (!p[u] && T.calc (u + 1)) return write (u),putchar ('\n'),void ();
q.pop ();
}
puts ("0");
}
signed main(){
freopen ("match.in","r",stdin);
freopen ("match.out","w",stdout);
read (n,m);
for (Int i = 1;i <= n;++ i) T.sum[i] = T.val[i] = node{1,0};
for (Int i = 0;i < n;++ i){
read (a[i]),p[i] = (i - a[i] + n) % n;
if (p[i] < i) T.Link (i + 1,p[i] + 1);
}
makeit();
while (m --> 0){
int x,y;read (x,y);
if (p[x] < x) T.Cut (x + 1,p[x] + 1);
p[x] = (x - y + n) % n;
if (p[x] < x) T.Link (x + 1,p[x] + 1);
makeit();
}
return 0;
}
字符串
Description
Solution
发现确定根之后可以 \(\Theta(n)\) 构造和判断。
考虑如何找根,设 \(B\) 边 \((u,v)\) 为在 \(E_S,E_T\) 中都出现的边,可以发现如果存在一个点 \(B\) 边度数 \(\ge 3\),如果它不是根的话一定不合法,因为这种边意味着根到 \(v\) 上的字符都相同。
还剩下 \(B\) 边度数都 \(\le 2\) 的情况,可以发现,这种情况下,一条 \(B\) 边 \((u,v)\) 如果 \(v\) 存在分岔那么分岔点的 \(\text{fail}\) 一定是根或者与根存在 \(B\) 边相连的点。枚举判断即可。
Code
#include <bits/stdc++.h>
using namespace std;
#define Int register int
#define MAXN 100005
template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c != ' ' && c != '\n') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}
template <typename T> void chkmax (T &a,T b){a = max (a,b);}
template <typename T> void chkmin (T &a,T b){a = min (a,b);}
vector <int> gS[MAXN],gT[MAXN];
int n,fat[MAXN],dep[MAXN],col[MAXN],par[MAXN],fail[MAXN];
void dfs (int u,int fa){
fat[u] = fa,dep[u] = dep[fa] + 1;
for (Int v : gS[u]) if (v ^ fa) dfs (v,u);
}
bool flg;
stack <int> sta[MAXN];
void dfs2 (int r,int u,int fa){
for (Int v : gS[u]) if (v ^ fat[u]){
flg &= (fail[v] == (sta[col[v]].empty() ? r : sta[col[v]].top()));
sta[col[v]].push (v);
}
for (Int v : gT[u]) if (v ^ fa) dfs2 (r,v,u);
for (Int v : gS[u]) if (v ^ fat[u]) sta[col[v]].pop();
}
bool check (int r){
dfs (r,0);
memset (fail,0,sizeof (fail));
for (Int u = 1;u <= n;++ u) if (u != r){
for (Int v : gT[u]){
if (dep[v] == dep[u]) return 0;
if (dep[v] < dep[u]){
if (!fail[u]) fail[u] = v;
else return 0;
}
}
if (!fail[u]) return 0;
}
int tot = 0;queue <int> q;
for (Int v : gS[r])
if (fail[v] != r) return 0;
else col[v] = ++ tot,q.push (v);
while (!q.empty()){
int u = q.front();q.pop ();
for (Int v : gS[u]) if (dep[v] > dep[u]){
if (fail[v] != r) col[v] = col[fail[v]],q.push (v);
else col[v] = ++ tot,q.push (v);
}
}
flg = 1,dfs2 (r,r,0);
return flg;
}
int su[MAXN],sv[MAXN],deg[MAXN];
void print (int rt){
write (rt),putchar ('\n');
for (Int i = 2;i <= n;++ i){
if (dep[su[i]] > dep[sv[i]]) swap (su[i],sv[i]);
write (col[sv[i]]),putchar (' ');
}
putchar ('\n');
return ;
}
#define pii pair<int,int>
vector <int> B[MAXN];
map <pii,int> mp;
void fuckit (){
for (Int i = 1;i <= n;++ i) if (B[i].size()){
for (Int e : gS[i]) if (!B[e].size()){
for (Int p : gT[e]) if (B[p].size()){
if (check (p)){print(p);return ;}
for (Int q : B[p]) if (check (q)){print(q);return ;}
}
}
}
check (1),print(1);
}
signed main(){
freopen ("string.in","r",stdin);
freopen ("string.out","w",stdout);
read (n);
for (Int i = 2,u,v;i <= n;++ i) read (u,v),su[i] = u,sv[i] = v,gS[u].push_back (v),gS[v].push_back (u),mp[make_pair (u,v)] = mp[make_pair (v,u)] = 1;
for (Int i = 2,u,v;i <= n;++ i){
read (u,v),gT[u].push_back (v),gT[v].push_back (u);
if (mp[make_pair (u,v)]) B[u].push_back (v),B[v].push_back (u);
}
for (Int i = 1;i <= n;++ i) if (B[i].size() >= 3){check (i);print(i);return 0;}
fuckit ();
return 0;
}