Codeforces Round #705 (Div. 2)

CF 自闭场,但考后还算顺利。(还上了 rating)

题目在这里

A Anti-knapsack

简单贪心题,考时一遍 AC。

针对每个 \(1\leq i\leq n\),我们考虑选或者不选。

有一下几条很显然的性质:

  1. \(i>k\) 时必选。
  2. \(i=k\) 时不选。

那么关键是 \(1\leq i<k\) 时的选择。

显然如果我们选了某个数 \(i\),那么 \(k-i\) 是不能选的。

那么我们最多可以选出 \(\frac{k}{2}\) 个数,显然 \(\left\lceil\dfrac{k}{2}\right\rceil\)\(k-1\) 是符合要求的。

综上,我们选择的集合为:

\[A=\{\left\lceil\dfrac{k}{2}\right\rceil,\left\lceil\dfrac{k}{2}\right\rceil+1,...,k-1,k+1,k+2,...,n\} \]

丑陋的代码:

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

int T,n,k;

int read(){
	int x=0,f=1;char c=getchar();
	while(c<'0' || c>'9') f=(c=='-')?-1:1,c=getchar();
	while(c>='0' && c<='9') x=x*10+c-48,c=getchar();
	return x*f;
}

int main(){
	T=read();
	while(T--){
		n=read(),k=read();
		int ans=0;
		for(int i=k-1;i>=1;i--){
			if(i*2+1<=k) break;
			++ans;
		}
		printf("%d\n",ans+n-k);
		for(int i=k-1;i>=1;i--){
			if(i*2+1<=k) break;
			printf("%d ",i);
		}
		for(int i=k+1;i<=n;i++) printf("%d ",i);
		puts("");
	}
	return 0;
}

B Planet Lapituletti

按照题意模拟即可,没啥好说的,考时一遍 AC。

#include<cstdio>
#include<cstring>
#include<algorithm>
#define INF 0x7fffffff
using namespace std;

int mn,A,B;
int ans_a,ans_b;
char s[10];

int Get(int x,int y,int P,int Q){
	int sum;
	if(P<x || P==x && Q<=y) sum=(x-P)*B+y-Q;
	else sum=(x*B+y)+(A-P)*B+B-Q;
	return sum;
}

int Find(int x){
	if(x==1) return 1;
	if(x==2) return 5;
	if(x==5) return 2;
	if(x==8) return 8;
	if(x==0) return 0; 
}

void work(int x,int y,int P,int Q){
	int a=x/10%10;
	int b=x%10;
	int c=y/10%10;
	int d=y%10;
	if(a!=1 && a!=2 && a!=5 && a!=8 && a!=0) return;
	if(b!=1 && b!=2 && b!=5 && b!=8 && b!=0) return;
	if(c!=1 && c!=2 && c!=5 && c!=8 && c!=0) return;
	if(d!=1 && d!=2 && d!=5 && d!=8 && d!=0) return;
	a=Find(a);b=Find(b);c=Find(c);d=Find(d);
	if(d*10+c>=A || b*10+a>=B) return;
	int now=Get(x,y,P,Q);
	if(now<mn){
		mn=now;
		ans_a=x;ans_b=y;
	}
}

void Print(int x){
	if(x<10) printf("0");
	printf("%d",x);
}

int main(){
	int T;
	scanf("%d",&T);
	while(T--){
		scanf("%d %d",&A,&B);
		mn=INF;
		int P,Q;
		scanf("%s",s+1);
		P=(s[1]-'0')*10+(s[2]-'0');
		Q=(s[4]-'0')*10+(s[5]-'0');
		for(int i=0;i<A;i++)
			for(int j=0;j<B;j++) work(i,j,P,Q);
		Print(ans_a);
		printf(":");
		Print(ans_b);
		puts("");
	}
	return 0;
}

C K-beautiful Strings

巧妙的构造题,考时直接跳过了,考完发现比较水。

首先很好证明当且仅当 \(k\) 整除 \(n\) 时有解(一定存在解 zzz...zzz),否则一定无解。

然后考虑构造,我们可以枚举开始改变的位置,显然这个位置越后越好。

对于每次枚举的位置 \(i\),显然 \(i+1\)\(n\) 位的字符串我们是可以随意填充的。

我们只需要枚举第 \(i\) 位填上什么,然后看后面位数能否够将所有字符都加到整除 \(k\)

当然枚举的时候要大于 \(i\) 位置原本的字符。

时间复杂度 \(O(26N)\)

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100010
using namespace std;

int n,k;
int b[N];
char a[N];

