CF杂题训练(交互题不做,2500以上的看情况吧)

CF1562

A The Miracle and the Sleeper

TP
考虑一个数,取模比他小的数模数的最大值一定是 ( a + 1 ) / 2 (a+1)/2 (a+1)/2
同时 a a a要取得越大越好
考虑 l l l,如果 l > a / 2 l>a/2 l>a/2 直接最大值为 r % l r\%l r%l
反之为 ( a + 1 ) / 2 (a+1)/2 (a+1)/2

#include <bits/stdc++.h>
using namespace std;
const int maxn = 5e4+5;
int readint(){
	int x=0,f=1;char s=getchar();
	#define sc (s=getchar())
	while(s<'0'||s>'9'){
		if(s=='-')
			f=-1;
		sc;
	}
	while(s>='0'&&s<='9'){
		x=(x<<3)+(x<<1)+(s^48);
		sc;
	}
	#undef sc
	return x*f;
} 
int main (){
	int t=readint();
	while(t--){
		int a=readint(),b=readint();
		if(a>b/2)
			printf("%d\n",b%a);
		else
			printf("%d\n",((b+1)>>1)-1); 
	} 
	return 0;
} 

B Scenes From a Memory

TP
考虑如果有 1 , 4 , 6 , 8 , 9 1,4,6,8,9 1,4,6,8,9直接输出就行了
反之只剩下 2 , 3 , 5 , 7 2,3,5,7 2,3,5,7,如果其中有一个的个数超过两个就直接输出 2 , a a 2,aa 2,aa
剩下就是大家都为一个,讨论情况易得

#include <bits/stdc++.h>
using namespace std;
const int maxn = 5e4+5;
int readint(){
	int x=0,f=1;char s=getchar();
	#define sc (s=getchar())
	while(s<'0'||s>'9'){
		if(s=='-')
			f=-1;
		sc;
	}
	while(s>='0'&&s<='9'){
		x=(x<<3)+(x<<1)+(s^48);
		sc;
	}
	#undef sc
	return x*f;
} 
char s[55];
int cnt[10];
int main (){
	int t=readint();
	while(t--){
		memset(cnt,0,sizeof(cnt));
		int n=readint();
		scanf("%s",s+1);
		int flag=0;
		for(int i=1;i<=n;i++){
			int num=s[i]-'0';
			cnt[num]++;
			if(num==1||num==4||num==6||num==8||num==9){
				printf("1\n%d\n",num);
				flag=1;
				break;
			}
		}
		if(flag)
			continue;
		for(int i=1;i<=9;i++){
			if(cnt[i]>=2){
				printf("2\n%d%d\n",i,i);
				flag=1;
				break;
			}
		}
		if(flag)
			continue;
		puts("2");
		for(int i=1;i<=n;i++){
			int num=s[i]-'0';
			if(num==2){
				cnt[2]--;
				if(cnt[5])
					puts("25"),flag=1;
				else if(cnt[7])
					puts("27"),flag=1;
			}
			if(num==3){
				cnt[3]--;
				if(cnt[5])
					puts("35"),flag=1;
				else if(cnt[2])
					puts("32"),flag=1;
			}
			if(num==5){
				cnt[5]--;
				if(cnt[2])
					puts("52"),flag=1;
				else if(cnt[7])
					puts("57"),flag=1;
			}
			if(num==7){
				cnt[7]--;
				if(cnt[5])
					puts("75"),flag=1;
				else if(cnt[2])
					puts("72"),flag=1;
			}
			if(flag==1)
				break;
		}
	}
	return 0;
} 

C Rings

TP
开头傻逼了,没想到前导零也算
那么有 0 0 0的时候直接取前面或者后面(看那一边大于 n / 2 n/2 n/2即可)
如果没有 0 0 0,那就全是 1 1 1,取两个相等长度的即可

#include <bits/stdc++.h>
using namespace std;
const int maxn = 5e4+5;
int readint(){
	int x=0,f=1;char s=getchar();
	#define sc (s=getchar())
	while(s<'0'||s>'9'){
		if(s=='-')
			f=-1;
		sc;
	}
	while(s>='0'&&s<='9'){
		x=(x<<3)+(x<<1)+(s^48);
		sc;
	}
	#undef sc
	return x*f;
} 
char s[maxn];
int main (){
	int t=readint();
	while(t--){
		int n=readint();
		scanf("%s",s+1);
		bool flag=0;
		for(int i=1;i<=n;i++){
			if(s[i]=='0'){
				flag=1;
				if(i>n/2)
					printf("1 %d 1 %d\n",i,i-1);
				else
					printf("%d %d %d %d\n",i,n,i+1,n);
				break;
			} 
		}
		if(!flag){
			printf("1 %d 2 %d\n",n-1,n);
		}
	}
	return 0;
} 

D Two Hundred Twenty One

TP
先考虑整个串,如果发现当前区间满足情况,直接输出 0 0 0
如果当前区间长度为奇数,那么只需要删除一个即可,因为贡献是逐渐累积的,设这个区间贡献为 s s s,你一定可以在一个位置找到他的前面的贡献是 s / 2 s/2 s/2,后面为 s / 2 s/2 s/2,
如果为偶数,先任意删一个位置,在按奇数的方法选择
接下来考虑删除位置,也就是考虑奇数长度删哪里,由上面所说,设删除的位置为 k k k,也就是 l → ( k − 1 ) l\to (k-1) l(k1)的贡献,等于 ( k + 1 ) → r (k+1)\to r (k+1)r
也就是说 p r e [ k − 1 ] − p r e [ l − 1 ] = p r e [ r ] − p r e [ k ] pre[k-1]-pre[l-1]=pre[r]-pre[k] pre[k1]pre[l1]=pre[r]pre[k]
移项 p r e [ k − 1 ] + p r e [ k ] = p r e [ l − 1 ] + p r e [ r ] pre[k-1]+pre[k]=pre[l-1]+pre[r] pre[k1]+pre[k]=pre[l1]+pre[r]
然后先预处理,再去查找(离散化和大常数 S T L STL STL都可以)懒狗就是我

#include <bits/stdc++.h>
using namespace std;
const int maxn = 3e5+5;
int readint(){
	int x=0,f=1;char s=getchar();
	#define sc (s=getchar())
	while(s<'0'||s>'9'){
		if(s=='-')
			f=-1;
		sc;
	}
	while(s>='0'&&s<='9'){
		x=(x<<3)+(x<<1)+(s^48);
		sc;
	}
	#undef sc
	return x*f;
} 
char s[maxn];
int sum[maxn];
map<int,set<int> > mp;
int main (){
	int t=readint();
	while(t--){
		int n=readint(),m=readint();
		scanf("%s",s+1);
		mp.clear();
		sum[0]=0;
		for(int i=1;i<=n;i++) {
			sum[i]=sum[i-1]+((s[i]=='+')?1:-1)*((i&1)?1:-1);
			mp[sum[i]+sum[i-1]].insert(i);
		}
		for(int i=1;i<=m;i++){
			int x=readint(),y=readint();
			if(sum[y]-sum[x-1]==0)
				puts("0");
			else{	
				if((y-x)&1){
					puts("2"),printf("%d ",y);
					y--;
				}
				else
					puts("1");
				int pos=*mp[sum[y]+sum[x-1]].lower_bound(x);
				printf("%d\n",pos);
			}
		}
	}
	return 0;
}  

E Rescue Niwen!

TP
易得,选了以 i i i号位开头的,那么全都选
a b c ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ abc······ abc,只选前三个,后面再遇到 a b c abc abc显然个数要少一些(感性理解,我并不知道末尾大小影响会造成啥)
然后求一个 L C P LCP LCP后再做 d p dp dp即可
(我打死不会说我是用 S A SA SA结果还是用 n 2 n^2 n2求的)

