[模拟赛]模拟赛集合。

#A1

A 卡利班

发现其任意时刻其结构为一颗树,而 \(f(x)\) 即为这颗树的直径 \((a,b)\) 两端点到 \(x\) 的最大值。

我们要做的就是动态合并两颗树。

可以使用 \(LCT\) ,复杂度 \(O(nlogn)\)

如果不会 \(LCT\) 的话建议使用启发式合并,暴力遍历小的那边,暴力更新\(dep\)和倍增数组。

A 卡利班
#include<iostream>
#include<cstdio>
#define ll long long 
#define N 3000005

//___________________

ll f[N],c[N][2],siz[N],st[N];
bool ri[N];

#define l(x) c[x][0]
#define r(x) c[x][1]

inline bool nroot(int x){return l(f[x]) == x || r(f[x]) == x;}

inline void up(int x){siz[x] = (siz[l(x)] + siz[r(x)] + 1);}

inline void pushr(int x){std::swap(l(x),r(x));ri[x] ^= 1;}

inline void pushdown(int x){
	if(ri[x]){
		if(l(x))pushr(l(x));
		if(r(x))pushr(r(x));
		ri[x] = 0;
	} 
}

inline void rotate(int x){
	int y = f[x],z = f[y],k = r(y) == x,w = c[x][!k];
	if(nroot(y))
	c[z][r(z) == y] = x;c[x][!k] = y;c[y][k] = w;
	if(w)
	f[w] = y;f[y] = x;f[x] = z;
	up(y);up(x);
}

inline void splay(int x){
	ll y = x,z = 0;
	st[++z] = y;
	while(nroot(y))st[++z] = y = f[y];
	while(z)pushdown(st[z -- ]);
	while(nroot(x)){
		ll y = f[x],z = f[y];
		if(nroot(y))
		rotate((l(y) == x) ^ (l(z) == y) ? x : y);
		rotate(x);
	}
	up(x);
}

inline void access(int x){
	for(int y = 0;x;x = f[y = x])
	splay(x),r(x) = y,up(x);
}

inline void makeroot(int x){access(x),splay(x),pushr(x);}

inline void split(int x,int y){makeroot(x),access(y),splay(y);}

inline void link(int x,int y){makeroot(x);f[x] = y;}

//__________________LCT

ll fa[N],l[N],r[N];

inline ll find(int x){return (fa[x] == x) ? x : fa[x] = find(fa[x]);}

inline void merge(int x,int y){//x -> y
	ll ans = 0,ansl,ansr;
	fa[x] = y;
	split(l[x],r[x]);
	if(siz[r[x]] - 1 > ans)
	ans = siz[r[x]] - 1,ansl = l[x],ansr = r[x];

	split(l[y],r[x]);
	if(siz[r[x]] - 1 > ans)
	ans = siz[r[x]] - 1,ansl = l[y],ansr = r[x];
	
	split(l[x],r[y]);
	if(siz[r[y]] - 1 > ans)
	ans = siz[r[y]] - 1,ansl = l[x],ansr = r[y];

	split(l[y],r[y]);
	if(siz[r[y]] - 1 > ans)
	ans = siz[r[y]] - 1,ansl = l[y],ansr = r[y];	

	split(l[x],l[y]);
	if(siz[l[y]] - 1 > ans)
	ans = siz[l[y]] - 1,ansl = l[x],ansr = l[y];	

	split(r[x],r[y]);
	if(siz[r[y]] - 1 > ans)
	ans = siz[r[y]] - 1,ansl = r[x],ansr = r[y];			

	l[y] = ansl,r[y] = ansr;
	
//	std::cout<<x<<" "<<y<<" "<<l[y]<<" "<<r[y]<<std::endl;
}

//__________________DSU

ll n,q,type,last;

int main(){
//	freopen("A.in","r",stdin);
//	freopen("A.out","w",stdout);
	scanf("%lld",&type);
	scanf("%lld%lld",&n,&q);
	for(int i = 1;i <= n;++i)
	fa[i] = l[i] = r[i] = i;
	while(q -- ){
		ll opt,u,v;
		scanf("%lld",&opt);
		if(opt == 1){
			scanf("%lld%lld",&u,&v);
			u ^= type * last;
			v ^= type * last;
			link(u,v);
			merge(find(u),find(v));
		}else{
			scanf("%lld",&u);
			u ^= type * last;
			ll al = l[find(u)],ar = r[find(u)];
			ll ans = 0;
//			std::cout<<al<<" "<<ar<<std::endl;
			split(al,u);
			ans = std::max(ans,siz[u] - 1);
			split(ar,u);
			ans = std::max(ans,siz[u] - 1);
			std::cout<<(last = ans)<<std::endl;
		}
	}
}

B. M-IV-Y

对所有颜色的位置维护其最前,最后两个位置,并分别处理,然后静态答案为\(A\)的数在\(B\)中的逆序对数。

考虑把一个操作看做从一个颜色集合中删除一个数,然后加入一个颜色集合。