void Print(int x,int y){
	memset(b,0,sizeof(b));
	a[x]=y+'a';
	for(int i=1;i<=x;i++){
		putchar(a[i]);
		b[a[i]-'a']++;
	}
	int sum=0;
	for(int i=0;i<26;i++){
		b[i]=(k - (b[i] % k)) % k;
		sum+=b[i];
	}
	for(int i=x+1;i<=n-sum;i++) putchar('a');
	for(int i=0;i<26;i++)
		for(int j=1;j<=b[i];j++) putchar(i+'a');
	puts("");
}

void work(){
	scanf("%d %d",&n,&k);
	scanf("%s",a+1);
	memset(b,0,sizeof(b));
	if(n % k){puts("-1");return;}
	for(int i=1;i<=n;i++) b[a[i]-'a']++;
	int sum=0;
	for(int i=0;i<26;i++) sum+=(k - (b[i] % k)) % k;
	if(!sum){puts(a+1);return;}
	
	for(int i=n;i>=1;i--){
		int p=a[i]-'a';
		sum-=(k - (b[p] % k)) % k;
		b[p]--;
		sum+=(k - (b[p] % k)) % k;
		for(int j=p+1;j<26;j++){
			int now=sum;
			now-=(k - (b[j] % k)) % k;
			now+=(k - ((b[j]+1) % k)) % k;
			if(now<=n-i) {Print(i,j);return;}
		}
	}
}

int main(){
	int T;
	scanf("%d",&T);
	while(T--) work();
	return 0;
}

D GCD of an Array

考试时靠动态开点线段树卡着空间通过的,思路近乎无脑。

考完才发现有更简单的做法。

将每个数分解质因数,显然根据算数基本定理,对于所有出现过的质数 \(p_i\)

\[\gcd=\Pi_{i=1}^n p_i^{min\{c_i\}} \]

显然我们需要的是一个支持查询区间最小值单点修改的数据结构。

一个 multiset 不知道比你动态开点线段树好写多少倍了。

同时为了维护每个数的质因数个数,偷懒写个 map,就可以水过了。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<set>
#define N 200010
#define MOD 1000000007
using namespace std;

int n,m,q;
int prm[N];
bool vis[N];
multiset<int>st[N];
map<int,int>mp[N];
long long ans=1;

int read(){
	int x=0,f=1;char c=getchar();
	while(c<'0' || c>'9') f=(c=='-')?-1:1,c=getchar();
	while(c>='0' && c<='9') x=x*10+c-48,c=getchar();
	return x*f;
}

void Get_Prime(){
	m=0;
	memset(vis,false,sizeof(vis));
	for(int i=2;i<=200000;i++){
		if(!vis[i]) prm[++m]=i;
		for(int j=1;j<=m;j++){
			if(prm[j]*i>200000) break;
			vis[prm[j]*i]=true;
			if(i%prm[j]==0) break;
		}
	}
}

void add(int pos,int val){
	for(int i=1;(long long)prm[i]*prm[i]<=val && i<=m;i++)
		if(val % prm[i]==0){
			int cnt=0;
			while(val % prm[i]==0) val/=prm[i],cnt++;
			int last=mp[pos][prm[i]];
			mp[pos][prm[i]]+=cnt;
			int mn=0;
			if(st[prm[i]].size()==n) mn=*st[prm[i]].begin();
			if(last) st[prm[i]].erase(st[prm[i]].find(last));
			st[prm[i]].insert(last+cnt);
			if(st[prm[i]].size()==n){
				int now=*st[prm[i]].begin();
				for(int j=mn+1;j<=now;j++) ans=(1LL*ans*prm[i]) % MOD;
			}
		}
	if(val>1){
		int last=mp[pos][val];
		mp[pos][val]++;
		int mn=0;
		if(st[val].size()==n) mn=*st[val].begin();
		if(last) st[val].erase(st[val].find(last));
		st[val].insert(last+1);
		if(st[val].size()==n){
			int now=*st[val].begin();
			for(int j=mn+1;j<=now;j++) ans=(1LL*ans*val) % MOD;
		}
	}
}

int main(){
	Get_Prime();
	n=read();q=read();
	for(int i=1;i<=n;i++){
		int a=read();
		add(i,a);
	}
	while(q--){
		int x=read(),y=read();
		add(x,y);
		printf("%lld\n",ans);
	}
	return 0;
}

E 和 F 比较神仙,先咕着。

posted @ 2021-03-07 12:43  LPF'sBlog  阅读(248)  评论(2编辑  收藏  举报