Codeforces Round #700 (Div. 1+Div. 2)
CF1480A
显然先选前面的。
对于先手,如果当前位不是\(a\),变为\(a\)即可。
否则变为\(b\)。
后手也这样分类讨论一下就好了。
code:
#include<cstdio>
#include<cstring>
using namespace std;
int n,m,k,x,y,z,t;
char s[100039];
int main(){
register int i;
scanf("%d",&t);
while(t--){
scanf("%s",s+1);n=strlen(s+1);
for(i=1;i<=n;i++){
if(i&1){
if(s[i]=='a') s[i]='b';
else s[i]='a';
}
else{
if(s[i]=='z') s[i]='y';
else s[i]='z';
}
}
printf("%s\n",s+1);
}
}
[CF1480B](http://codeforces.com/problemset/problem/1480/B)
显然英雄总共要扣的血量是恒定的。
那么我们就把最重的一击放到最后面看看前面能能不能撑下来即可。
code:
#include<cstdio>
#include<algorithm>
using namespace std;
int n,m,k,z,t;
long long a,b,now,flag;
struct yyy{long long x,y;}s[100039];
inline bool cmp(yyy x,yyy y){return x.y<y.y;}
int main(){
register int i;
scanf("%d",&t);
while(t--){
scanf("%lld%lld%d",&b,&a,&n);flag=0;
for(i=1;i<=n;i++) scanf("%lld",&s[i].y);
for(i=1;i<=n;i++) scanf("%lld",&s[i].x);
sort(s+1,s+n+1,cmp);
for(i=1;i<=n;i++){
now=(s[i].x+b-1)/b;
a-=s[i].y*(now-1);
if(a<=0){flag=1;break;}
a-=s[i].y;
}
if(flag) printf("NO\n");
else printf("YES\n");
}
}
CF1479A
这东西一看就是给二分的。
考虑一个序列一定有一个区域最小值。
首先检查\(1\)与\(n\),观察是不是区域最小值。
如果不是那么二分。
对于中点\(mid\),查询两边以及它自己\(x,y,z\)。
如果是\(y>x,z\),那么两边一定都有区域最小值,随便走哪边都行。
如果是上行趋势,那么左边一定有。
反之则右边一定有。
查询次数\(3logn\)
code:
#include<cstdio>
using namespace std;
int n,m,k,x,y,z,l,r,mid;
int main(){
register int i;
scanf("%d",&n);
printf("? 1\n");fflush(stdout);
scanf("%d",&x);
if(n==1){printf("! %d\n",1);fflush(stdout);return 0;}
printf("? 2\n");fflush(stdout);scanf("%d",&y);
if(x<y){printf("! %d\n",1);fflush(stdout);return 0;}
l=1;
printf("? %d\n",n);fflush(stdout);
scanf("%d",&x);
printf("? %d\n",n-1);fflush(stdout);
scanf("%d",&y);
if(x<y){printf("! %d\n",n);fflush(stdout);return 0;}
r=n;
mid=l+r>>1;
printf("? %d\n",mid);fflush(stdout);scanf("%d",&x);
printf("? %d\n",mid-1);fflush(stdout);scanf("%d",&y);
printf("? %d\n",mid+1);fflush(stdout);scanf("%d",&z);
if(x<y&&x<z){printf("! %d\n",mid);fflush(stdout);return 0;}
if(y>x&&x>z) l=mid;
else r=mid;
}
}
CF1479B1
为什么陈指导要\(dp\)啊,贪心好打还少一个\(log\)它不香吗?
考虑维护当前两个集合的最近的一个。
如果两个有一个和当前一样那一定是直接盖另一个上去并答案加一。
但是如果两个都不一样就没有办法选择。
所以我们预处理\(net_i\)表示\(a_i\)下一次什么时候出现。
显然是先覆盖早出现的更优。
所以就可以愉快地贪心了。
code:
#include<cstdio>
using namespace std;
int n,m,k,x,y,z,a[100039],ans=1,now,now1,now2,net[100039],f[100039];
int main(){
register int i;
scanf("%d",&n);
for(i=1;i<=n;i++) scanf("%d",&a[i]);
for(i=0;i<=n;i++) f[a[i]]=n+1;
for(i=n;i>=0;i--){
net[i]=f[a[i]];
f[a[i]]=i;
}
for(i=1;i<=n;i++){
if(a[now1]!=a[i]&&a[now2]!=a[i]){
now++;
if(net[now1]>net[now2]) now2=i;
else now1=i;
}
else if(a[now1]!=a[i]) now++,now1=i;
else if(a[now2]!=a[i]) now++,now2=i;
else now1=now2=i;
}
printf("%d\n",now);
}
CF1479B2
其实和B1不是一个东西吗,把符号改一下就好了啊。
code:
#include<cstdio>
using namespace std;
int n,m,k,x,y,z,a[100039],ans=1,now,now1,now2,net[100039],f[100039];
int main(){
register int i;
scanf("%d",&n);
for(i=1;i<=n;i++) scanf("%d",&a[i]);
for(i=0;i<=n;i++) f[a[i]]=n+1;
for(i=n;i>=0;i--){
net[i]=f[a[i]];
f[a[i]]=i;
}
for(i=1;i<=n;i++){
if(a[now1]!=a[i]&&a[now2]!=a[i]){
now++;
if(net[now1]<net[now2]) now2=i;
else now1=i;
}
else if(a[now1]==a[i]) now1=i;
else if(a[now2]==a[i]) now2=i;
}
printf("%d\n",now);
}
CF1479C
不咕了不咕了。
其实这个肯定有解。
这种这么小的点数肯定是二进制拆分。
首先先由\(1\)号点向每个点连一条\(l\)的边。
然后由每个非一号点\(i\)向后面的每个非\(n\)号点连一条\(2^{i-2}\)长度的边。
然后对\(r-l\)二进制拆分,之后如果第\(i\)位为\(1\)就把这个数后\(i-2\)位全部清零,因为之前已经包括,然后连这样权值的边即可。
code:
#include<cstdio>
using namespace std;
int n,m,k,l,r;
struct yyy{int x,y,z;}s[10039];
int main(){
register int i,j;n=22;
scanf("%d%d",&l,&r);printf("YES\n");r-=l;
for(i=2;i<=n;i++)s[++m]={1,i,l};
for(i=2;i<n;i++){
for(j=i+1;j<n;j++) s[++m]={i,j,1<<i-2};
}
for(i=2;i<n;i++)if((r>>i-2)&1)s[++m]={i,n,((r>>i-1)<<i-1)+1};
printf("%d %d\n",n,m);
for(i=1;i<=m;i++) printf("%d %d %d\n",s[i].x,s[i].y,s[i].z);
}