#include <bits/stdc++.h>
using namespace std;
const int maxn = 5e3+5;
int readint(){
	int x=0,f=1;char s=getchar();
	#define sc (s=getchar())
	while(s<'0'||s>'9'){
		if(s=='-')
			f=-1;
		sc;
	}
	while(s>='0'&&s<='9'){
		x=(x<<3)+(x<<1)+(s^48);
		sc;
	}
	#undef sc
	return x*f;
}
char s[maxn];
int cnt[maxn],maxsize;
int sa[maxn],rk[maxn];
int x[maxn<<1],y[maxn<<1];
int lcp[maxn][maxn];
int height[maxn];
int dp[maxn];
void get_sa(int n){
	int maxsize=150;
	for(int i=0;i<=maxsize;i++)
		cnt[i]=0;
	for(int i=0;i<=n*2+5;i++)
		x[i]=0,y[i]=0;
	for(int i=1;i<=n;i++){
		x[i]=s[i];
		cnt[x[i]]++; 
	} 
	for(int i=1;i<=maxsize;i++)
		cnt[i]+=cnt[i-1];
	for(int i=n;i>=1;i--)
		sa[cnt[x[i]]--]=i;
	for(int k=1;k<=n;k<<=1){
		int num=0;
		for(int i=n-k+1;i<=n;i++)
			y[++num]=i;
		for(int i=1;i<=n;i++)
			if(sa[i]>k)
				y[++num]=sa[i]-k;
		for(int i=1;i<=maxsize;i++)
			cnt[i]=0;
		for(int i=1;i<=n;i++)
			++cnt[x[i]];
		for(int i=1;i<=maxsize;i++)
			cnt[i]+=cnt[i-1];
		for(int i=n;i>=1;i--)
			sa[cnt[x[y[i]]]--]=y[i],y[i]=0;
		swap(x,y);
		x[sa[1]]=1;num=1;
		for(int i=2;i<=n;i++)
			x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k])?num:++num;
		if(num==n)
			break;
		maxsize=num;
	}
}
void getheight(int n){
	for(int i=1;i<=n;i++)
		rk[sa[i]]=i;
//	cout<<rk[2]<<" "<<rk[3]<<endl;
	int k=0;
	for(int i=1;i<=n;i++){
		if(rk[i]==1)
			continue;
		if(k)k--;
		int j=sa[rk[i]-1];
		while(i+k<=n&&j+k<=n&&s[j+k]==s[i+k]) k++;
		height[rk[i]]=k;
	}
}
void getlcp(int n){
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			lcp[i][j]=0;
	for(int i=1;i<=n;i++){
		lcp[i][i]=n-sa[i]+1;
		for(int j=i+1;j<=n;j++)
			lcp[i][j]=min(lcp[i][j-1],height[j]);		
	}
	for(int i=1;i<=n;i++)
		for(int j=i-1;j;j--)
			lcp[i][j]=lcp[j][i];
}
int main (){
	int t=readint();
	while(t--){
		int n=readint();
		scanf("%s",s+1);
		get_sa(n);
		getheight(n);
		getlcp(n);
		for(int i=1;i<=n;i++)
			dp[i]=0;
		dp[1]=n;	
		for(int i=2;i<=n;i++){
			dp[i]=n-i+1;
			for(int j=1;j<=i-1;j++){
				if(rk[j]>rk[i]||lcp[rk[i]][rk[j]]==n-i+1)
					continue;		
				dp[i]=max(dp[i],dp[j]+n-i+1-lcp[rk[i]][rk[j]]);
			}
		}
		int maxx=0;
		for(int i=1;i<=n;i++){
			maxx=max(dp[i],maxx);
		}
		printf("%d\n",maxx);
	}
	return 0;
}

CF1567

TP

A Domino Disaster

不做评价

#include <bits/stdc++.h>
using namespace std;
const int maxn = 5e3+5;
int readint(){
	int x=0,f=1;char s=getchar();
	#define sc (s=getchar())
	while(s<'0'||s>'9'){
		if(s=='-')
			f=-1;
		sc;
	}
	while(s>='0'&&s<='9'){
		x=(x<<3)+(x<<1)+(s^48);
		sc;
	}
	#undef sc
	return x*f;
}
char s[105];
int main (){
	int t=readint();
	while(t--){
		int n=readint();
		scanf("%s",s+1);
		for(int i=1;i<=n;i++){
			if(s[i]=='U')
				printf("D");
			if(s[i]=='L')
				printf("L");
			if(s[i]=='R')
				printf("R");
			if(s[i]=='D')
				printf("U");
		}
		puts("");
	} 
	return 0;
}

B MEXor Mixup

TP
不做评价

#include <bits/stdc++.h>
using namespace std;
const int maxn = 3e5+5;
int readint(){
	int x=0,f=1;char s=getchar();
	#define sc (s=getchar())
	while(s<'0'||s>'9'){
		if(s=='-')
			f=-1;
		sc;
	}
	while(s>='0'&&s<='9'){
		x=(x<<3)+(x<<1)+(s^48);
		sc;
	}
	#undef sc
	return x*f;
}
int pre[maxn];
int main (){
	int t=readint();
	for(int i=1;i<=3e5;i++)
		pre[i]=pre[i-1]^i;
	while(t--){
		int a=readint(),b=readint();
		int sum=pre[a-1],num=a;
		if(sum==b)
			printf("%d\n",num);
		else if((b^sum)!=a)
			printf("%d\n",num+1);
		else
			printf("%d\n",num+2);
	} 
	return 0;
}

C Carrying Conundrum

TP
考虑到奇数位加到奇数位,偶数位加到偶数位,分开讨论即可

#include <bits/stdc++.h>
using namespace std;
const int maxn = 3e5+5;
int readint(){
	int x=0,f=1;char s=getchar();
	#define sc (s=getchar())
	while(s<'0'||s>'9'){
		if(s=='-')
			f=-1;
		sc;
	}
	while(s>='0'&&s<='9'){
		x=(x<<3)+(x<<1)+(s^48);
		sc;
	}
	#undef sc
	return x*f;
}
int pre[maxn];
char s[15];
int main (){
	int t=readint();
	while(t--){
		scanf("%s",s+1);
		int n=strlen(s+1);
		int len1=0,len2=0;
		for(int i=1;i<=n;i++){
			if(i&1)
				len1=len1*10+(s[i]^48);
			else
				len2=len2*10+(s[i]^48);
		}
		printf("%d\n",(len2+1)*(len1+1)-2);
	}
	
	return 0;
}

D Expression Evaluation Error

TP
显然,保留最高位是最优秀的方案,借位保证个数先向低位借

#include <bits/stdc++.h>
using namespace std;
int readint(){
	int x=0,f=1;char s=getchar();
	#define sc (s=getchar())
	while(s<'0'||s>'9'){
		if(s=='-')
			f=-1;
		sc;
	}
	while(s>='0'&&s<='9'){
		x=(x<<3)+(x<<1)+(s^48);
		sc;
	}
	#undef sc
	return x*f;
}
int getlog10(int num){
	int ans=1;
	for(int i=1;;i++){
		ans*=10;
		if(ans>num)
			return i-1;
	}
}
int main (){
	int t=readint();
	while(t--){
		int s=readint(),n=readint();
		while(1){
			if(n==1){
				printf("%d\n",s);
				break;
			}
			int num=pow(10,getlog10(s-n+1));
			printf("%d ",num);
			s-=num;
			n--;
		}
	}
	return 0;
}/

E Non-Decreasing Dilemma

