Live2D

2021-07-14 集训题解

货币

题目传送门

Description

Solution

假设 \(\text{nxt}_i\) 为与 \(i\) 同块的下一个点的位置,那么设 \(f_l\) 表示左端点在 \(l\) 时最靠右的合法右端点,那么可以得到:

\[f_l=\max_{i=0}^{l-1}\{\text{nxt}_i\} \]

特殊的,我们定义 \(\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;
}
posted @ 2021-07-14 20:07  Dark_Romance  阅读(60)  评论(0编辑  收藏  举报