打了那么多cf(cross fire),这才第一次打了高端cf(codeforces)……感觉2个小时确实有点紧,很考验选手临危不乱的心态,还有读题一定要快,结合着样例反推题意很有效果。

A. Erasing Zeroes

题目链接

题目原文

1303A

题目大意

给出一个01串,求最少删除多少个0,保证字符串中所有的1都是相邻的。

思路

模拟。假设当前位i是1,上一个1的位置是j,那么中间就有i-j-1个0。所以开一个变量记录上一个1的位置,不断累加答案即可。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include < cstdio >
#include < cstring >
#include < algorithm >
using namespace std;
const int maxn=105;
char str[maxn];
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
memset(str,0,sizeof(str));
scanf("%s",str+1);
int n=strlen(str+1);
bool f=false;int last=0,ans=0;
for(int i=1;i<=n;i++)
{
if(str[i]=='1')
{
if(!f)
{
f=true;
last=i;
}
else
{
ans+=(i-last-1);
last=i;
}
}
}
printf("%d\n",ans);
}
return 0;
}

B. National Project

题目链接

题目原文

1303B

题目大意

给出要修的路的长度nn,能修出好路的周期时间g,不能修出好路的周期时间b。现在从能修出好路的周期开始,问在保证修的所有路中好路的比例要大于或等于一半的条件下,至少需要多少天。

思路

我用的二分答案,然后尽量在能修出好路的周期时间里修,看是否满足条件

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include &lt; cstdio &gt;
#include &lt; cstring &gt;
#include &lt; algorithm &gt;
using namespace std;
const int maxn=200005;
long long n,g,b,T;
int main()
{
#ifdef lemon
freopen("B.txt","r",stdin);
#endif
scanf("%lld",&T);
while(T--)
{
scanf("%lld%lld%lld",&n,&g,&b);
long long left=n,right=1e18,ans=-1;
while(left<=right)
{
long long mid=(left+right)>>1;
long long cc=mid/(b+g);
long long res=mid%(b+g);
long long gd=g*cc+min(res,g);
long long bd=b*cc+max(0ll,res-g);
long long temp=mid-n;
if(bd-temp<0)
{
gd+=(bd-temp);
bd=0;
}
else bd-=temp;
bool flag=false;
long long tot=gd+bd;
if((tot+1)/2<=gd) flag=true;
if(flag) ans=mid,right=mid-1;
else left=mid+1;
}
printf("%lld\n",ans);
}
return 0;
}

C. Perfect Keyboard

题目链接

题目原文

1303C

题目大意

有一个键盘,只有一行26个小写字母。现在一个人想每次只按相邻的两个键,比如说这次按了pos位置的键,下一次只能按pos-1或者pos+1位置的键。现在给出一个字符串,问能不能通过这样的方式按出,如果能,请输出键盘布局。

思路

搜索。直接上dfs完。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#include &lt; cstdio &gt;
#include &lt; cstring &gt;
#include &lt; algorithm &gt;
using namespace std;
const int maxn=205;
char str[maxn],ans[505];
bool vis[maxn],used[maxn];
int n,flag,T;
void dfs(int x,int pos)
{
if(x==n+1)
{
flag=1;
return;
}
if(vis[pos+1]&&ans[pos+1]==str[x]) dfs(x+1,pos+1);
if(vis[pos-1]&&ans[pos-1]==str[x]) dfs(x+1,pos-1);

if((!vis[pos+1])&&(!used[str[x]]))
{
vis[pos+1]=true;
ans[pos+1]=str[x];
used[str[x]]=true;
dfs(x+1,pos+1);
if(flag!=-1) return;
vis[pos+1]=false;
used[str[x]]=false;
}
if((!vis[pos-1])&&(!used[str[x]]))
{
vis[pos-1]=true;
ans[pos-1]=str[x];
used[str[x]]=true;
dfs(x+1,pos-1);
if(flag!=-1) return;
vis[pos-1]=false;
used[str[x]]=false;
}
}
int main()
{
scanf("%d",&T);
while(T--)
{
memset(str,0,sizeof(str));
memset(ans,0,sizeof(ans));
memset(vis,false,sizeof(vis));
memset(used,false,sizeof(used));
scanf("%s",str+1);
n=strlen(str+1);
flag=-1;
ans[250]=str[1];
vis[250]=true;used[str[1]]=true;
dfs(2,250);
if(flag==-1)
{
printf("NO\n");
continue;
}
printf("YES\n");
int now=250,left=250,right=250;
while(vis[now+1]) now++,right=now;
now=250;
while(vis[now-1]) now--,left=now;
for(int i=left;i<=right;i++) printf("%c",ans[i]);
for(int i='a';i<='z';i++) if(!used[i]) printf("%c",i);
printf("\n");
}
return 0;
}

