A层省选2

A. 货币

线段树维护nxti表示i下一个同色的位置,fr表示以r为右端点的最大左端点

fr=min(i|nxti>r)

使用启发式合并进行维护

由于fr单调,我们线段树二分出以ifrr这个区间,进行分裂

具体实现的一些细节:

  1. fr不用真的维护出来,利用定义式可以快速求出

  2. 先更新nxt再查找0 - i最大的nxt,这样略去了不由新的nxt贡献的区间

  3. 0设置一个哨兵,记录最后一个出现的颜色的最左端,每次query时保证一定包含所有颜色

code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<random>
#include<vector>
#include<set>
using namespace std;
const int maxn = 3e5 + 10;
inline int read(){
	int x = 0; char c = getchar();
	while(c < '0' || c > '9')c = getchar();
	do{x = (x << 3) + (x << 1) + (c ^ 48); c = getchar();}while(c <= '9' && c >= '0');
	return x;
}
int n, q, T, las, mi;
set<int> s[maxn];
struct SET{
	int f[maxn];
	void init(){for(int i = 1; i <= n; ++i)f[i] = i;}
	int fa(int x){return f[x] = f[x] == x ? x : fa(f[x]);}
}S;
int nxt[maxn];
struct tree{
	int mx[maxn << 2 | 1];
	void modify(int x, int l, int r,int pos,int val){
		if(l == r){nxt[l] = mx[x] = val; return;}
		int mid =(l + r) >> 1;
		if(pos <= mid)modify(x << 1, l, mid, pos, val);
		else modify(x << 1 | 1, mid + 1, r, pos, val);
		mx[x] = max(mx[x << 1], mx[x << 1 | 1]);
	}
	int query(int x, int l, int r, int L, int R){
		if(L > R)return 0;
		if(L <= l && r <= R)return mx[x];
		int mid = (l + r) >> 1, ans = 0;
		if(L <= mid) ans = max(ans, query(x << 1, l, mid, L, R));
		if(R > mid) ans = max(ans, query(x << 1 | 1, mid + 1, r, L, R));
		return ans;
	}
	int find(int x, int l, int r, int val){
		if(l == r)return l;
		int mid = (l + r) >> 1;
		if(mx[x << 1] > val)return find(x << 1, l, mid, val);
		else return find(x << 1 | 1, mid + 1, r, val);
	}
}t;
void solve(int pos, int val){
	int r = nxt[pos], now = 0;
	t.modify(1, 0, n + 1, pos, val);
	int pmi = t.query(1, 0, n + 1, 0, pos);
	while(now <= n && nxt[now] <= r){
		now = t.find(1, 0, n + 1, pmi);
		mi = min(mi, pmi - now + 1);
		pmi = nxt[now];
	}
}
void work(int u, int v){
	if(u > v)swap(u ,v);
	if(s[u].size() < s[v].size())swap(s[u], s[v]);
	for(int i : s[v])s[u].insert(i);
	for(int i : s[v]){
		auto it = s[u].find(i);
		if(it != s[u].begin()){
			--it;
			solve(*it, i);
			++it;
		}
		if(it != prev(s[u].end())){
			++it;
			solve(i, *it);
		}
	}
	s[v].clear();
	S.f[v] = u;
	int pos = nxt[0];
	while(s[pos].size() == 0)--pos;
	solve(0, pos);
}
int main(){
	freopen("currency.in", "r",stdin);
	freopen("currency.out", "w",stdout);
	n = read(), q = read(), T = read();
	mi = n;
	for(int i = 1; i <= n; ++i)t.modify(1, 0, n + 1, i, maxn);
	for(int i = 1; i <= n; ++i)s[i].insert(i);
	t.modify(1, 0, n + 1, 0, n);
	S.init();
	for(int i = 1; i <= q; ++i){
		int u = (read() + T * las - 1) % n + 1;
		int v = (read() + T * las - 1) % n + 1;
		u = S.fa(u); v = S.fa(v);
		if(u != v)work(u, v);
		printf("%d\n",las = mi);
	}
	return 0;
}

B. 比赛

发现当x+a[i]i并且改完一定赢才会改

那么发现如果没有人能把他改完的值(一定等于他自己)改掉,那么他才会改

如果一个人没有被限制,可以通过ia[i]x找出他限制谁改,最后找出最前面的答案即可

但是还需要用LCT优化啥的,我还不会,先跑了

暴力
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<random>
#include<vector>
#include<set>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
const int maxn = 300005;
inline int read(){
	int x = 0; char c = getchar();
	while(c < '0' || c > '9')c = getchar();
	do{x = (x << 3) + (x << 1) + (c ^ 48); c = getchar();}while(c <= '9' && c >= '0');
	return x;
}
int n, q, a[maxn], cnt[maxn];
set<int>s;
void match(){
	int nans = 0;
	for(int i = n - 1; i >= 1; --i)
	  if(cnt[i] == 0){
		  if(a[i] % n  == i)nans = i, s.insert(i);
		  int to = (i - a[i] + n) % n;
		  if(to < i)++cnt[to];
	  }
	printf("%d\n",nans);
}
void del(int x);
void add(int x);

void add(int x){
	if(x == 0)return;
	++cnt[x];
	if(cnt[x] == 1){
		if(a[x] == x)s.erase(x);
		else{
			int to = (x - a[x] + n) % n;
			if(to < x)del(to);
		}
	}
}
void del(int x){
	if(x == 0)return;
	--cnt[x];
	if(cnt[x] == 0){
		if(a[x] == x)s.insert(x);
		else{
			int to = (x - a[x] + n) % n;
			if(to < x)add(to);
		}
	}
}
void upd(int x, int y){
	if(cnt[x] == 0){
		if(a[x] == x)s.erase(x);
		else {
			int lasto = (x - a[x] + n) % n;
			if(lasto < x) del(lasto);
		}
		if(y == x && x)s.insert(x);
		else{
			int to = (x - y + n) % n;
			if(to < x) add(to);
		}
	}
	a[x] = y;
	if(s.empty())printf("0\n");else printf("%d\n",*s.begin());
}

