省选测试35

总结

名次和昨天还是一样,不过状态有所提升

A.事情的相似度

分析

对于字符串建立后缀自动机

那么区间 \([l,r]\) 的答案就是区间中任意两个点贡献的最大值

对于后缀树上的每一个节点开一个 \(set\) 维护 \(endpos\) 集合

将子树内的点所在的 \(set\) 启发式合并,并在 \(lca\) 处计算能够贡献答案的点对

发现一个点和它的前驱后继贡献答案一定是最优的

这样最终点对的数量是 \(nlogn\) 级别的

把所有的点对和询问按照右端点从小到大排序依次计算

只要拿树状数组维护后缀最大值即可

代码

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cmath>
#include<set>
#define rg register
inline int read(){
	rg int x=0,fh=1;
	rg char ch=getchar();
	while(ch<'0' || ch>'9'){
		if(ch=='-') fh=-1;
		ch=getchar();
	}
	while(ch>='0' && ch<='9'){
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*fh;
}
const int maxn=4e5+5;
int n,q,ans[maxn],shuyu[maxn],tp;
char s[maxn];
struct jie{
	int l,r,id;
	friend bool operator < (const jie& A,const jie& B){
		return A.r<B.r;
	}
}c[maxn];
struct Node{
	int l,r,val;
	Node(){}
	Node(rg int aa,rg int bb,rg int cc){
		l=aa,r=bb,val=cc;
	}
	friend bool operator < (const Node& A,const Node& B){
		return A.r<B.r;
	}
}sta[maxn*20];
std::set<int> se[maxn];
#define sit std::set<int>::iterator
struct SAM{
	int fa[maxn],len[maxn],ch[maxn][5],lst,cnt,h[maxn],tot;
	struct asd{
		int to,nxt;
	}b[maxn];
	void ad(rg int aa,rg int bb){
		b[tot].to=bb;
		b[tot].nxt=h[aa];
		h[aa]=tot++;
	}
	void insert(rg int c){
		rg int p=lst;
		rg int np=lst=++cnt;
		len[np]=len[p]+1;
		for(;p && !ch[p][c];p=fa[p]) ch[p][c]=np;
		if(!p) fa[np]=1;
		else {
			rg int q=ch[p][c];
			if(len[q]==len[p]+1) fa[np]=q;
			else {
				rg int nq=++cnt;
				len[nq]=len[p]+1;
				memcpy(ch[nq],ch[q],sizeof(ch[q]));
				fa[nq]=fa[q];
				fa[np]=fa[q]=nq;
				for(;p && ch[p][c]==q;p=fa[p]) ch[p][c]=nq;
			}
		}
	}
	void build(){
		lst=cnt=1;
		for(rg int i=1;i<=n;i++){
			insert(s[i]-'0');
			se[lst].insert(i);
		}
	}
	void dfs(rg int now){
		rg sit it1,it2;
		for(rg int i=h[now];i!=-1;i=b[i].nxt){
			rg int u=b[i].to;
			if(u==fa[now]) continue;
			dfs(u);
			if(se[now].size()<se[u].size()) std::swap(se[now],se[u]);
			for(rg sit it=se[u].begin();it!=se[u].end();++it){
				rg int tmp=*it;
				se[now].insert(tmp);
				if(se[now].size()>1){
					it1=it2=se[now].lower_bound(tmp);
					++it2;
					if(it1==se[now].begin()){
						sta[++tp]=Node(tmp,*it2,len[now]);
					} else if(it2==se[now].end()){
						--it1;
						sta[++tp]=Node(*it1,tmp,len[now]);
					} else {
						sta[++tp]=Node(tmp,*it2,len[now]);
						--it1;
						sta[++tp]=Node(*it1,tmp,len[now]);
					}
				}
			}
			for(rg sit it=se[u].begin();it!=se[u].end();++it){
				se[now].insert(*it);
			}
		}
	}
	void pre(){
		memset(h,-1,sizeof(h));
		tot=1;
		for(rg int i=2;i<=cnt;i++){
			ad(i,fa[i]),ad(fa[i],i);
		}
		dfs(1);
		std::sort(sta+1,sta+tp+1);
	}
}sam;
int tr[maxn];
int lb(rg int xx){
	return xx&-xx;
}
int cx(rg int wz){
	rg int nans=0;
	for(rg int i=wz;i<=n;i+=lb(i)) nans=std::max(nans,tr[i]);
	return nans;
}
void xg(rg int wz,rg int val){
	for(rg int i=wz;i>0;i-=lb(i)) tr[i]=std::max(tr[i],val);
}
int main(){
	n=read(),q=read();
	scanf("%s",s+1);
	sam.build();
	for(rg int i=1;i<=q;i++){
		c[i].l=read(),c[i].r=read(),c[i].id=i;
	}
	std::sort(c+1,c+q+1);
	sam.pre();
	rg int now=1,head=1;
	for(rg int i=1;i<=n;i++){
		while(now<=tp && sta[now].r==i){
			xg(sta[now].l,sta[now].val);
			now++;
		}
		while(head<=q && c[head].r==i){
			ans[c[head].id]=cx(c[head].l);
			head++;
		}
	}
	for(rg int i=1;i<=q;i++) printf("%d\n",ans[i]);
	return 0;
}

B.跳蚤王国的宰相

分析

发现满足条件的点一定是树的重心

于是我们先求出原树的重心 \(C\),以之为根后,每个子树的大小都不超过 \(\frac{n}{2}\)

\(C\) 连出去的边即可,因为拆下来的子树的大小肯定不超过 \(\frac{n}{2}\)

贪心地从大到小拆

记录一个前缀和二分就行了

拆到了当前点所在的子树要特判一下

分为两种情况,一种是强制拆,一种是不拆

代码

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cmath>
#include<set>
#define rg register
inline int read(){
	rg int x=0,fh=1;
	rg char ch=getchar();
	while(ch<'0' || ch>'9'){
		if(ch=='-') fh=-1;
		ch=getchar();
	}
	while(ch>='0' && ch<='9'){
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*fh;
}
const int maxn=1e6+5;
int h[maxn],tot=1,n;
struct asd{
	int to,nxt;
}b[maxn<<1];
void ad(rg int aa,rg int bb){
	b[tot].to=bb;
	b[tot].nxt=h[aa];
	h[aa]=tot++;
}
int siz[maxn],maxsiz[maxn],rt;
void getroot(rg int now,rg int lat){
	siz[now]=1;
	for(rg int i=h[now];i!=-1;i=b[i].nxt){
		rg int u=b[i].to;
		if(u==lat) continue;
		getroot(u,now);
		siz[now]+=siz[u];
		maxsiz[now]=std::max(maxsiz[now],siz[u]);
	}
	maxsiz[now]=std::max(maxsiz[now],n-siz[now]);
	if(maxsiz[now]<maxsiz[rt]) rt=now;
}
int shuyu[maxn],sta[maxn],tp,sum[maxn],wz[maxn];
void dfs(rg int now,rg int lat,rg int col){
	shuyu[now]=col;
	siz[now]=1;
	for(rg int i=h[now];i!=-1;i=b[i].nxt){
		rg int u=b[i].to;
		if(u==lat) continue;
		dfs(u,now,col);
		siz[now]+=siz[u];
	}
}
int main(){
	memset(h,-1,sizeof(h));
	n=read();
	rg int aa,bb;
	for(rg int i=1;i<n;i++){
		aa=read(),bb=read();
		ad(aa,bb),ad(bb,aa);
	}
	maxsiz[0]=0x3f3f3f3f;
	getroot(1,0);
	for(rg int i=h[rt];i!=-1;i=b[i].nxt){
		dfs(b[i].to,rt,b[i].to);
		sta[++tp]=siz[b[i].to];
	}
	std::sort(sta+1,sta+tp+1);
	std::reverse(sta+1,sta+tp+1);
	for(rg int i=1;i<=tp;i++) wz[sta[i]]=wz[sta[i]]?wz[sta[i]]:i;
	for(rg int i=1;i<=tp;i++) sum[i]=sum[i-1]+sta[i];
	for(rg int i=1;i<=n;i++){
		if(rt==i) printf("0\n");
		else {
			rg int tmp=siz[shuyu[i]];
			rg int nans=std::lower_bound(sum+1,sum+tp+1,n-n/2-siz[i])-sum;
			if(nans>=wz[tmp]){
				nans=std::min(std::lower_bound(sum+1,sum+tp+1,n-n/2+tmp-siz[i])-sum-1,std::lower_bound(sum+1,sum+tp+1,n-n/2)-sum);
			}
			printf("%d\n",nans);
		}
	}
	return 0;
}

C.蛐蛐国的修墙方案

分析

如果把 \(i\)\(p[i]\) 连边,那么最终会形成若干个环

处理长度为 \(2\) 的环,每一个环有两种选择

枚举每一个环是哪一种选择,直接搜索就行

复杂度为 \(n2^{\frac{n}{4}}\)

代码

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cmath>
#include<set>
#include<vector>
#include<cstdlib>
#define rg register
inline int read(){
	rg int x=0,fh=1;
	rg char ch=getchar();
	while(ch<'0' || ch>'9'){
		if(ch=='-') fh=-1;
		ch=getchar();
	}
	while(ch>='0' && ch<='9'){
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*fh;
}
const int maxn=405;
int n,a[maxn],mp[maxn][maxn],vis[maxn],tim,sta[maxn],tp,mat[maxn];
char s[maxn];
std::vector<int> g[maxn];
void predfs(rg int now){
	vis[now]=tim;
	g[tim].push_back(now);
	for(rg int i=1;i<=n;i++){
		if(mp[now][i] && !vis[i]) predfs(i);
	}
}
bool jud(){
	rg int sum=0;
	for(rg int i=1;i<=n;i++){
		sum+=mat[i];
		if(sum<0) return 0;
	}
	if(sum!=0) return 0;
	return 1;
}
void init(rg int aa,rg int bb){
	mat[std::min(aa,bb)]=1;
	mat[std::max(aa,bb)]=-1;
}
void init2(rg int aa,rg int bb){
	if(a[aa]==bb){
		mat[aa]=1,mat[bb]=-1;
	} else {
		mat[aa]=-1,mat[bb]=1;
	}
}
void dfs(rg int now){
	if(now>tp){
		if(jud()){
			for(rg int i=1;i<=n;i++){
				if(mat[i]<0) s[i]=')';
				else s[i]='(';
			}
			printf("%s\n",s+1);
			std::exit(0);
		}
		return;
	}
	rg int tmp=sta[now],tmp2=g[tmp].size();
	for(rg int i=0;i<tmp2;i+=2){
		init2(g[tmp][i],g[tmp][i+1]);
	}
	dfs(now+1);
	for(rg int i=0;i<tmp2;i++) mat[g[tmp][i]]=0;
	for(rg int i=1;i<tmp2-1;i+=2){	
		init2(g[tmp][i],g[tmp][i+1]);
	}
	init2(g[tmp][0],g[tmp][tmp2-1]);
	dfs(now+1);
	for(rg int i=0;i<tmp2;i++) mat[g[tmp][i]]=0;
}
int main(){
	n=read();
	for(rg int i=1;i<=n;i++){
		a[i]=read();
		mp[i][a[i]]=mp[a[i]][i]=1;
	}
	for(rg int i=1;i<=n;i++){
		if(!vis[i]){
			tim++;
			predfs(i);
		}
	}
	for(rg int i=1;i<=tim;i++){
		if(g[i].size()==2){
			init(g[i][0],g[i][1]);
		} else {
			sta[++tp]=i;
		}
	}
	dfs(1);
	return 0;
}
posted @ 2021-03-03 16:53  liuchanglc  阅读(90)  评论(0编辑  收藏  举报