十二省联考 2019 部分题解

异或粽子

传送门
做法可以类比超级钢琴,每次贪心取当前最大的一段区间然后将其裂成两个更小的,用可持久化01trie来维护区间 [ l , r i ] [l,r_i] 的最优值(即固定左端点),然后对于这些端点的答案用一个堆来维护即可。
代码:

#include<bits/stdc++.h>
#include<tr1/unordered_map>
#define ri register int
using namespace std;
const int rlen=1<<18|1;
inline char gc(){
	static char buf[rlen],*ib,*ob;
	(ib==ob)&&(ob=(ib=buf)+fread(buf,1,rlen,stdin));
	return ib==ob?-1:*ib++;
}
typedef long long ll;
const int N=5e5+5,M=N*40;
int n,k;
inline unsigned int read(){
	unsigned int ans=0;
	char ch=gc();
	while(!isdigit(ch))ch=gc();
	while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
	return ans;
}
ll sum[N],a[N];
int rt[N];
namespace trie{
	#define lc (son[p][0])
	#define rc (son[p][1])
	int son[M][2],siz[M],tot=0;
	inline void insert(int&tt,int o,ll v){
		int p=tt=++tot;
		for(ri t,i=31;~i;--i){
			lc=son[o][0],rc=son[o][1];
			son[p][t=(v>>i)&1]=++tot;
			p=son[p][t],o=son[o][t];
			siz[p]=siz[o]+1;
		}
	}
	inline ll query(int ql,int qr,ll v){
		ll ret=0;
		for(ri t,i=31;~i;--i){
			t=((v>>i)&1)^1;
			if(siz[son[ql][t]]^siz[son[qr][t]])ql=son[ql][t],qr=son[qr][t],ret|=(i^31)?(1<<i):(1ll<<i);
			else ql=son[ql][t^1],qr=son[qr][t^1];
		}
		return ret;
	}
	#undef lc
	#undef rc
}
struct Node{ll v;int l,r,L,R;friend inline bool operator<(const Node&a,const Node&b){return a.v<b.v;}};
priority_queue<Node>q;
tr1::unordered_map<ll,int>mp;
set<int>S[N];
int sig=0;
int main(){
	n=read(),k=read();
	trie::insert(rt[0],0,0);
	for(ri i=1;i<=n;++i){
		sum[i]=sum[i-1]^(ll)read(),trie::insert(rt[i],rt[i-1],sum[i]);
		if(!mp[sum[i]])mp[sum[i]]=++sig;
		S[mp[sum[i]]].insert(i);
	}
	for(ri i=1;i<=n;++i){
		ll tmp=trie::query(rt[i-1],rt[n],sum[i-1]);
		q.push((Node){tmp,i,*S[mp[tmp^sum[i-1]]].lower_bound(i),i,n});
	}
	int l,r,lim,L,R;
	ll ans=0,t;
	while(k--){
		Node tmp=q.top();
		q.pop();
		l=tmp.l,r=tmp.r,L=tmp.L,R=tmp.R;
		ans+=tmp.v;
		if(r^L){
			t=trie::query(rt[L-1],rt[r-1],sum[l-1]);
			q.push((Node){t,l,*S[mp[t^sum[l-1]]].lower_bound(L),L,r-1});
		}
		if(r^R){
			t=trie::query(rt[r],rt[R],sum[l-1]);
			q.push((Node){t,l,*S[mp[t^sum[l-1]]].lower_bound(r+1),r+1,R});
		}
	}
	cout<<ans;
	return 0;
}

字符串问题

传送门
感觉不是很难的字符串题。。。
显然可以想到用 s a m + t o p s o r t sam+topsort 去过掉 80 p t s 80pts 的部分分。
然后剩下的二十分就只需要用一个前缀和优化建图就完了。
代码:

