全国信息学分区联赛模拟试题(一)
【试题概览】
题目名称 | 漂亮字串 | Set | Prison |
---|---|---|---|
提交文件 | bs.* | set.* | prison.* |
输入文件 | bs.in | set.in | prison.in |
输出文件 | bs.ans | set.ans | prison.ans |
时间限制 | 1s | 1s | 1s |
空间限制 | 128MB | 128MB | 128MB |
题目来源 | Codejam | codejam | cadejam |
漂亮字串
【题目描述】
Caima 认为 O 和 X 是最优美的两个字母,由 O、X 组成的串是最优美的串。在这些最优美的串中, 如果任意只包含 X 的子串,长度不超过 maxX,任意只包含 O 的子串,长度不超过 maxO,且整个串 最多有 countO 个 O,countX 个 X。那么这个就是超级优美无敌串。
现在 Caima 想知道最长的超级优美无敌串有多长,希望你告诉他。
【输入格式】
输入包含多行,至文件结束为止;
每行四个数,依次是 countO、countX、maxO、maxX。
【输出格式】
每组数据输出一行,一个数表示最长的超级优美无敌串的长度。
【数据规模】
0<=countO,countX,maxO,maxX<=1000000
【输入样例】
10 10 0 0
3 5 1 1
【输出样例】
0
7
【注意事项】
第二个样例的解释:”XOXOXOX”
最多 1000 组数据,其中 30%的数据 0<=countO,countX,maxO,maxX<=20,且数据组数不超过 20 组。
题解
直接贪心,不妨设O的个数小于X的个数,
那么将O排成一行,中间插入X,在满足其他条件的情况下一定是最优的,可以O(1)得到结果,代码如下:
#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <cmath>
#define LL long long
using namespace std;
int co,cx,mo,mx;
int main(){
freopen("bs.in","r",stdin);
freopen("bs.out","w",stdout);
while(scanf("%d%d%d%d",&co,&cx,&mo,&mx)!=EOF){
if(mo==0){
printf("%d
",min(cx,mx));
continue;
}
else if(mx==0){
printf("%d
",min(co,mo));
continue;
}
else if(co==cx){
printf("%d
",co+cx);
continue;
}
else if(co>cx){
int to=cx+1;
int ans=(int)min((LL)co,(LL)to*mo)+cx;
printf("%d
",ans);
continue;
}
else if(co<cx){
int tx=co+1;
int ans=(int)min((LL)cx,(LL)tx*mx)+co;
printf("%d
",ans);
}
}
return 0;
}
Set
【题目描述】
现在给你一些连续的整数,它们是从 A 到 B 的整数。一开始每个整数都属于各自的集合,然后你 需要进行如下操作:
每次选择两个属于不同集合的整数,如果这两个整数拥有大于等于 P 的公共质因数,那么把它们 所在的集合合并。
反复上述操作,直到没有可以合并的集合为止。
现在 Caima 想知道,最后有多少个集合。
【输入格式】
一行,三个整数 A,B,P。
【输出格式】
一个树,表示最终集合的个数。
【数据规模】
A<=B<=100000;2<=P<=B。
【输入样例】
10 20 3
【输出样例】
7
【注意事项】
有 80%的数据 B<=1000.
样例解释{10,20,12,15,18},{13},{14},{16},{17},{19}。
题解
是一个合并的问题,考虑用到并查集,先线性筛出要用到的素数,每次将该素数倍数的数合并为一个集合,复杂度可看作线性
#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <cmath>
#define LL long long
using namespace std;
int prime[100100];
bool flag[100100];
void pre(){
for(int i=2;i<=100000;i++){
if(!flag[i])prime[++prime[0]]=i;
for(int j=1;j<=prime[0]&&i*prime[j]<=100000;j++){
flag[i*prime[j]]=true;
if(i%prime[j]==0)break;
}
}
}
int Fa[100100];
int findf(int x){
if(x==Fa[x])return x;
return Fa[x]=findf(Fa[x]);
}
void mergef(int x,int y){
Fa[findf(x)]=findf(y);
}
int A,B,P,ans;
int main(){
freopen("set.in","r",stdin);
freopen("set.out","w",stdout);
pre();
scanf("%d%d%d",&A,&B,&P);
for(int i=A;i<=B;i++)Fa[i]=i;
for(int i=1;i<=prime[0];i++){
if(prime[i]<P)continue;
if(prime[i]>B)break;
int s=(A/prime[i])*prime[i];
if(s<A)s+=prime[i];
for(int j=s;j<=B;j+=prime[i]){
mergef(s,j);
}
}
for(int i=A;i<=B;i++)
if(findf(i)==i)ans++;
printf("%d
",ans);
return 0;
}
Prison
【题目描述】
Caima 王国中有一个奇怪的监狱,这个监狱一共有 P 个牢房,这些牢房一字排开,第 i 个仅挨着第 i+1 个(最后一个除外)。现在正好牢房是满的。
上级下发了一个释放名单,要求每天释放名单上的一个人。这可把看守们吓得不轻,因为看守们 知道,现在牢房中的 P 个人,可以相互之间传话。如果某个人离开了,那么原来和这个人能说上话的 人,都会很气愤,导致他们那天会一直大吼大叫,搞得看守很头疼。如果给这些要发火的人吃上肉, 他们就会安静点。 现在看守们想知道,如何安排释放的顺序,才能使得他们花费的肉钱最少。
【输入格式】
第一行 2 个数 P 和 Q,Q 表示释放名单上的人数;
第二行 Q 个数,表示要释放哪些人。
【输出格式】
仅一行,表示最少要给多少人次送肉吃。
【数据规模】
1<=P<=1000;1<=Q<=100。
【输入样例】
20 3
3 6 14
【输出样例】
35
【注意事项】
Q<=P;且 50%的数据 1<=P<=100;1<=Q<=5。
题解
考虑记忆化搜索,对于每段牢房,我们枚举最先放出去的犯人,
之后左边的犯人和右边的犯人就没有任何关系了,
那么dp[l][r]=r-l+dp[l][m]+dp[m][r],
注意可以在0和n+1的位置插一个空牢房,以免特殊情况的判断
#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <cmath>
#define LL long long
#define INF (1<<30)
using namespace std;
int P,Q;
int rls[200];
int dp[200][200];
bool Find[200][200];
int dfs(int l,int r){
if(Find[l][r])return dp[l][r];
Find[l][r]=true;
if(l+1==r)return dp[l][r]=0;
dp[l][r]=INF;
for(int i=l+1;i<=r-1;i++)
dp[l][r]=min(dfs(l,i)+dfs(i,r),dp[l][r]);
dp[l][r]+=rls[r]-rls[l]-2;
return dp[l][r];
}
int main(){
freopen("prison.in","r",stdin);
freopen("prison.out","w",stdout);
scanf("%d%d",&P,&Q);
for(int i=1;i<=Q;i++)
scanf("%d",&rls[i]);
rls[0]=0;rls[Q+1]=P+1;
printf("%d
",dfs(0,Q+1));
}