TP
最开始的想法就是找出每段连续不下降的字串,但是查询和修改跟本解决不了,于是线段树,只需要注意中间拼接的问题,也就是 m e r g e merge merge,刚开始不写结构体 m e r g e merge merge不动,查询直接给我人干麻

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 2e5+5;
int readint(){
	int x=0,f=1;char s=getchar();
	#define sc (s=getchar())
	while(s<'0'||s>'9'){
		if(s=='-')
			f=-1;
		sc;
	}
	while(s>='0'&&s<='9'){
		x=(x<<3)+(x<<1)+(s^48);
		sc;
	}
	#undef sc
	return x*f;
}
struct node{
	int lx,rx,len;
	int suml,sumr;
	LL ans;
}tree[maxn<<2];
node merge(node x,node y){
	node final;
	final.lx=x.lx,final.rx=y.rx;
	final.len=x.len+y.len;
	final.ans=x.ans+y.ans;
	if(x.rx<=y.lx){
		final.ans+=1ll*x.sumr*y.suml;
		if(x.suml==x.len)
			final.suml=x.suml+y.suml;
		else
			final.suml=x.suml;
		if(y.sumr==y.len)
			final.sumr=y.sumr+x.sumr;
		else
			final.sumr=y.sumr;
	}
	else
		final.suml=x.suml,final.sumr=y.sumr;
	return final;
}
void build(int x,int l,int r){
	if(l==r){
		tree[x].lx=tree[x].rx=readint();
		tree[x].len=tree[x].suml=tree[x].sumr=tree[x].ans=1;
		return;
	}
	int mid=(l+r)>>1;
	build(x<<1,l,mid);
	build(x<<1|1,mid+1,r);
	tree[x]=merge(tree[x<<1],tree[x<<1|1]);
}
void update(int x,int l,int r,int pos,int val){
	if(l==r){
		tree[x].lx=tree[x].rx=val;
		return;
	}
	int mid=(l+r)>>1;
	if(pos<=mid)
		update(x<<1,l,mid,pos,val);
	else
		update(x<<1|1,mid+1,r,pos,val);
	tree[x]=merge(tree[x<<1],tree[x<<1|1]);
}
node query(int x,int l,int r,int goal_l,int goal_r){
	if(goal_l<=l&&goal_r>=r)
		return tree[x];
	int mid=(l+r)>>1;
	if(goal_r<=mid)
		return query(x<<1,l,mid,goal_l,goal_r);
	else if(goal_l>mid)
		return query(x<<1|1,mid+1,r,goal_l,goal_r);
	else
		return merge(query(x<<1,l,mid,goal_l,goal_r),query(x<<1|1,mid+1,r,goal_l,goal_r));
}
int main (){
	int n=readint(),q=readint();
	build(1,1,n);
	while(q--){
		int op=readint();
		if(op==1){
			int pos=readint(),val=readint();
			update(1,1,n,pos,val);
		}
		else{
			int l=readint(),r=readint();
			printf("%lld\n",query(1,1,n,l,r).ans);
		}
	}
	return 0;
}

CF1561

A Simply Strange Sort

TP
模拟题,复杂度不会算

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 1e3+5;
int readint(){
	int x=0,f=1;char s=getchar();
	#define sc (s=getchar())
	while(s<'0'||s>'9'){
		if(s=='-')
			f=-1;
		sc;
	}
	while(s>='0'&&s<='9'){
		x=(x<<3)+(x<<1)+(s^48);
		sc;
	}
	#undef sc
	return x*f;
}
int a[maxn];
int main(){
	int t=readint();
	while(t--){
		int n=readint();
		for(int i=1;i<=n;i++)
			a[i]=readint();
		int st=1,i=0;
		while(1){
			bool flag=1;
			for(int j=1;j<=n;j++){
				if(a[j]!=j)
					flag=0;
            }
			if(flag){
				printf("%d\n",i);
				break; 
			}
            st=i%2+1;
			for(int j=st;j<n;j+=2){
				if(a[j]>a[j+1])
					swap(a[j],a[j+1]);
            }
			i++;
		}
	}
	return 0;
}

B. Charmed by the Game

TP
先考虑最下情况,交替胜利且为保守球,后来多出来的单独排序,然后把前面的交替胜利一对一对互换,可以的到所有值,但是如果后面多出来的是奇数,那么要分类讨论哪一个人先发球

#include <bits/stdc++.h>
using namespace std;
int readint(){
	int x=0,f=1;char s=getchar();
	#define sc (s=getchar())
	while(s<'0'||s>'9'){
		if(s=='-')
			f=-1;
		sc;
	}
	while(s>='0'&&s<='9'){
		x=(x<<3)+(x<<1)+(s^48);
		sc;
	}
	#undef sc
	return x*f;
}
int FABS(int x){
	if(x<0)
		x=-x;
	return x;
}
vector<int> g;
int main (){
	int t=readint();
	while(t--){
		int a=readint(),b=readint();
		int delta=FABS(a-b);
		if(a==b)
			g.push_back(0);
		else if(delta==1)
			g.push_back(0),g.push_back(1);
		else{
			if(delta&1)
				g.push_back(delta/2),g.push_back(delta/2+1);
			else
				g.push_back(delta/2);
		}
		if(a<b)
			swap(a,b);
		for(int i=1;i<=b;i++){
			if(delta&1)
				g.push_back(delta/2+i*2),g.push_back(i*2+delta/2+1);
			else
				g.push_back(delta/2+i*2);
		}
		printf("%d\n",(int)g.size());
		for(int i=0;i<g.size();i++){
			printf("%d ",g[i]);
		}
		g.clear();
		puts("");
	}
	return 0;
}

C Deep Down Below

TP
排序即可

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+5;
typedef long long LL;
int readint(){
	int x=0,f=1;char s=getchar();
	#define sc (s=getchar())
	while(s<'0'||s>'9'){
		if(s=='-')
			f=-1;
		sc;
	}
	while(s>='0'&&s<='9'){
		x=(x<<3)+(x<<1)+(s^48);
		sc;
	}
	#undef sc
	return x*f;
}
int FABS(int x){
	if(x<0)
		x=-x;
	return x;
}
int delta[maxn],ind[maxn];
int k[maxn];
bool cmp(int x,int y){
	return delta[x]<delta[y];
}
int main (){
	int t=readint();
	while(t--){
		memset(delta,0,sizeof(delta));
		int n=readint();
		for(int i=1;i<=n;i++){
			ind[i]=i;
			k[i]=readint();
			int a=readint(),ans;
			delta[i]=ans=a+1;
			for(int j=2;j<=k[i];j++){
				ans++;
				a=readint();
				if(a>=ans){
					int t=ans;
					ans=a+1;
					delta[i]+=(ans-t);
				}
			}
		}
		sort(ind+1,ind+n+1,cmp);
		int sum,need;
		sum=need=delta[ind[1]];
		for(int i=2;i<=n;i++){
			sum+=k[ind[i-1]];
			if(sum<delta[ind[i]]){
				need+=(delta[ind[i]]-sum);
				sum+=(delta[ind[i]]-sum);
			}
		}
		printf("%d\n",need);
	}
	return 0;
}

D Up the Strip

d p [ i ] dp[i] dp[i]为改到i的方案数
通过减法转移的 d p [ i + 1 ] → d p [ n ] dp[i+1]\to dp[n] dp[i+1]dp[n]
假设通过除 a a a得到的 d p [ i ∗ a ] → d p [ i ∗ a + a − 1 ] dp[i*a]\to dp[i*a+a-1] dp[ia]dp[ia+a1]
调和级数 + + +后缀和

#include <bits/stdc++.h>
using namespace std;
const int maxn = 4e6+5;
typedef long long LL;
int readint(){
	int x=0,f=1;char s=getchar();
	#define sc (s=getchar())
	while(s<'0'||s>'9'){
		if(s=='-')
			f=-1;
		sc;
	}
	while(s>='0'&&s<='9'){
		x=(x<<3)+(x<<1)+(s^48);
		sc;
	}
	#undef sc
	return x*f;
}
int FABS(int x){
	if(x<0)
		x=-x;
	return x;
}
int dp[maxn],suf[maxn]; 
int main (){
	int n=readint(),mo=readint();
	dp[n]=suf[n]=1;
	for(int i=n-1;i>=1;i--){
		dp[i]=suf[i+1];
		for(int j=2;j*i<=n;j++){
			dp[i]=(0ll+dp[i]+suf[j*i]-suf[min(j*i+j,n+1)])%mo;
			dp[i]=(dp[i]+mo)%mo;
		}
		suf[i]=(0ll+suf[i+1]+dp[i])%mo;
//		cout<<dp[i]<<endl;
	}
	printf("%d\n",dp[1]);
	return 0;
}

E. Bottom-Tier Reversals

TP
因为每次翻转的个数为奇数,那么翻转后位置的奇偶无法改变
如果奇数在偶数位上,或者偶数在奇数位上那就一辈子坐牢
考虑两两一组,将从小的大的翻转,只不过要倒序,才能保证过后的翻转不会有影响,具体看实现吧