然后可能对颜色集合的最前最后产生影响,所以又变成了从\(A,B\)中加入,删除点。

考虑有修改操作的二维数点,CDQ分治。

C. 博福斯 TRV

不会可持久平衡树。
暴力六十不好吗。

#A3

A. 搞颜色

考虑拆贡献到每个颜色。

对每两个点夹住的区间,根据其长度进行一个区间等差数列加,用两颗BIT维护。

A. 搞颜色
#include<bits/stdc++.h>
#define ll long long 
#define N 300005

int n,m;
int a[N];

inline int read(){
	int ans = 0,f = 1;
	char a = getchar();
	while(a != '-' && !(a <= '9' && a >= '0'))
	a = getchar();
	while(a <= '9' && a >= '0')
	ans = (ans << 1) + (ans << 3) + (a - '0'),a = getchar();
	return f * ans; 
} 

std::set<int>S[N];

ll T[N];//BIT
ll B[N];//Tag

#define lowbit(x) (x & -x)

inline void addT(int x,int p){
	for(int i = x;i <= n;i += lowbit(i))
	T[i] += p;
}


inline void addB(int x,int p){
	for(int i = x;i <= n;i += lowbit(i))
	B[i] += p;	
}


inline ll QT(int x){
	ll ans = 0;
	for(int i = x;i;i -= lowbit(i))
	ans = ans + T[i];
	return ans;
}

inline int QB(int x){
	int ans = 0;
	for(int i = x;i;i -= lowbit(i))
	ans = ans + B[i];
	return ans;
}

#define IT std::set<int>::iterator

inline void Add(int len,int opt){//长度,符号 
	addT(1,len * opt);
	addT(len + 1,-1 * len * opt);
	addB(1,1 * opt);
	addB(len + 1,-1 * opt);
}

inline void pre(){//first_ans
	for(int i = 1;i <= n;++i){
//		std::cout<<i<<" "<<std::endl;
		IT it = S[i].begin();
		++ it;
		IT end = S[i].end();
		while(it != S[i].end()){
			IT las = prev(it);
//			std::cout<<*las<<" "<<*it<<" "<<(*it - *las - 1)<<std::endl;				
 			Add(*it - *las - 1,1);
 			++ it;
		}		
	}
}

inline void change(int x,int w){
	IT it = S[a[x]].lower_bound(x);
	IT las,nex;
	nex = ++it;
	-- it;
	las = prev(it);
	Add(*it - *las - 1,-1);
	Add(*nex - *it - 1,-1);
	Add(*nex - *las - 1,1);
	S[a[x]].erase(x);
	a[x] = w;
	S[a[x]].insert(x);
	it = S[a[x]].lower_bound(x);
	nex = ++it;
	-- it;
	las = prev(it);
	Add(*it - *las - 1,1);
	Add(*nex - *it - 1,1);
	Add(*nex - *las - 1,-1);	
}

int main(){
//	freopen("q.in","r",stdin);
//	freopen("q.out","w",stdout);
	n = read(),m = read(); 
	for(int i = 1;i <= n;++i){
		a[i] = read();
		S[a[i]].insert(i);
	}
	for(int i = 1;i <= n;++i){
		S[i].insert(0);
		S[i].insert(n + 1);
	}
	pre();	
	while(m -- ){
		int opt,x,w;
		opt = read();
		if(opt == 2){
			x = read();
			std::cout<<1ll * (n - x + 1) * n - (QT(x) - 1ll * QB(x) * (x - 1))<<std::endl;			
		}
		else{
			x = read(),w = read();
			change(x,w);
		}	
	}
}

B. 匹配

remake成功了。

高消一下,发现设\(f_i = a_{i,i}\),所以第\(i\)行,只有\(f_i\)\(0\)两个取值。
当且仅当\(j \in i\)\(a_{i,j} = f_i\)
所以\(\sum_{k\in i}f_k = a_i\)

考虑归纳法证明其,考虑第\(i\)行减去所有子集\(k\)对应的行消元结果,\(\sum_{k\in i}f_k[k\in j] = \sum_{k \in (i \& j)}f_k(k \neq i)\)

\(i \notin j\),则原式\(\sum_{k\in i\& j}f_k = a_{i \& j}\)

否则\(\sum_{k \in i} f_k = a_i - f_i(i \neq k)\)

考虑\(\sum_{k \in i}f_k = a_i\),可以推出\(a\)\(f\)高维前缀和的结果,做一次高维差分,一次FMT可以得到答案。

复杂度\(O(nlogn)\).

B. 匹配
#include<bits/stdc++.h>
#define ll long long 
#define mod 998244353
#define N 1000005

ll ans = 1; 

ll a[N];

int n;

inline void FWT_or(ll *f,int type){
	for(int mid = 1;mid < n;mid <<= 1){
		for(int block = mid << 1,j = 0;j < n;j += block)
		for(int i = j;i < j + mid;++i)
		f[i + mid] = (f[i + mid] + f[i] * type + mod) % mod; 
	}	
}

