Codeforces Round #705 (Div. 2)
CF 自闭场,但考后还算顺利。(还上了 rating)
A Anti-knapsack
简单贪心题,考时一遍 AC。
针对每个 \(1\leq i\leq n\),我们考虑选或者不选。
有一下几条很显然的性质:
- \(i>k\) 时必选。
- \(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 比较神仙,先咕着。