#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e3+50;
int readint(){
	int x=0,f=1;char s=getchar();
	#define sc (s=getchar())
	while(s<'0'||s>'9'){
		if(s=='-')
			f=-1;
		sc;
	}
	while(s>='0'&&s<='9'){
		x=(x<<3)+(x<<1)+(s^48);
		sc;
	}
	#undef sc
	return x*f;
}
int a[maxn],ans[maxn<<2],cnt;
int FABS(int x){
	if(x<0)
		x=-x;
	return x;
}
int find(int n,int x){
	for(int i=1;i<=n;i++)
		if(a[i]==x)
			return i;
}
void change(int x){
	for(int i=1,j=x;i<=x/2;i++,j--)
		swap(a[i],a[j]);
	ans[++cnt]=x;
}
int main(){
	int t=readint();
//	cout<<t<<endl;
	while(t--){
		int n=readint();
		cnt=0;
		bool flag=0;
		for(int i=1;i<=n;i++)
			a[i]=readint();
		for(int i=1;i<=n;i++)
			if(FABS(a[i]-i)%2==1){
				puts("-1");
				flag=1;
				break;
			}
		if(flag)
			continue;
		for(int i=1;i<=n/2;i++){
			int pos1=find(n,i*2-1);
			change(pos1);
			int pos2=find(n,i*2);
			change(pos2-1);
			change(pos2+1);
			change(3);
			change(n-i*2+2);
		}
		change(n);
		printf("%d\n",cnt);
		for(int i=1;i<=cnt;i++){
			printf("%d ",ans[i]);
		}
		puts("");
	}
	return 0;
}

1560

D Make a Power of Two

TP
搞清楚右边加数位的可以是任何数就可以了

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int readint(){
	int x=0,f=1;char s=getchar();
	#define sc (s=getchar())
	while(s<'0'||s>'9'){
		if(s=='-')
			f=-1;
		sc;
	}
	while(s>='0'&&s<='9'){
		x=(x<<3)+(x<<1)+(s^48);
		sc;
	}
	#undef sc
	return x*f;
}
char c[25];
struct zz{
	char s[25];
	int len;
};
vector<zz>g;
void prepare(){
	LL num=1;
	zz t;
	t.s[1]='1';
	t.len=1;
	g.push_back(t);
	for(int i=1;i<=60;i++){
		num<<=1ll;
		LL tot=num;
		t.len=0;
		while(tot){
			t.s[++t.len]=48^(tot%10);
			tot/=10;
		}
		reverse(t.s+1,t.s+t.len+1);
		g.push_back(t);
	}
}
int main (){
	int t=readint();
	prepare();
//	for(int i=0;i<g.size();i++){
//		for(int j=1;j<=g[i].len;j++){
//			printf("%c",g[i].s[j]);
//		}
//		puts("");
//	}
	while(t--){
		scanf("%s",c+1);
		int n=strlen(c+1);
		int ans=0x3f3f3f3f;
		for(int i=0;i<g.size();i++){
        	int k=1;
        	for(int j=1;j<=n;j++)
            	if(k<=g[i].len&&c[j]==g[i].s[k])
					++k;
			k--;
	        ans=min(ans,n+g[i].len-k-k);
	    }
	    printf("%d\n",ans);
	}
	return 0;
}

E Polycarp and String Transformation

TP
口胡一个题
可以统计每个字母的最后出现的位置,然后排序得到他是第几个删除的,然后第 i i i轮删除的字母的出现个数一定是 i i i的倍数,逆向得到开头初串,然后模拟判可行性

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=100005;
const int inf=1<<30;
const ll inff=1ll<<60;
inline int read(){
	int x=0,f=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
	return x*f;
}
string s,s1,s2,s3;
int l,f[35],ans[35],cnt;
void solve(){
	cin>>s;
	int sz=s.size();
	for(int i=0;i<26;i++)f[i]=0;
	cnt=0;
	for(int i=sz-1;i>=0;i--){//倒序处理,得到第二问的解
	    if(!f[s[i]-'a'])ans[++cnt]=s[i]-'a';
	    f[s[i]-'a']++;
	}
    //注意我们求出来的第二问的解实际上也是倒序的,
    //但是由于我比较懒就没有数组翻转了
	l=0;
	for(int i=1;i<=cnt;i++)
	    if(f[ans[i]]%(cnt-i+1)!=0){//cnt-i+1即ans[i]出现的轮数
	    	puts("-1");//这个解已经不合法了
			return;
	    }
	    else l+=f[ans[i]]/(cnt-i+1);
        //求出初始串的长度l
	s1="",s2="";
	for(int i=0;i<l;i++)s1+=s[i];
	int now=cnt;
    //接下来是模拟,检验解的合法性
    //s1为当前的字符串,s2为字符串对应的串t,s3用来更新s1
	while(s1!=""){
		s2+=s1;
		s3="";
		sz=s1.size();
		for(int i=0;i<sz;i++)
		    if(ans[now]!=s1[i]-'a')
				s3+=s1[i];
		s1=s3;
		now--;
	}
	//cout<<s2<<'\n';
	if(s2==s){//如果检验得到的串t与给出的串t相同,那么这个解是合法的
		for(int i=0;i<l;i++)cout<<s[i];
		cout<<' ';
		for(int i=cnt;i;i--)putchar(ans[i]+'a');
		puts("");
	}
	else puts("-1");//解不合法,输出-1
}
int main(){int tests=1;tests=read();
while(tests--){
	solve();
}	return 0;
}

F2 Nearest Beautiful Number (hard version)

TP
这题挺水的,觉得不值 2100 2100 2100,显然,要让的到的数最小,必须让他前缀相同的越多,所以用 s e t set set不断塞数,如果大于可以用的数字,就向前找离他最近的那位不是 9 9 9的数位 + + ++ ++,然后将 n n n更新,因为已经知道那其间已经没有满足要求的数了,向上紧逼

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+5;
int readint(){
	int x=0,f=1;char s=getchar();
	#define sc (s=getchar())
	while(s<'0'||s>'9'){
		if(s=='-')
			f=-1;
		sc;
	}
	while(s>='0'&&s<='9'){
		x=(x<<3)+(x<<1)+(s^48);
		sc;
	}
	#undef sc
	return x*f;
}
char s[15];
int main (){
	int t=readint();
	while(t--){
		scanf("%s",s+1);
		int k=readint();
		int n=strlen(s+1);
		while(1){
			set<char> st;
			st.clear();
			for(int i=1;i<=n;i++)
				st.insert(s[i]);
			if(st.size()<=k){
				for(int i=1;i<=n;i++)
					printf("%c",s[i]);
				puts("");
				break;
			}
			st.clear();
			int pos=1;
			while(pos<=n){
				st.insert(s[pos]);
				if(st.size()>k){
					while(s[pos]=='9')
						pos--;
					s[pos]++;
					for(int i=pos+1;i<=n;i++)
						s[i]='0';
					break;
				}
				pos++;
			}
		}
	}
	return 0;
}

1559

A Mocha and Math

TP
数学题还是一个废物,考虑一个数位取和不能增加这一位为 1 1 1的数字的个数,所以每个数字的都为 1 1 1的数位才能保留,这有意思

#include <bits/stdc++.h>
using namespace std;
int readint(){
	int x=0,f=1;char s=getchar();
	#define sc (s=getchar())
	while(s<'0'||s>'9'){
		if(s=='0')
			f=-1;
		sc;
	}
	while(s>='0'&&s<='9'){
		x=(x<<3)+(x<<1)+(s^48);
		sc;
	}
	#undef sc
	return x*f;
}
int main (){
	int t=readint();
	while(t--){
		int n=readint();
		int ans=readint();
		for(int i=2;i<=n;i++){
			ans&=readint();
		}
		printf("%d\n",ans);
	}
	return 0;
} 

B Mocha and Red and Blue

TP
d p dp dp写麻了,我定义的是 d p [ i ] [ 0 / 1 ] [ 0 / 1 ] dp[i][0/1][0/1] dp[i][0/1][0/1]当前位置为 B / R B/R B/R的情况下最大串的最小值,以及当前连续位置串长度的最小值
哦,伞兵了,突然发现不是最长连续段,是指和前面相同的个数有多少个,自己想复杂了, m d md md
看看这俩兄弟的吧
模拟版
dp版

C Mocha and Hiking

TP
细想发现有三种情况可以完成整个路程
n + 1 n+1 n+1号城市可通向 1 1 1 号城市。
n n n 号城市可通向 n + 1 n + 1 n+1 号城市。
某座编号 k k k的城市能通向 n + 1 n+1 n+1 号城市,且 n + 1 n + 1 n+1 号城市能通向 k + 1 k + 1 k+1 号城市。

