【Codeforces】Manthan, Codefest 18 (Div. 1 + Div. 2) E,F,G

传送门:CodeforcesRound1037
这场的题解,标程都不太友好啊。。。



E. Trips

题解

考虑倒着回答询问,假设所有人都将去旅行,再考虑不满足条件的逐一删去。
d i d_i di表示结点 i i i在旅行网中的度数(已经删去的点不再考虑),用 s e t set set p a i r ( d i , i ) pair(d_i,i) pair(di,i),按 d i d_i di升序排列。
不断弹出旅行网中度数 &lt; k &lt;k <k的点,再逐一更新与之相连的点的度数即可,每次操作断掉一条边,记录 s e t set set中剩余的点数即为答案。

代码

#include<bits/stdc++.h>
#define pii pair<int,int>
#define mkp make_pair
#define fi first
#define sc second
using namespace std;
const int N=2e5+10;
int n,m,k,d[N],ans[N],in[N],sum;
pii rl[N],tp;
vector<pii>g[N];
set<pii>S;

int main(){
	int i,j,q,x,y;
    scanf("%d%d%d",&n,&m,&k);
    sum=n;
    for(i=1;i<=m;++i){
    	scanf("%d%d",&x,&y);
    	rl[i].fi=x;rl[i].sc=y;
    	d[x]++;d[y]++;
    	g[x].push_back(mkp(y,i));
    	g[y].push_back(mkp(x,i));
    }
    for(i=1;i<=n;++i)
    	in[i]=1,S.insert(mkp(d[i],i));
    for(i=m;i;--i){
       if(!sum) {ans[i]=0;continue;}
	   set<pii>::iterator iter=S.begin();
       for(tp=*iter;(!S.empty()) && ((!in[tp.sc])||(d[tp.sc]!=tp.fi)||(tp.fi<k));){
       	  if(!in[tp.sc] || (d[tp.sc]!=tp.fi)) S.erase(iter);
       	  else{
       	  	 S.erase(iter);
       	  	 in[tp.sc]=0;sum--;
       	  	 for(q=g[tp.sc].size()-1;~q;--q){
       	  	 	j=g[tp.sc][q].fi;if(!in[j] || g[tp.sc][q].sc>i) continue;
       	  	 	d[j]--;S.insert(mkp(d[j],j));
       	  	 }
       	  }
		  if(S.empty()) break;
		  else iter=S.begin(),tp=*iter;
       }
       ans[i]=sum;x=rl[i].fi;y=rl[i].sc;
       if(in[x] && in[y]) 
	   {d[x]--;d[y]--;S.insert(mkp(d[x],x));S.insert(mkp(d[y],y));}
    }
    for(i=1;i<=m;++i) printf("%d\n",ans[i]);
}

F. Maximum Reduction

题解

一个递归的过程显然可以转化为统计每个值的出现次数。
考虑单调栈处理出 L i , R i L_i,R_i Li,Ri,区间 [ L i , R i ] [L_i,R_i] [Li,Ri]表示 a i a_i ai为满足 a i = m a x j = L i R i a j a_i=max_{j=L_i}^{R_i} a_j ai=maxj=LiRiaj的最大区间。
对于第 i i i个数 a i a_i ai,出现次数等价于长度为 k , 2 k − 1 , 3 k − 2 , . . . , n k − n + 1 k,2k-1,3k-2,...,nk-n+1 k,2k1,3k2,...,nkn+1的左右端点在 [ L i , R i ] [L_i,R_i] [Li,Ri]范围内,且覆盖点 i i i的区间个数。
c a l ( l , r ) cal(l,r) cal(l,r)为长度如上的左右端点出现在 [ l , r ] [l,r] [l,r]范围内的区间个数,显然是一个等差数列可以算出(这里预处理了一下),则 a i a_i ai的出现次数可以通过容斥得到: c a l ( l , r ) − c a l ( l , i − 1 ) − c a l ( i + 1 , r ) cal(l,r)-cal(l,i-1)-cal(i+1,r) cal(l,r)cal(l,i1)cal(i+1,r)
复杂度 O ( n ) O(n) O(n)

代码

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10,mod=1e9+7;
int n,k,a[N],q[N],top;
int L[N],R[N],v[N],s[N],tot;
int ans;

inline int ad(int x,int y){x+=y;return x>=mod?x-mod:x;}
inline int dc(int x,int y){x-=y;return x<0?x+mod:x;}
inline int mul(int x,int y){return 1ll*x*y%mod;}

inline int cal(int l,int r)
{
	if(l+k-1>r) return 0;
	int len=r-l+1,bs=len/(k-1);
	if(v[bs]>len) bs--;
	return ad(dc(mul(len,bs),s[bs]),bs);
}