int main(){
//	freopen("q.in","r",stdin);
//	freopen("q.out","w",stdout);
	scanf("%d",&n);
	for(int i = 0;i < n;++i)
	scanf("%lld",&a[i]);
	FWT_or(a,-1);
	for(int i = 0;i < n;++i)
	ans = ans * a[i] % mod; 
	std::cout<<ans<<std::endl;
}

C. 蹦蹦炸弹

投出带来无限快乐的蹦蹦炸弹!

考虑我们使用\(krasual\)

我们设\(a_i\)\(i\)的联通块编号。

考虑如果有连边则\([l1,r1],[l2,r2]\)有不同。

hash,二分直接找到要改的地方。

每次都会加一条边,只会加\(n - 1\)条。

然后考虑更新联通块编号。

即我们启发式合并暴力修改一边的编号。

是傻逼题,可惜我也是傻逼。

C. 蹦蹦炸弹
#include<bits/stdc++.h>
#define ll long long 
#define N 100005
#define mod 998244353
#define M 500005

int n,m;

int fa[N];

std::set<int>S[N];

ll base[N],inv[N];

inline ll pow(ll a,ll b){
	ll ans = 1;
	while(b){
		if(b & 1) ans = ans * a % mod;
		a = a * a % mod;
		b >>= 1;
	}
	return ans;
}

ll T[N];

#define lowbit(x) (x & -x)

inline void add(int x,ll p){
	for(int i = x;i <= n;i += lowbit(i))
	T[i] = (T[i] + p + mod) % mod;
}

inline ll find(int x){
	ll ans = 0;
	for(int i = x;i;i -= lowbit(i))
	ans = (ans + T[i]) % mod;
	return ans;
}

inline ll Hash(int l,int r){
	return (find(r) - find(l - 1) + mod) % mod * inv[l] % mod; 
}

struct P{
	int l1,l2,len,w;
}e[M];

bool operator < (P a,P b){
	return a.w < b.w;
}

ll ans = 0;

int cnt = 0;

#define IT std::set<int>::iterator 

inline void merge(int x,int y){
	if(S[x].size() > S[y].size())std::swap(x,y);//x -> y
	IT it = S[x].begin();
	while(it != S[x].end()){
		add(*it,-1 * base[*it] * x % mod);
		add(*it,base[*it] * y % mod);
		fa[*it] = y;
		S[y].insert(*it);
		++it;
	}
}

inline bool check(int l1,int r1,int l2,int r2){
//	std::cout<<"["<<l1<<" "<<r1<<"]"<<"["<<l2<<" "<<r2<<"]"<<std::endl;	
//	std::cout<<Hash(l1,r1)<<" "<<Hash(l2,r2)<<std::endl; 
	if(Hash(l1,r1) == Hash(l2,r2) || cnt == n - 1)
	return 0;
	#define mid1 ((l1 + r1) >> 1)
	#define mid2 ((l2 + r2) >> 1)
	while(l1 != r1){
		if(Hash(l1,mid1) == Hash(l2,mid2))
		l1 = mid1 + 1,l2 = mid2 + 1;
		else
		r1 = mid1,r2 = mid2;
//		std::cout<<"["<<l1<<" "<<r1<<"]"<<"["<<l2<<" "<<r2<<"]"<<std::endl;
	}
//	std::cout<<"ADD"<<"["<<l1<<" "<<r1<<"]"<<"["<<l2<<" "<<r2<<"]"<<std::endl;
	merge(fa[l1],fa[l2]);
	return 1;
}

int main(){
//	freopen("q.in","r",stdin);
//	freopen("q.out","w",stdout);	
	scanf("%d%d",&n,&m);
	for(int i = 1;i <= n;++i)
	fa[i] = i,S[i].insert(i);
	base[0] = 1;
	for(int i = 1;i <= n;++i)
	base[i] = base[i - 1] * 10 % mod;
	for(int i = 1;i <= n;++i)
	add(i,fa[i] * base[i] % mod);
	inv[n] = pow(base[n],mod - 2);
	for(int i = n - 1;i >= 0;--i)
	inv[i] = inv[i + 1] * 10 % mod;
	for(int i = 1;i <= m;++i){
		scanf("%d%d%d%d",&e[i].l1,&e[i].l2,&e[i].len,&e[i].w);
	}
	std::sort(e + 1,e + m + 1);
	for(int i = 1;i <= m;++i){
//		std::cout<<e[i].l1<<" "<<e[i].l2<<" "<<e[i].len<<" "<<e[i].w<<std::endl; 
//		check(e[i].l1,e[i].l1 + e[i].len - 1,e[i].l2,e[i].l2 + e[i].len - 1); 
		while(check(e[i].l1,e[i].l1 + e[i].len - 1,e[i].l2,e[i].l2 + e[i].len - 1))
		ans += e[i].w,cnt ++ ;
	}
	std::cout<<ans<<std::endl; 
}

/*
5 3
1 3 2 5
2 3 2 3
4 5 1 1
*/
原文地址:https://www.cnblogs.com/dixiao/p/15711218.html