#include<iostream>
#include<cstdio>
using namespace std;
int t,n;
int a[10005];
bool qwq;
int main(){
	cin>>t;
	for(int ii=1;ii<=t;ii++){
		qwq=0;
		cin>>n;
		for(int i=1;i<=n;i++)
			cin>>a[i];
		if(a[1]==1){
			cout<<n+1<<" ";
			for(int i=1;i<=n;i++)
				cout<<i<<" ";
			cout<<"\n";
			continue;
		}
		if(a[n]==0){
			for(int i=1;i<=n+1;i++)
				cout<<i<<" ";
			cout<<"\n";
			continue;
		}
		for(int i=1;i<=n-1;i++)
			if(a[i]==0&&a[i+1]==1){
				for(int j=1;j<=i;j++)
					cout<<j<<" ";
				cout<<n+1<<" ";
				for(int j=i+1;j<=n;j++)
					cout<<j<<" ";
				cout<<"\n";
				qwq=1;
				break;	
			}
		if(qwq==0)
			cout<<"-1\n";
	}
	return 0;
}

D Mocha and Diana (Hard Version)

TP
真牛逼,说实话不看题解把我卖了都想不到
先证明小的那棵树连通块的个数一定为 1 1 1
设小的为 A A A,大的为 B B B
假设 A A A如今已经不能操作了,任意条边相连都会影响 B B B图为树
A A A中其中有两个树 x , y x,y x,y
由于不能再连边了,所以两颗树的任何节点在另一个森林中都可以联通
所以其中有一个连通块一定为 1 1 1
由此贪心分析可知,随意加可行边即可。
考虑优化贪心。

考虑一个中心点 s s s
我们先让所有点与 s s s 尝试连边。
然后连完后令 A A A 图中与 s s s 不连通的点集为 L L L B B B 图中与 s s s 不连通的点集为 R R R
显然 L ∩ R = ∅ L\cap R=\varnothing LR=
考虑 l ∈ L l\in L lL r ∈ R r\in R rR
由定义有 A 图中 l l l s s s 不连通, r r r s s s 连通, B B B 图相反。
那么任意 l l l r r r 都可连边。
然后只要随意配对完 L L L R R R 就行了,此时一幅图变成一个连通块。
时间复杂度 O ( n α ( n ) ) O(n\alpha(n)) O(nα(n))

E Mocha and Stars

TP
如果不考虑 g c d gcd gcd的话,就是一个裸的背包,考虑 g c d gcd gcd后可以考虑一个容斥
在处理 gcd ⁡ \gcd gcd x x x 的问题时,可以倒着枚举一个 i i i,设 a n s i ans_i ansi gcd ⁡ \gcd gcd 恰好为 i i i 的方案数,
f i f_i fi gcd ⁡ \gcd gcd i i i 的倍数的方案数。
那么就可以用 a n s g = f g − ∑ 2 m / g a n s i ∗ g ans_g = f_g - \sum_{2}^{m/g} ans_{i*g} ansg=fg2m/gansig 来求解。
很好的博客

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+5;
const int mo = 998244353;
int readint(){
   int x=0,f=1;char s=getchar();
   #define sc (s=getchar())
   while(s<'0'||s>'9'){
   	if(s=='-')
   		f=-1;
   	sc;
   }
   while(s>='0'&&s<='9'){
   	x=(x<<3)+(x<<1)+(s^48);
   	sc;
   }
   #undef sc
   return x*f;
}
int dp[maxn],pre[maxn],upd[maxn];//dp,前缀和,修改后的一些值 
int f[maxn],ans[maxn];
int l[55],r[55];
int main (){
   int n=readint(),m=readint();
   for(int i=1;i<=n;i++)
   	l[i]=readint(),r[i]=readint();
   for(int d=m;d>=1;d--){
   	for(int i=0;i<=m/d;i++)	
   		dp[i]=0,pre[i]=0,upd[i]=0;
   	pre[0]=upd[0]=1;
   	for(int j=1;j<=m/d;j++)
   		pre[j]=(0ll+pre[j-1]+upd[j])%mo;
   	for(int i=1;i<=n;i++){
   		for(int j=m/d;j>=ceil(l[i]*1.0/d);j--){
   			if(j>r[i]/d)
   				dp[j]=(0ll+pre[j-(int)ceil(l[i]*1.0/d)]-pre[j-r[i]/d-1]+mo)%mo;
   			else
   				dp[j]=pre[j-(int)ceil(l[i]*1.0/d)];
   		}
   		for(int j=0;j<=m/d;j++)
   			upd[j]=dp[j],dp[j]=0;
   		pre[0]=upd[0];
   		for(int j=1;j<=m/d;j++)
   			pre[j]=(0ll+pre[j-1]+upd[j])%mo;
   	}
   	for(int i=0;i<=m/d;i++)
   		f[d]=(0ll+upd[i]+f[d])%mo;
   	ans[d]=f[d];
   	for(int i=d+d;i<=m;i+=d)
   		ans[d]=(0ll+ans[d]-ans[i]+mo)%mo;
   }
   printf("%d\n",ans[1]);
   return 0;
}

1557

A Ezzat and Two Subsequences

TP

#include<bits/stdc++.h>
using namespace std;
int main(){
   int t; cin>>t;
   while(t--){
   	double n; cin>>n;
   	int maxx=-0x3f3f3f3f;
   	double sum=0;
   	int k=n;
   	while(k--){
   		int x;
   		cin>>x;
   		sum+=x;
   		maxx=max(x,maxx);//更新最大值
   	}
   	printf("%0.9lf\n",(sum-maxx)/(n-1)+maxx);
   }
   return 0;//好习惯
}

B Moamen and k-subarrays

刚开始没看到数字都不相同
TP

#include<bits/stdc++.h>
using namespace std;
int t;
int n,k;
struct node {
   int name,x;
}a[100005];
bool cmp(node a,node b) {
   return a.x<b.x;
}
int main() {
   cin>>t;
   while(t--) {
       cin>>n>>k;
       for(int i=1;i<=n;i++)cin>>a[i].x,a[i].name=i;
       sort(a+1,a+n+1,cmp);
       int cnt=1;
       for(int i=2;i<=n;i++)
           if(a[i].name!=a[i-1].name+1)cnt++;
       if(cnt>k)cout<<"No"<<endl;
       else cout<<"Yes"<<endl;
   }
}

C Moamen and XOR

TP
考虑到 n n n为奇数时只能等于即可

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define int LL
const int maxn = 2e5+5;
const int mo = 1e9+7;
int readint(){
   int x=0,f=1;char s=getchar();
   #define sc (s=getchar())
   while(s<'0'||s>'9'){
   	if(s=='-')
   		f=-1;
   	sc;
   }
   while(s>='0'&&s<='9'){
   	x=(x<<3)+(x<<1)+(s^48);
   	sc;
   }
   #undef sc
   return x*f;
}
int fac[maxn],inv[maxn];
void init(int n){
   fac[0]=inv[0]=inv[1]=1;
   for(int i=1;i<=n;i++)
   	fac[i]=1ll*fac[i-1]*i%mo;
   for(int i=2;i<=n;i++)
   	inv[i]=1ll*(mo-mo/i)*inv[mo%i]%mo;
   for(int i=2;i<=n;i++)
   	inv[i]=1ll*inv[i-1]*inv[i]%mo;
}
int C(int n,int m){
   if(n<=0||m<=0||n<m)
   	return 1;
   return 1ll*fac[n]*inv[n-m]%mo*inv[m]%mo;
}
int qkpow(int a,int b){
   int ans=1;
   while(b){
   	if(b&1)
   		ans=1ll*ans*a%mo;
   	b>>=1;
   	a=1ll*a*a%mo;
   }
   return ans%mo;
}
signed main (){
   init(maxn-5);
   int t=readint();
   while(t--){
   	int n=readint(),k=readint();
   	int sum=0,ans=0;
   	for(int i=0;i<n;i+=2){//ijһλÏàµÈµÄ·½°¸Êý 
   		sum=(0ll+sum+C(n,i))%mo;
   	}
//		cout<<sum<<endl;
   	if(n&1)
   		ans=(0ll+ans+qkpow((sum+1)%mo,k))%mo;
   	else{
   		for(int i=0;i<k;i++)
   			ans=(0ll+ans+qkpow(sum,i)%mo*qkpow(qkpow(2,n),k-i-1)%mo)%mo;
   		ans=(0ll+ans+qkpow(sum,k))%mo;
   	}
   	printf("%d\n",ans);
   }
   return 0;
}

1549

A Gregor and Cryptography