D. Fill The Bag

题目链接

题目原文

1303D

题目大意

给出一个数字nn和m个数,这m个数都是2的幂次方。
现在每次操作你可以把m个数中的一个平均地一分为2,比如32 操作一次得到两个16,再操作一次就是4个8,现在问最少要操作多少次,可以在操作后把这些数字恰好凑为n。如果无解请输出-1。

思路

比赛的时候只有20分钟了,思路比较混乱,刚预处理出来比赛就结束了。
首先如果这m个数之和是大于或者等于nn的,那么一定有解,反之一定无解,可以通过数的二进制表示来证明。
如果有解的话,我们先预处理出这m个数所对应的二进制位并存入cnt数组。然后我们去找n的二进制下为1的位置i是否在m个数存在(检测cnt数组即可),若存在,那么我们就不用拆了。若不存在,那么我们就拆第i+1个数,并且ans++,依次做下去即可。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include &lt; cstdio &gt;
#include &lt; cstring &gt;
#include &lt; algorithm &gt;
using namespace std;
const int maxn=100005;
long long n,m,a[maxn],cnt[maxn];
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
memset(cnt,0,sizeof(cnt));
long long ans=0,sum=0;
scanf("%lld%lld",&n,&m);
for(int i=1;i<=m;i++)
{
scanf("%lld",&a[i]);
sum+=a[i];
for(long long j=0;;j++)
{
if((1ll<<j)&a[i])
{
cnt[j]++;
break;
}
}
}
if(sum<n)
{
printf("-1\n");
continue;
}
for(long long i=0;i<63;i++)
{
long long now=(n>>i)&1;
cnt[i]-=now;
if(cnt[i]>=2) cnt[i+1]+=cnt[i]/2;
if(cnt[i]<0) cnt[i+1]--,ans++;
}
printf("%lld\n",ans);
}
return 0;
}

E. Erase Subsequences

题目链接

题目原文

1303D

题目大意

给定字符串s1s1s2s2,问能否用至多两个s1s1的非重叠子序列相加构造出s2s2

思路

和寒假集训第五天的H题相似而略有不同。上次的题是刚好拼出,两个子序列长度相加等于字符串总长,这道题要求存在子序列即可(只要长度等于s2s2即可)。所以只需要将转移改成位置就好了。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include &lt; cstdio &gt;
#include &lt; cstring &gt;
#include &lt; algorithm &gt;
using namespace std;
const int maxn=405;
const int inf=0x7f7f7f7f;
char s1[maxn],s2[maxn],s3[maxn];
int T,nxt[maxn][26],f[maxn][maxn];
int main()
{
scanf("%d",&T);
while(T--)
{
memset(s1,0,sizeof(s1));
memset(s2,0,sizeof(s2));
scanf("%s%s",s1+1,s2+1);
int l1=strlen(s1+1),l2=strlen(s2+1);
for(int i=0;i<26;i++) nxt[l1][i]=inf;
for(int i=l1;i;i--)
{
for(int j=0;j<26;j++) nxt[i-1][j]=nxt[i][j];
nxt[i-1][s1[i]-'a']=i;
}
bool flag=false;
for(int i=1;i<=l2;i++)
{
int l3=l2-i;
for(int j=i+1;j<=l2;j++) s3[j-i]=s2[j];
f[0][0]=0;
for(int j=0;j<=i;j++)
for(int p=0;p<=l3;p++)
{
if(!j&&!p) continue;
f[j][p]=inf;
if(j&&f[j-1][p]!=inf) f[j][p]=min(f[j][p],nxt[f[j-1][p]][s2[j]-'a']);
if(p&&f[j][p-1]!=inf) f[j][p]=min(f[j][p],nxt[f[j][p-1]][s3[p]-'a']);
}
if(f[i][l3]<=l1) flag=true;
if(flag) break;
}
if(flag) printf("YES\n");
else printf("NO\n");
}
return 0;
}

剩下的等我做出来再补。

edu-round-82