训练赛5(ICPC全国邀请赛—校内选拔赛 )
导语
运气好没有因罚时掉到第5,终于能出去打一次比赛
涉及的知识点
贪心、模拟、字符串、数学、DP、几何、思维、栈
题目
A( CodeForces 1276A )
题目大意:给出一个字符串s,去掉最少的字符使得该串中不存在连续三个字符能构成"one"或"two",输出每个去掉字符的位置,多种答案输出一种
思路:本来想使用KMP,但是发现本题给的模式串并不是对称的,而且很小,所以应该可以直接暴力,统计s中有几个two和one即可,对于twone这个字符串单独讨论
代码
#include <bits/stdc++.h>
using namespace std;
int len,t;
char str[2121212];
deque<int>Q;
int main() {
scanf("%d\n",&t);
while(t--) {
scanf("%s",str+1);
len=strlen(str+1);
for(int i=1; i<=len; i++) {
string tmp;
for(int j=0; j<3; j++)//获取连续三个字符构成的字符串
tmp+=str[i+j];
if(tmp=="two") {
Q.push_back(i+1);//存下标,去掉中间字符就行了
}
if(tmp=="one") {
if(!Q.empty()&&Q.back()==-1) {
Q.pop_back();
continue;
}
Q.push_back(i+1);
}
for(int j=3; j<5; j++)
tmp+=str[i+j];
if(tmp=="twone") {
Q.pop_back();
Q.push_back(i+2);
Q.push_back(-1);//存一个标记,代表需要去掉重复值,因为后面必定会扫一个one
}
}
getchar();
printf("%d\n",Q.size());
len=Q.size();
for(int i=0; i<len; i++) {
printf("%d%c",Q.front(),i==len-1?'\n':' ');
Q.pop_front();
}
if(len==0)
putchar('\n');
}
return 0;
}
B(计蒜客 A1616 )
题目大意:两队lol队员,每队5人,每队可禁用5个英雄,共100个,先给出一个5*100的01矩阵代表我方拥有人物情况,s[i][j]表示第i个人是否有第j个人物,1有0无,询问共有多少种对局情况(敌方假定有全部人物)
思路:对局情况由四个部分组成:我方选择、敌方选择、我方禁止、敌方禁止,那么总对局数就等于这四部分之积,后三部分的积是可以求出的: A 95 5 × C 90 5 × C 85 5 A_{95}^5×C_{90}^5×C_{85}^5 A955×C905×C855,分别对应敌方选择,我方禁止,敌方禁止,经过计算与取余可以得到值为531192758,之后采用动态规划的方法来计算选择方案数,可得状态转移方程。
1.s[i][j]为0,dp[i][j]=dp[i][j-1],第j个人物没有,直接选下一个
2.s[i][j]为1,dp[i][j]=dp[i][j-1]+dp[i-1][j-1],第j个人物有,变成可选可不选,累和方案
对我方5人需要求全排列,然后每个排列进行一次dp,因为每个排列的不同也算一种方案
最后累和可选方案,乘计算取余的值即可
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
char square[6][121];
ll dp[6][121],data[6],mod=1e9+7,num=531192758,ans;
int main() {
while(~scanf("%s",square[1]+1)) {
data[1]=1;
for(int i=2; i<=5; i++) {
scanf("%s",square[i]+1);
data[i]=i;
}
do {
memset(dp,0,sizeof(dp));
for(int i=1; i<=100; i++) {
dp[1][i]=dp[1][i-1];
if(square[data[1]][i]=='1')
dp[1][i]++;
}//必须初始化,否则后面的dp过程无法成立
for(int i=2; i<=5; i++) {
for(int j=1; j<=100; j++)
if(square[data[i]][j]=='0')//不可选
dp[i][j]=dp[i][j-1]%mod;
else//可选可不选
dp[i][j]=(dp[i][j-1]+dp[i-1][j-1])%mod;
}
ans=(ans+dp[5][100])%mod;
} while(next_permutation(data+1,data+6));
printf("%lld\n",ans*num%mod);
ans=0;
}
return 0;
}
C(AtCoder agc031_a )
题目大意:给出一个长度为N的字符串,现给出本题中子串的定义:如果两个子串的各自的字符来源于原串的不同位置,则视为两字符串不同,并且两子串中字符的位置符合原串中的相对位置,求出没有重复字符的子串个数
思路:因为原串的顺序是固定的,所以可以认为取出来的字符串是自动排序的,即无需考虑排列问题,那么只需要考虑每个字符的组合问题了,记录每种字符出现的次数,每种字符选取一个位置即可,还要考虑不选的情况
代码
#include <bits/stdc++.h>
using namespace std;
long long N,num[27],ans=1,mod=1e9+7;
char ch;
int main() {
scanf("%lld",&N);
getchar();
while((ch=getchar())!='\n')
num[ch-'a']++;//记录数值
for(int i=0; i<27; i++)
ans=(ans*(num[i]+1))%mod;//排列组合
printf("%lld",ans-1);
return 0;
}
D(CodeForces 764B )
题目大意:给出n个立方体,每个立方体被赋值,现执行操作,在第i步时,翻转第i个到第n-i+1个盒子(包括边界),直到i ≤ \le ≤n-i+1,现给出最终的翻转结果,求初始序列
思路:模拟过程即可,找规律可以发现每次只是换了边界值,注意数据规模
代码
#include <bits/stdc++.h>
using namespace std;
int a[212121],n;
int main() {
scanf("%d",&n);
for(int i=1; i<=n; i++)
scanf("%d",&a[i]);
for(int i=1; i<=(n+1)/2; i+=2)
swap(a[i],a[n-i+1]);
for(int i=1; i<=n; i++)
printf("%d%c",a[i],i==n?'\n':' ');
return 0;
}
E(CodeForces 494A)
题目大意:给出一个由)、(、#三个字符组成的字符串,#可以变成至少一个),问该字符串能否括号匹配完,如果能输出每个#变成的)数量
思路:将字符串从后往前遍历,将碰到的第一个#用来满足之后)不足的情况,将其他#变为一个),最后判断是否匹配完
代码
#include <bits/stdc++.h>
using namespace std;
char str[121212];
int len,l,r,flag,ans;
vector<int>v;
int main() {
scanf("%s",str+1);
len=strlen(str+1);
for(int i=len; i>=1; i--) {
if(str[i]==')'||str[i]=='#') {
if(l>0) {
if(l>r&&flag==0) {
printf("-1");
return 0;
} else {
if(l>r) {
ans+=l-r;
r=0;
} else
r-=l;
l=0;
}
}
if(str[i]=='#')
flag++;
r++;
} else
l++;
}
if(l>0) {
if(l>r&&flag==0) {
printf("-1");
return 0;
} else {
if(l>r) {
ans+=l-r;
r=0;
} else
r-=l;
l=0;
}
}
if(r==0)
for(int i=1; i<=flag; i++)
printf("%d\n",i==flag?1+ans:1);
else
printf("-1");
return 0;
}
F(CodeForces 777A )
题目大意:三个杯子,一开始只有杯子里盖了一个球,现在知道第奇数次换0和1杯,偶数次换1和2杯,知道了最后求在哪,问一开始球在哪
思路:找规律,根据三种不同的情况判断即可
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
char data[3][6]={'0','1','2','2','1','0','1','0','0','1','2','2','2','2','1','0','0','1'};
int main() {
ll n,t;
scanf("%lld%lld",&n,&t);
ll x=n%6;
for(int i=0; i<=2; i++)
if(data[i][x]==t+'0') {
printf("%d",i);
break;
}
return 0;
}
G( AtCoder agc031_b )
题目大意:给出N个数,可以进行0或多次操作,每次操作为将一段左右端点相等的区间全都变为左右相等的值,询问最后有多少种不同的序列
思路:设dp[i]表示当序列长度为i时,有多少种不同的序列,对于第i个位置,有两个选择,操作或不操作,操作,则获得上一个相等值位置的值,不操作,则获得相邻的前一个位置的值,如果上一个相等值位置正好相邻,直接赋值即可,具体看代码
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll N,val[212121],sum[212121],dp[212121]={1},mod=1e9+7;
//val记录值,sum记录当前值的上一个相等位置解的数量
//dp记录当序列长度为i时,有多少种不同的颜色方案
int main() {
scanf("%lld",&N);
for(int i=1; i<=N; i++)
scanf("%lld",&val[i]);//扫入颜色
for(int i=1; i<=N; i++) {
if(val[i]==val[i-1]) {
dp[i]=dp[i-1];//如果相邻相等,状态转移
continue;
}
dp[i]=(dp[i-1]+sum[val[i]])%mod;
//如果非相邻相等,记录方案数,该位置方案数有两个来源
//选择变颜色,获得上一个相等位置的方案数,选择不变,获得前一个位置
sum[val[i]]=dp[i];
}
printf("%lld",dp[N]);
return 0;
}
H(CodeForces 749B )
题目大意:给出三个点坐标,求出能和这三个点组成平行四边形的第四个点有几个,并求出坐标
思路:平行四边形性质
代码
#include <bits/stdc++.h>
using namespace std;
typedef pair<int,int>pr;
pr data[3];
int main() {
for(int i=0; i<3; i++)
scanf("%d%d",&data[i].first,&data[i].second);
printf("3\n");
for(int i=0; i<3; i++)
printf("%d %d\n",data[i].first+data[(i+1)%3].first-data[(i+2)%3].first,data[i].second+data[(i+1)%3].second-data[(i+2)%3].second);
return 0;
}
I(计蒜客 A1428 )
题目大意:给出n个字符串,按输入顺序从每个字符串中选取一个后缀首尾拼接,求出一个字典序最小的字符串
思路:贪心,从最后一个字符串开始选取字典序最小的后缀,将后缀拼在前一个字符串上构成新串,以此类推
代码
#include <bits/stdc++.h>
using namespace std;
string str[212121];
int T,n,len[212121];
int main() {
scanf("%d",&T);
while(T--) {
scanf("%d",&n);
for(int i=1; i<=n; i++) {
cin >>str[i];//录入数据
len[i]=str[i].size();//记录长度
}
for(int i=n; i>=1; i--) {
int pos=1,length=str[i].length();//更新选取下标和当前构成的字符串的长度
for(int j=1; j<=len[i]; j++)//随着j的增大,右边的比较字符串在移位,每次移位后都有自动填充
if(str[i].substr(pos-1,length-pos+1)>str[i].substr(j-1,length-j+1))//找到相等长度下后缀最小下标
pos=j;
str[i-1]+=str[i].substr(pos-1,length-pos+1);
}
cout <<str[0]<<endl;
str[0]="";
}
return 0;
}
J(CodeForces 254B )
题目大意:输入n组数,每组数为截止月份+日期+这个任务需要的人数+需要的任务时间
思路:将天数转化为数字,并作为下标,记录每天的人数,获取最大需求量
代码
#include <bits/stdc++.h>
using namespace std;
int mon[14]= {0,31,28,31,30,31,30,31,31,30,31,30,31},day[1212],month[15],ans,n;
int main() {
freopen("input.txt","r",stdin);
freopen("output.txt","w",stdout);
scanf("%d",&n);
for(int i=1; i<=12; i++)
month[i]=month[i-1]+mon[i];//记录每个月末到起始的天数
for(int i=1; i<=n; i++) {
int m,d,p,t;
scanf("%d%d%d%d",&m,&d,&p,&t);
m--;//相对于起始的偏移量,如1月内就不能以31天来算,
for(int j=month[m]+d+100-t; j<month[m]+d+100; j++) {
day[j]+=p;//对于当前任务,从最开始到结束,中间的天数都要增加人数
//以下标来代表天数
ans=max(ans,day[j]);//获取最大需求量
}
}
printf("%d",ans);
return 0;
}