TP

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 2e5+5;
const int mo = 1e9+7;
int readint(){
   int x=0,f=1;char s=getchar();
   #define sc (s=getchar())
   while(s<'0'||s>'9'){
   	if(s=='-')
   		f=-1;
   	sc;
   }
   while(s>='0'&&s<='9'){
   	x=(x<<3)+(x<<1)+(s^48);
   	sc;
   }
   #undef sc
   return x*f;
}
signed main (){
   int t=readint();
   while(t--){
   	int p=readint();
   	printf("%d %d\n",p/2,p/2*2);
   } 
   return 0;
}

B Gregor and the Pawn Game

TP

#include<bits/stdc++.h>
using namespace std;
int t,n;
string s1,s2;
int ans1[200005],ans2[200005];
bool vis[200005];
int main(){
   cin>>t;
   while(t--){
   	memset(vis,0,sizeof(vis));//多测不清空,保龄两行泪
   	cin>>n;
   	cin>>s1>>s2;
   	int num=0;
   	for(int i=1;i<=n;i++){
   		ans1[i]=s1[i-1]-'0';
   		ans2[i]=s2[i-1]-'0';
   	}
   	for(int i=1;i<=n;i++){
   		if(ans1[i]==0&&!vis[i]&&ans2[i]==1){
   			num++;
   			vis[i]=1;
   		}
   		else if(i!=1&&ans1[i-1]==1&&ans1[i]==1&&!vis[i-1]&&ans2[i]==1){
   			num++;
   			vis[i-1]=1;//被友军走了
   		} 
           /*
           当然,第一个和最后一个因为地理原因要特殊考虑
           */
   		else if(i!=n&&ans1[i+1]==1&&ans1[i]==1&&!vis[i+1]&&ans2[i]==1){
   			num++;
   			vis[i+1]=1;
   		}
   	}
   	cout<<num<<'\n';
   } 
   return 0;
}

Web of Lies

TP
易得与周围邻居相比最小便可以留下来

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;

long long n,m,t,out[200050],ans;

int main(){
   long long i,j,u,v;
   cin>>n>>m;
   ans=n;//一开始都能活
   while(m--){
       cin>>u>>v;
       if(u>v) swap(u,v);
       if(out[u]==0) ans--;
       out[u]++;
   }
   cin>>t;
   while(t--){
       cin>>j;
       if(j==1){
           cin>>u>>v;
           if(u>v) swap(u,v);
           if(out[u]==0) ans--;
           out[u]++;
       }
       if(j==2){
           cin>>u>>v;
           if(u>v) swap(u,v);
           out[u]--;
           if(out[u]==0) ans++;
       }
       if(j==3){
           cout<<ans<<endl;
       }
   }
   return 0;
}

D Integers Have Friends

TP
设一连串的数除数为 m m m,余数为 b b b
a i = k i ∗ m + b a_i=k_i*m+b ai=kim+b
a i + 1 − a i = m ∗ ( k i + 1 − k i ) a_{i+1}-a_i=m*(k_{i+1}-k_i) ai+1ai=m(ki+1ki)
发现差的绝对值求 g c d gcd gcd便是所除的 m m m
得到新的数组 b i = a i + 1 − a i b_i=a_{i+1}-a_i bi=ai+1ai,求最长区间的 g c d gcd gcd大于一即可
建议写 s t st st表,不然容易 T T T,毕竟 O ( n ∗ l o g 2 n ) O(n*log^2n) O(nlog2n)

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 2e5+5;
LL readLL(){
   LL x=0,f=1;char s=getchar();
   #define sc (s=getchar())
   while(s<'0'||s>'9'){
   	if(s=='-')
   		f=-1;
   	sc;
   }
   while(s>='0'&&s<='9'){
   	x=(x<<3ll)+(x<<1ll)+(s^48);
   	sc;
   }
   #undef sc
   return x*f;
}
int readint(){
   int x=0,f=1;char s=getchar();
   #define sc (s=getchar())
   while(s<'0'||s>'9'){
   	if(s=='-')
   		f=-1;
   	sc;
   }
   while(s>='0'&&s<='9'){
   	x=(x<<3)+(x<<1)+(s^48);
   	sc;
   }
   #undef sc
   return x*f;
}
LL FABS(LL x){
   if(x<0)
   	x=-x;
   return x;
}
LL gcd(LL a,LL b){
   if(!b)
   	return a;
   return gcd(b,a%b);
}
LL st[maxn][20],a[maxn],b[maxn];
void prepare(int n){
   for(int i=1;i<n;i++)
   	st[i][0]=FABS(a[i+1]-a[i]);
   for(int j=1;j<=19;j++){
   	for(int i=1;i+(1<<(j-1))-1<n;i++)
   		st[i][j]=gcd(st[i][j-1],st[i+(1<<(j-1))][j-1]);
   }
}
LL query(int x,int l,int r){
   int k=log2(r-l+1);
   return gcd(st[l][k],st[r-(1<<k)+1][k]);
}
bool check(int n,int len){
   for(int i=1;i<=n-len+1;i++){
   	if(query(1,i,i+len-2)>1)
   		return 1;
   }
   return 0;
}
signed main (){
   int t=readint();
   while(t--){
//		memset(tree,0,sizeof(tree));
//		memset(a,0,sizeof(a));
   	int n=readint();
   	for(int i=1;i<=n;i++)
   		a[i]=readLL();
   	if(n==1){
   		printf("1\n");
   		continue;
   	}
   	prepare(n);
   	int l=2,r=n,ans=1;
   	while(l<=r){
   		int mid=(l+r)>>1;
   		if(check(n,mid)){
   			ans=mid;
   			l=mid+1;
   		}
   		else
   			r=mid-1;
   	}
   	printf("%d\n",ans);
   } 
   return 0;
}

CF1555

D Say No to Palindromes

TP
可以尝试模拟一下: a a a 不回文,尝试添加一个字符,可以添加 b b b 或者 c c c, 这里选择添加 b b b, 于是字符串变成了 a b ab ab; 再次尝试添加字符,发现只能添加 c c c,于是字符串变成了 a b c abc abc; 再次尝试添加字符,发现只能添加 a a a,于是字符串变成了 a b c a abca abca;再次尝试添加字符,发现只能添加 b,于是字符串变成了 abcab;再次尝试添加字符,发现只能添加 c,于是字符串变成了 a b c a b c abcabc abcabc
发现不回文的字符串只能使一个以前三个字符组成的循环,而且前三种字符只有 A 3 3 A_3^3 A33
种排列方式。
对于每次询问,可以枚举这 6 6 6 种排列方式,取最小值即可。

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int n,m;
char a[N],s[7][3]={{'a','b','c'},{'a','c','b'},{'b','a','c'},{'b','c','a'},{'c','a','b'},{'c','b','a'}};
int sum[N][7];
int main(){
	scanf("%d%d",&n,&m);
	scanf("%s",a+1);
	for(int k=0;k<=6;k++){
		int now=0;
		for(int i=1;i<=n;i++,now++){
			if(now==3) now=0;
			if(a[i]!=s[k][now]) sum[i][k]++;
			sum[i][k]+=sum[i-1][k];
		}//六种情况的前缀和
	}
	for(int i=1,l,r;i<=m;i++){
		scanf("%d%d",&l,&r);
		int ans=1e9;
		for(int k=0;k<=6;k++){
			ans=min(ans,sum[r][k]-sum[l-1][k]);
		}//选取代价最小的那一个
		printf("%d\n",ans);
	}
	return 0;
}

E Boring Segments