int main(){
	freopen("match.in", "r", stdin);
	freopen("match.out", "w", stdout);
	n = read(), q = read();
	for(int i = 0; i < n; ++i)a[i] = read();
	match();
	for(int i = 1; i <= q; ++i){
		int x = read(), y = read();
		upd(x, y);	
	}
	return 0;
}

C. 字符串

如何找根?shuffle

一定存在既在fail树又在tire树上的边,这些边都是从根节点出发的,而且一条链上的字符完全相同

那么,如果存在一个点度数大于2,那么他一定是根

如果不存在,那么就会有一条链,他是aaaaaaaa....这样的串,考虑他在tire树上不在链上的邻居,一定是aaaaaa....aab这样的串,这个邻居的fail要么还是类似的邻居(这样我们不管),要么在链上,代表b或者空串,那么他要么是根,要么与根是邻居,只有三个点,可以暴力check一下

具体check,应该先bfs处理边上的信息,再建AC自动机,但是为了省事,可以根据fail树上的父亲在
tire树上深度小于它这个必要条件判一下

如果你这样写没过,可以试试倒叙扫shuffle等乱搞做法,

code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<random>
#include<vector>
#include<set>
#include<iostream>
using namespace std;

typedef long long ll;
typedef unsigned long long ull;
const int maxn = 300005;
inline int read(){
	int x = 0; char c = getchar();
	while(c < '0' || c > '9')c = getchar();
	do{x = (x << 3) + (x << 1) + (c ^ 48); c = getchar();}while(c <= '9' && c >= '0');
	return x;
}
int n, ans[maxn];
struct tree{
	int head[maxn], tot;
	struct edge{int to, net;}e[maxn << 1 | 1];
	void add(int u, int v){
		e[++tot].net = head[u];
		head[u] = tot;
		e[tot].to = v;
	}
	int dep[maxn], fa[maxn];
	void dfs(int x){
		for(int i = head[x]; i; i = e[i].net){
			int v = e[i].to;
			if(v == fa[x])continue;
			dep[v] = dep[x] + 1;
			fa[v] = x;
			dfs(v);
		}
	}
}trie, fail;
queue<int>q;
int mx, root;
void getans(){
	q.push(root);
	while(!q.empty()){
		int x = q.front(); q.pop();
		for(int i = trie.head[x]; i; i = trie.e[i].net){
			int v = trie.e[i].to;
			if(v == trie.fa[x])continue;
			if(fail.fa[v] != root){
				ans[v] = ans[fail.fa[v]];
			}else ans[v] = ++mx;
			q.push(v);
		}
	}
	printf("%d\n",root);
	for(int i = 1; i <= trie.tot; i += 2){
		int u = trie.e[i].to, v = trie.e[i + 1].to;
		if(trie.dep[u] < trie.dep[v])swap(u, v);
		printf("%d ",ans[u]);
	}
	printf("\n");
}
int p[maxn];
bool check(int nrt){
	trie.dep[nrt] = 1; trie.fa[nrt] = 0; trie.dfs(nrt);
	fail.dep[nrt] = 1; fail.fa[nrt] = 0; fail.dfs(nrt);
	trie.dep[0] = 0;
	for(int i = 1; i <= n; ++i)if(trie.dep[fail.fa[i]] >= trie.dep[i] || trie.dep[fail.fa[trie.fa[i]]] + 1 < trie.dep[fail.fa[i]])return false;
	return true;
}
int cnt[maxn];
bool vis[maxn];
int work1(){
	for(int x = 1; x <= n; ++x){
		for(int i = trie.head[x]; i; i = trie.e[i].net) vis[trie.e[i].to] = 1;
		for(int i = fail.head[x]; i; i = fail.e[i].net) { int v = fail.e[i].to; if(vis[v]) ++cnt[v];}
		for(int i = trie.head[x]; i; i = trie.e[i].net) vis[trie.e[i].to] = 0;
	}
	for(int i = 1; i <= n; ++i) if(cnt[i] > 2)return i;
	int rt = 0;
	for(int x = 1; x <= n; ++x){
		if(cnt[x]){
			for(int i = trie.head[x]; i; i = trie.e[i].net){
				int y = trie.e[i].to;
				if(!cnt[y]){
					for(int j = fail.head[y]; j; j = fail.e[j].net){
						int z = fail.e[j].to; 
						if(cnt[z]){rt = z;break;}
					}
				}
				if(rt)break;
			}
		}
		if(rt)break;
	}
	if(check(rt))return rt;
	for(int i = trie.head[rt]; i; i = trie.e[i].net){
		int y = trie.e[i].to;
		if(cnt[y] && check(y))return y;
	}
	return rt;
}
int main(){
	freopen("string.in", "r", stdin);
	freopen("string.out", "w", stdout);
	n = read();
	for(int i = 1; i < n; ++i){
		int u = read(), v = read();
		trie.add(u, v); trie.add(v, u);
	}
	for(int i = 1; i < n; ++i){
		int u = read(), v = read();
		fail.add(u, v); fail.add(v, u);
	}
	root = work1();
	trie.dep[root] = 1; trie.fa[root] = 0; trie.dfs(root);
	fail.dep[root] = 1; fail.fa[root] = 0; fail.dfs(root);
	getans();
	return 0;
}
posted @   Chen_jr  阅读(15)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示