#include<bits/stdc++.h>
#define ri register int
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
inline int read(){
	#define gc getchar
	int ans=0;
	char ch=gc();
	while(!isdigit(ch))ch=gc();
	while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
	return ans;
	#undef gc
}
const int N=6e6+5;
vector<pii>e[N];
int A,B,du[N];
int n,len[N];
char s[N];
struct Node{int len,id;friend inline bool operator<(const Node&a,const Node&b){return a.len<b.len;}};
namespace sam{
	vector<Node>S1[N],S2[N];
	int son[N][26],len[N],link[N],tot,last,pos[N],st[N][20];
	inline int build(){return link[++tot]=0,S1[tot].clear(),S2[tot].clear(),len[tot]=0,memset(son[tot],0,sizeof(son[tot])),tot;}
	inline void clear(){tot=0,last=build();}
	inline void add(int x,int y,int w){e[x].push_back(pii(y,w)),++du[y];}
	inline void extend(int x,int id){
		int p=last,np=build();
		len[pos[id]=last=np]=len[p]+1;
		while(p&&!son[p][x])son[p][x]=np,p=link[p];
		if(!p){link[np]=1;return;}
		int q=son[p][x],nq;
		if(len[q]==len[p]+1){link[np]=q;return;}
		nq=build(),len[nq]=len[p]+1,memcpy(son[nq],son[q],sizeof(son[q])),link[nq]=link[q],link[q]=link[np]=nq;
		while(p&&son[p][x]==q)son[p][x]=nq,p=link[p];
	}
	inline void init(){
		for(ri i=1;i<=tot;++i)st[i][0]=link[i];
		for(ri j=1;j<20;++j)for(ri i=1;i<=tot;++i)st[i][j]=st[st[i][j-1]][j-1];
	}
	inline void update(int id,int l,int r){
		int x=pos[r];
		for(ri i=19;~i;--i)if(len[st[x][i]]>=r-l+1)x=st[x][i];
		id<=A?S1[x].push_back((Node){r-l+1,id}):S2[x].push_back((Node){r-l+1,id});
	}
	inline void prepare(){
		for(ri p1,p2,s1,s2,r1,r2,p=tot;p;--p){
			sort(S1[p].begin(),S1[p].end());
			sort(S2[p].begin(),S2[p].end());
			p1=p2=0,s1=S1[p].size()-1,s2=S2[p].size()-1;
			add(A+B+p,A+B+p+tot,0);
			if(~s1)add(A+B+p,S1[p][0].id,0);
			for(ri i=p1;i<s1;++i)add(S1[p][i].id,S1[p][i+1].id,-S1[p][i].len);
			for(ri i=p2,j=p1;i<=s2&&j<=s1;++i){
				while(j<=s1&&S2[p][i].len>S1[p][j].len)++j;
				if(j>s1)break;
				add(S2[p][i].id,S1[p][j].id,0);
			}
			if(p^1)add(link[p]+A+B+tot,A+B+p,0);
			for(ri i=p2;i<=s2;++i)add(S2[p][i].id,A+B+p+tot,0);
		}
	}
}
inline void topsort(){
	static int q[N],hd,tl,cnt;
	static ll f[N],val[N],ans;
	hd=1,tl=cnt=ans=0;
	for(ri i=1;i<=A+B+sam::tot*2;++i){
		if(!du[i])q[++tl]=i;
		val[i]=f[i]=(i<=A?len[i]:0); 
	}
	while(hd<=tl){
		int x=q[hd++];
		++cnt,ans=max(ans,f[x]);
		for(ri i=0,v;i<e[x].size();++i){
			--du[v=e[x][i].fi];
			f[v]=max(f[v],f[x]+e[x][i].se+val[v]);
			if(!du[v])q[++tl]=v;
		}
	}
	for(ri i=1;i<=A+B+sam::tot*2;++i)if(du[i]){puts("-1");return;}
	cout<<ans<<'\n';
}
int main(){
	for(ri tt=read();tt;--tt){
		for(ri i=1;i<=A+B+sam::tot*2;++i)e[i].clear(),du[i]=len[i]=0;
		sam::clear();
		scanf("%s",s+1),n=strlen(s+1);
		reverse(s+1,s+n+1);
		for(ri i=1;i<=n;++i)sam::extend(s[i]-'a',i);
		sam::init();
		A=read();
		for(ri i=1,l,r;i<=A;++i)l=read(),r=read(),sam::update(i,n-r+1,n-l+1),len[i]=r-l+1;
		B=read();
		for(ri i=1,l,r;i<=B;++i)l=read(),r=read(),sam::update(i+A,n-r+1,n-l+1);
		sam::prepare();
		for(ri m=read(),u,v;m;--m)u=read(),v=read()+A,sam::add(u,v,0);
		topsort();
	}
	return 0;
}

春节十二响

去nm的春节十二响,直接用 s e t set 启发式合并+贪心就能 C a O CaO 过去。
复杂度参见长链剖分(才不是 O ( n l o g 2 n ) O(nlog^2n) 呢)可以去掉一个 l o g log
代码:

#include<bits/stdc++.h>
#define ri register int
using namespace std;
const int rlen=1<<18|1;
inline char gc(){
	static char buf[rlen],*ib,*ob;
	(ib==ob)&&(ob=(ib=buf)+fread(buf,1,rlen,stdin));
	return ib==ob?-1:*ib++;
}
inline int read(){
	int ans=0;
	char ch=gc();
	while(!isdigit(ch))ch=gc();
	while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
	return ans;
}
const int N=2e5+5;
typedef long long ll;
vector<int>e[N],tmp;
int n,a[N],fa[N];
multiset<int>S[N];
typedef multiset<int>::iterator It;
vector<It>g;
inline void merge(int x,int y){
	if(S[x].size()<S[y].size())swap(S[x],S[y]);
	if(!S[y].size())return;
	It ia=S[x].end(),ib=S[y].end();
	--ia,--ib;
	tmp.clear(),g.clear();
	while(1){
		tmp.push_back(max(*ia,*ib));
		g.push_back(ia);
		if(ib==S[y].begin())break;
		--ia,--ib;
	}
	for(ri i=0;i<g.size();++i)S[x].erase(g[i]);
	for(ri i=0;i<tmp.size();++i)S[x].insert(tmp[i]);
}
void dfs(int p){
	for(ri i=0,v;i<e[p].size();++i)dfs(v=e[p][i]),merge(p,v);
	S[p].insert(a[p]);
}
int main(){
	n=read();
	for(ri i=1;i<=n;++i)a[i]=read();
	for(ri i=2;i<=n;++i)fa[i]=read(),e[fa[i]].push_back(i);
	dfs(1);
	ll ans=0;
	for(It it=S[1].begin();it!=S[1].end();++it)ans+=*it;
	cout<<ans;
	return 0;
}

猜你喜欢

转载自blog.csdn.net/dreaming__ldx/article/details/92001329
今日推荐