TP
双指针维护上下界即可

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6+5;
int readint(){
	int x=0,f=1;char s=getchar();
	#define sc (s=getchar())
	while(s<'0'||s>'9'){
		if(s=='-')
			f=-1;
		sc;
	}
	while(s>='0'&&s<='9'){
		x=(x<<3)+(x<<1)+(s^48);
		sc;
	}
	#undef sc
	return x*f;
}
int tree[maxn<<2],lazy[maxn<<2];
struct zz{
	int l,r,w;
}s[maxn];
bool cmp(const zz &x,const zz &y){
	return x.w<y.w;
}
void pushdown(int x){
	if(lazy[x]!=0){
		lazy[x<<1]+=lazy[x];
		tree[x<<1]+=lazy[x];
		lazy[x<<1|1]+=lazy[x];
		tree[x<<1|1]+=lazy[x];
		lazy[x]=0;
	}
}
void update(int x,int l,int r,int goal_l,int goal_r,int val){
	if(goal_l<=l&&goal_r>=r){
		lazy[x]+=val;
		tree[x]+=val;
		return;
	} 
	pushdown(x);
	int mid=(l+r)>>1;
	if(goal_l<=mid)
		update(x<<1,l,mid,goal_l,goal_r,val);
	if(goal_r>mid)
		update(x<<1|1,mid+1,r,goal_l,goal_r,val);
	tree[x]=min(tree[x<<1],tree[x<<1|1]);
}
int main (){
	int n=readint(),m=readint()-1;
	for(int i=1;i<=n;i++){
		s[i].l=readint(),s[i].r=readint()-1,s[i].w=readint();
	}
	sort(s+1,s+n+1,cmp);
	int L=0,R=0,ans=0x3f3f3f3f;
	while(1){
		while(R+1<=n&&tree[1]==0)
			R++,update(1,1,m,s[R].l,s[R].r,1);
		if(tree[1]==0)
			break;
		while(L+1<=R&&tree[1]>0)
			L++,update(1,1,m,s[L].l,s[L].r,-1);
//		cout<<"ans:"<<L<<" "<<R<<endl;
		ans=min(ans,s[R].w-s[L].w);
	}
	cout<<ans;
	return 0;
} 

F Good Graph

TP
因为图中每个环的权值异或和为 1 1 1,所以由两个环拼成的环的异或和就是 0 0 0,不合法。因此,合法的图中每条边一定至多属于一个环。
怎么维护这个东西呢?我们只维护一颗树就好了,加入的一条边如果和树上的一条路径形成环,我们就把这条路经上的边都打上标记,在这之前要进行判断,如果边两端之间的路径上已经存在标记或形成的环的异或和不为 1 1 1,则不合法。如果新加入的边两端点本来不连通,直接加入即可。
这是一个动态维护的,显然可以用 L C T LCT LCT
像我这种比较菜的就离线下来写树剖

#include <bits/stdc++.h>
using namespace std;
const int maxm = 5e5+5;
const int maxn = 3e5+5;
int readint(){
	int x=0,f=1;char s=getchar();
	#define sc (s=getchar())
	while(s<'0'||s>'9'){
		if(s=='-')
			f=-1;
		sc;
	}
	while(s>='0'&&s<='9'){
		x=(x<<3)+(x<<1)+(s^48);
		sc;
	}
	#undef sc
	return x*f;
} 
struct dsu{
	int f[maxn];
	void makeset(int n){
		for(int i=1;i<=n;i++)
			f[i]=i;
	}
	int findset(int x){
		if(x!=f[x])
			f[x]=findset(f[x]);
		return f[x];
	}
	int merge(int x,int y){
		int fx=findset(x),fy=findset(y);
		if(fx==fy)
			return 0;
		f[fx]=fy;
		return 1;
	}
}a;
struct zz{
	int x,y,w;
	int type;
}s[maxm];
struct edge{
	int v,w,nxt;
	edge(){};
	edge(int V,int W,int N){
		v=V;
		w=W;
		nxt=N;
	}
}e[maxn<<1];
int head[maxn],edge_cnt=-1;
void addedge(int u,int v,int w){
	e[++edge_cnt]=edge(v,w,head[u]);
	head[u]=edge_cnt;
	e[++edge_cnt]=edge(u,w,head[v]);
	head[v]=edge_cnt;
}
int dep[maxn],xor_[maxn],son[maxn],siz[maxn];
int fa[maxn];
void dfs1(int u,int pre){
	siz[u]=1;dep[u]=dep[pre]+1;
	fa[u]=pre;
	for(int i=head[u];~i;i=e[i].nxt){
		int v=e[i].v;
		if(v==pre)
			continue;
		xor_[v]=xor_[u]^e[i].w;
		dfs1(v,u);
		siz[u]+=siz[v];
		if(siz[v]>siz[son[u]])
			son[u]=v;
	}
}
int dfn[maxn],rdfn[maxn],dfn_cnt,top[maxn];
void dfs2(int u,int anc){
	top[u]=anc;
	dfn[u]=++dfn_cnt;
	rdfn[dfn_cnt]=u;
	if(son[u])
		dfs2(son[u],anc);
	for(int i=head[u];~i;i=e[i].nxt){
		int v=e[i].v;
		if(v==fa[u]||v==son[u])
			continue;
		dfs2(v,v);
	}
}
int tree[maxn<<2],lazy[maxn<<2];
void pushdown(int x){
	if(lazy[x]){
		lazy[x<<1]=lazy[x];
		lazy[x<<1|1]=lazy[x];
		tree[x<<1]=lazy[x];
		tree[x<<1|1]=lazy[x];
		lazy[x]=0;
	}
}
void update(int x,int l,int r,int goal_l,int goal_r){
	if(goal_l<=l&&goal_r>=r){
		tree[x]=1;
		lazy[x]=1;
		return;
	} 
	pushdown(x);
	int mid=(l+r)>>1;
	if(goal_l<=mid)
		update(x<<1,l,mid,goal_l,goal_r);
	if(goal_r>mid)
		update(x<<1|1,mid+1,r,goal_l,goal_r);
	tree[x]=max(tree[x<<1],tree[x<<1|1]);
}
int query(int x,int l,int r,int goal_l,int goal_r){
	if(l>=goal_l&&goal_r>=r)
		return tree[x];
	pushdown(x);
	int mid=(l+r)>>1,ans=0;
	if(goal_l<=mid)
		ans=max(ans,query(x<<1,l,mid,goal_l,goal_r));
	if(goal_r>mid)
		ans=max(ans,query(x<<1|1,mid+1,r,goal_l,goal_r));
	tree[x]=max(tree[x<<1],tree[x<<1|1]);
	return ans;
}
bool findcircle(int x,int y){
	int tx=top[x],ty=top[y];
	while(tx!=ty){
		if(dep[tx]<dep[ty])
			swap(x,y),swap(tx,ty);
		if(query(1,1,dfn_cnt,dfn[tx],dfn[x])>0)
			return 1;
		x=fa[tx];
		tx=top[x];
	}
	if(dep[x]>dep[y])
		swap(x,y);
	if(query(1,1,dfn_cnt,dfn[x]+1,dfn[y])>0)
		return 1;
	return 0;
}
void modify(int x,int y){
	int tx=top[x],ty=top[y];
	while(tx!=ty){
		if(dep[tx]<dep[ty])
			swap(tx,ty),swap(x,y);
		update(1,1,dfn_cnt,dfn[tx],dfn[x]);
		x=fa[tx];
		tx=top[x];
	}
	if(x==y)	
		return;
	if(dep[x]>dep[y])
		swap(x,y);
	update(1,1,dfn_cnt,dfn[x]+1,dfn[y]);
}
int main (){
	memset(head,-1,sizeof(head));
	int n=readint(),m=readint();
	for(int i=1;i<=m;i++)
		s[i].x=readint(),s[i].y=readint(),s[i].w=readint();
	a.makeset(n);
	for(int i=1;i<=m;i++){
		int u=s[i].x,v=s[i].y;
		if(a.merge(u,v))
			addedge(u,v,s[i].w),s[i].type=1;
	}
	for(int i=1;i<=n;i++){
		if(!dfn[i])
			dfs1(i,0),dfs2(i,i);
	}
	for(int i=1;i<=m;i++){
		if(!s[i].type){
			int u=s[i].x,v=s[i].y;
			bool flag=0;
			if((xor_[u]^xor_[v]^s[i].w)==0)
				continue;
			if(findcircle(u,v))
				flag=1;
			if(flag)
				continue;
			modify(u,v);
			s[i].type=1;
		}
	}
	int sum=0;
	for(int i=1;i<=m;i++)
		if(s[i].type)
			puts("YES");
		else
			puts("NO");
	return 0;
}

CF1554

A Cherry

TP
首先,我们可以考虑设 a i a_i ai为我们选的最优序列里的最大值。
接下来要确定最小值。
每次加进一个数,只能使最小值不变或者令它更小。
所以,我们考虑只加入它的相邻的一个。
也就是说,最优序列肯定是由两个相邻的数组成的。

C Mikasa