int main(){
	int i,j;
	scanf("%d%d",&n,&k);
	for(i=1;i<=n;++i) scanf("%d",&a[i]),L[i]=1,R[i]=n;
	for(i=1;i<=n;++i){
		for(;top && a[q[top]]<a[i];top--) R[q[top]]=i-1;
		L[i]=q[top]+1;q[++top]=i;
    }
	for(i=1;;++i) {v[i]=i*k-i+1;if(v[i]>n) break;}
	tot=i-1;
	for(i=1;i<=tot;++i) s[i]=ad(s[i-1],v[i]);
	for(i=1;i<=n;++i) 
		ans=ad(ans,mul(a[i],dc(cal(L[i],R[i]),ad(cal(L[i],i-1),cal(i+1,R[i])))));
	printf("%d\n",ans);
}

G. A Game on Strings

题解

博弈论相关。处理出每个相同字符之间的区间的 s g sg sg函数和其前缀后缀的 s g sg sg函数。把所有需要处理的区间按长度排序,记忆化搜索预处理。每次询问整块和前后缀的 s g sg sg函数异或得到答案。
复杂度 O ( 2 6 2 n + 26 Q ) O(26^2n+26Q) O(262n+26Q)(似乎忽略了记忆化搜索的复杂度?)

代码

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+100;

int n,m,id[N],num[26];
int ql[26][N],qr[26][N],fl[26][N],fr[26][N];
int w[26][N],pos[N],cnt,tot;
char s[N];

struct L{
  int len,l,r;
  L(){}
  L(int LEN,int QL,int QR){len=LEN;l=QL;r=QR;}
}le[N*30];

inline bool cmp(const L&x,const L&y){
   if(x.len!=y.len) return x.len<y.len;
   if(x.l!=y.l) return x.l<y.l;
   return x.r<y.r;
} 

inline int get(int l,int r)
{
	int i,j,st,ed,res=0,nw;
	for(i=0;i<26;++i)
	 if(qr[i][l]<=r){
	 	nw=0;
		st=qr[i][l];ed=ql[i][r];
		for(j=id[st]+1;j<=id[ed];++j) nw^=w[i][j];
		if(l<st){
			if(fl[i][l]==-1) fl[i][l]=get(l,st-1);
			nw^=fl[i][l];
		}
		if(ed<r){
			if(fr[i][r]==-1) fr[i][r]=get(ed+1,r);
			nw^=fr[i][r];
		}
		res|=(1<<nw);
	 }
	 for(i=0;;++i)
	  if(!((res>>i)&1)) return i;
}

inline int ask(int l,int r)
{
	int i,j,st,ed,res=0,nw;
	for(i=0;i<26;++i)
	 if(qr[i][l]<=r){
		st=qr[i][l];ed=ql[i][r];
		nw=(w[i][id[st]]^w[i][id[ed]]);
		if(l<st){
			if(fl[i][l]==-1) fl[i][l]=ask(l,st-1);
			nw^=fl[i][l];
		}
		if(ed<r){
			if(fr[i][r]==-1) fr[i][r]=ask(ed+1,r);
			nw^=fr[i][r];
		}
		res|=(1<<nw);
 	 }
 	 for(i=0;;++i)
 	  if(!((res>>i)&1)) return i;
}

int main(){
	int i,j,alp,l,r;
	memset(fl,0xff,sizeof(fl));
	memset(fr,0xff,sizeof(fr));
	scanf("%s",s+1);
	n=strlen(s+1);
	for(i=1;i<=n;++i)
		id[i]=++num[s[i]-'a'];
	for(i=0;i<26;++i) ql[i][0]=0,qr[i][n+1]=n+1;
	for(i=1;i<=n;++i){
		for(j=0;j<26;++j) ql[j][i]=ql[j][i-1];
		ql[s[i]-'a'][i]=i;
	}
	for(i=n;i;--i){
		for(j=0;j<26;++j) qr[j][i]=qr[j][i+1];
		qr[s[i]-'a'][i]=i;
	}
	for(i=0;i<26;++i){
		for(cnt=0,j=1;j<=n;++j) if(s[j]-'a'==i) pos[++cnt]=j;
		for(j=1;j<cnt;++j)
		  le[++tot]=L(pos[j+1]-pos[j]-1,pos[j],pos[j+1]);
	}
	sort(le+1,le+tot+1,cmp);//le+tot->le+tot+1 
	for(i=1;i<=tot;++i) if(le[i].r>le[i].l+1){
		j=le[i].r;
        w[s[j]-'a'][id[j]]=get(le[i].l+1,j-1);
	}
	for(i=0;i<26;++i) 
	 for(j=2;j<=num[i];++j)
	   w[i][j]^=w[i][j-1];
	scanf("%d",&m);
	for(;m;--m){
		scanf("%d%d",&l,&r);
		puts(ask(l,r)?"Alice":"Bob");
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值