TP
由于不可能一个一个枚举,所以这道题要用到异或的一些性质。设 i ∈ { 0 , 1 , … , m } i\in \{0,1,\ldots ,m \} i{0,1,,m},与其让 n n n 去一个一个异或 i i i,不如构造一个答案,让这个答案不属于这个集合。
a ⊕ b = c a\oplus b=c ab=c ⇒ a ⊕ c = b \Rightarrow a\oplus c=b ac=b
根据这个性质,为了使 n ⊕ i ≠ k n\oplus i \ne k ni=k
其实只要构造出一个 k k k,使得 n ⊕ k ∉ { 0 , 1 , … , m } n\oplus k \notin \{0,1,\ldots ,m \} nk/{0,1,,m}
也就是 n ⊕ k ≥ m + 1 n\oplus k \ge m+1 nkm+1,接下来就是按位进行构造了。
从高位到低位一位一位构造,对于第 ii 位,我们可以分四种情况讨论:
n n n i i i 位为 1 1 1 ( m + 1 ) (m+1) (m+1) i i i 位也为 1 1 1 时, k k k i i i位赋为 0 0 0 就可以了,因为 1 ⊕ 0 = 1 1\oplus 0=1 10=1 ≥ m \ge m m 的同位。
n n n i i i 位为 1 1 1 ( m + 1 ) (m+1) (m+1) i i i 位为 0 时,k 第 i i i 位赋为 0 0 0,因为 1 ⊕ 0 = 1 , > m 1\oplus 0=1,>m 10=1,>m 的同位;但是 k k k 的之后所有位也都得是 0 0 0 了。为什么?因为我们构造的 k 要尽可能小。
n n n i i i 位为 0 0 0 ( m + 1 ) (m+1) (m+1) i i i 位为 1 1 1 时, k k k i i i 位赋为 1 1 1,因为 0 ⊕ 1 = 1 , ≥ 0\oplus 1=1,\ge 01=1 m 的同位。
n n n i i i 位为 0 0 0 ( m + 1 ) (m+1) (m+1) i i i 位也为 0 0 0 时, k k k i i i 位赋为 0 0 0,因为 0 ⊕ 0 = 0 , ≥ m 0\oplus 0=0,\ge m 00=0m 的同位。

D Diane

TP
我们考虑一个全是同一字母的串。如果长度为奇数,那么它的各个奇数长度的子串出现次数为奇数个,偶数长度为偶数个。如果长度为偶数则刚好相反,奇数长度子串出现偶数次,偶数长度出现奇数次。
发现这个的话,之后就很简单了,就是把奇数串,偶数串都放入传中,中间加一个串防止两边影响。很容易证明这样就是对的。

CF1546D AquaMoon and Chess

仔细研究发现 11 11 11一组是可以随便移动,不受影响,所以我们可以两个 1 1 1分为一组,如果连续 1 1 1的个数为偶数依旧,依旧两两一组,如果发现为奇数个 1 1 1那把最后的 10 10 10分为一组,令 A = 11 , B = 10 A=11,B=10 A=11,B=10发现 A B AB AB是可以随意交换的, A A A 0 0 0也是可以随便交换的, B B B B B B就不可以(好像是一句废话,换了当没换), B B B 0 0 0也不可以换,所以 B B B 0 0 0本质是一样的,相对位置不改变,所以只用统计 11 11 11的个数为 a a a 0 0 0的个数为 b b b,答案为 ( a + b b ) {a+b}\choose{b} (ba+b)

CF1542C Strange Function

考虑反向定义
F ( k ) F(k) F(k)为最小的不为因子为 k k k的数的个数
其中 2 < = k < = 45 2<=k<=45 2<=k<=45,因为 l c m ( 1 , 2 , . . . k ) > 1 0 16 lcm(1,2,...k)>10^{16} lcm(1,2,...k)>1016
显然 F ( k ) = n / l c m ( 1 , 2... , k − 1 ) − n / l c m ( 1 , 2.... k ) F(k)=n/lcm(1,2...,k-1)-n/lcm(1,2....k) F(k)=n/lcm(1,2...,k1)n/lcm(1,2....k)

CF1542D Priority Queue

考虑的每个数单独求贡献,于是可以在 +      x +\ \ \ \ x +    x对这个数单独进行 d p dp dp求他一共有多少种方案
d p [ i ] [ j ] dp[i][j] dp[i][j]表示前 i i i个操作已经处理,总共有 j j j个数比 x x x小,因为比他小的数比他先删除
p o s pos pos为我们研究的 +      x +\ \ \ \ x +    x为第几次操作
考虑 i < p o s i<pos i<pos的情况
若当前这一位是 − - 号,那么就有 d p i , j = d p i − 1 , j + d p i − 1 , j + 1 dp_{i,j}=dp_{i-1,j}+dp_{i-1,j+1} dpi,j=dpi1,j+dpi1,j+1,另外特别注意一下 d p i , 0 dp_{i,0} dpi,0需要再次 + d p i − 1 , 0 + dp_{i-1,0} +dpi1,0
不选这个 − - 号,不变,选了这个 − - 号并且 j j j 本来就已经是 0 0 0 了,也是不变,因此 d p i − 1 , 0 dp_{i-1,0} dpi1,0要算两遍)
如果是 + + +号,并且当前的 v a l j > v a l p o s val_{j}>val_{pos} valj>valpos
那么选不选都无所谓,就有 d p i , j = d p i − 1 , j ∗ 2 dp_{i,j}=dp_{i-1,j}*2 dpi,j=dpi1,j2
否则的话,如果选, j j j就增加 1 1 1,否则不变,就有 d p i , j = d p i − 1 , j + d p i − 1 , j − 1 dp_{i,j}=dp_{i-1,j}+dp_{i-1,j-1} dpi,j=dpi1,j+dpi1,j1
考虑 i > p o s i>pos i>pos的情况
若当前这一位是 − - 号, d p i , 0 dp_{i,0} dpi,0不需要再次 + d p i − 1 , 0 + dp_{i-1,0} +dpi1,0,因为这样就会把 v a l p o s val_{pos} valpos给删掉
其次为 + + +且可选可不选的的一定要满足 v a l p o s ≤ v a l j val_{pos}\le val_{j} valposvalj,不然方案重复等死了

#include <bits/stdc++.h>
using namespace std;
const int maxn = 505;
const int mo = 998244353;
int readint(){
   int x=0,f=1;char s=getchar();
   #define sc (s=getchar())
   while(s<'0'||s>'9'){
   	if(s=='-')
   		f=-1;
   	sc;
   }
   while(s>='0'&&s<='9'){
   	x=(x<<3)+(x<<1)+(s^48);
   	sc;
   }
   #undef sc
   return x*f;
}
int a[maxn],dp[maxn][maxn];
int ans;
void getdp(int x,int n){
   for(int i=0;i<=n+1;i++)
   	for(int j=0;j<=n+1;j++)
   		dp[i][j]=0;
   dp[0][0]=1;
   for(int i=1;i<x;i++)
   	if(a[i]==0){
   		for(int j=0;j<=n;j++)
   			dp[i][j]=(0ll+dp[i-1][j]+dp[i-1][j+1])%mo;
   		dp[i][0]=(0ll+dp[i][0]+dp[i-1][0])%mo;
   	}
   	else{
   		for(int j=0;j<=n;j++){
   			if(a[i]>a[x])
   				dp[i][j]=1ll*dp[i-1][j]*2%mo;
   			else
   				dp[i][j]=(0ll+dp[i-1][j]+dp[i-1][j-1])%mo;
   		}
   	}
   for(int j=0;j<=n;j++)
   	dp[x][j]=dp[x-1][j];
   for(int i=x+1;i<=n;i++){
   	if(a[i]==0){
   		for(int j=0;j<=n;j++)
   			dp[i][j]=(0ll+dp[i-1][j]+dp[i-1][j+1])%mo;
   	}
   	else{
   		for(int j=0;j<=n;j++){
   			if(a[i]>=a[x])
   				dp[i][j]=1ll*dp[i-1][j]*2%mo;
   			else
   				dp[i][j]=(0ll+dp[i-1][j]+dp[i-1][j-1])%mo;
   		}
   	}
   }
   int sum=0;
   for(int j=0;j<=n;j++)
   	sum=(0ll+sum+dp[n][j])%mo;
   ans=(0ll+ans+1ll*sum*a[x]%mo)%mo;
}
int main (){
   int n=readint();
   for(int i=1;i<=n;i++){
   	char op[3];
   	scanf("%s",op);
   	if(op[0]=='-')
   		continue;
   	a[i]=readint();
   }
   for(int i=1;i<=n;i++){
   	if(a[i]!=0)
   		getdp(i,n);
   }
   cout<<ans<<endl;
   return 0;
} 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值