三道水题
今天考的三道题都十分的水,然而……
这也暴露出我组合数学及数论基本概念不熟练,还是写一下这两道水题吧。
A
时间限制:1s
内存限制:256M
题目描述
给定一个长度为 n 的序列 ai
现在对于一个数 x ,我们每次可以选择一个 k ,将 x 变为 x+ak 或者 x−ak ,一个数 x 被称为好的,当且仅当经过一系列操作之后,我们可以将 x 变为 0 。
现在给定一个长度为 m 的序列 bi ,你需要求出这个序列中好的数的个数。注意位置不同的两个数即使值相同,也视为两个不同的数。
输入格式
第一行一个正整数 n ;
第二行 n 个数,表示序列
ai
;
第三行一个正整数 m ;
第四行 m 个数,表示序列
bi
。
输出格式
输出一行一个整数,表示序列中好的数的个数。
样例
a.in
2
4 6
1 2
a.out
1
样例解释
2 可以通过减去 6 加上 4 得到 0 ,所以 2 是好的。
数据范围
对于
20%
的数据
n=1
;
对于
50%
的数据
n≤2
;
对于
70%
的数据
n≤100
。
对于所有数据,
1≤n,m≤105
,对于所有
1≤k≤n
有
1≤ak≤231−1
,对于所有
1≤k≤m
有
1≤bk≤231−1
。
不难发现: 对于 b 中的一个数,当且仅当其能被 a 中的所有数的最大公因数整除是,它是好的。
#include<iostream>
#include<cstdio>
using namespace std;
const int MAXN=100005;
int n,m;
int aa[MAXN],ba[MAXN];
int gcd(int a,int b)
{return b?gcd(b,a%b):a;}
int main()
{
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
int i,ans=0;
scanf("%d",&n);
for(i=1;i<=n;i++) scanf("%d",&aa[i]);
scanf("%d",&m);
for(i=1;i<=m;i++) scanf("%d",&ba[i]);
int g=aa[1];
for(i=2;i<=n;i++) g=gcd(g,aa[i]);
for(i=1;i<=m;i++) ans+=(ba[i]%g)==0;
printf("%d\n",ans);
}
B
时间限制:1s
内存限制:256M
题目描述
给定正整数 k ,求构造一棵树,是的包含了 1 号节点的连通子图个数恰好为 k 。
连通子图可以为全集,但不可为空集。
输入格式
多行,每行一组测试数据,一行一个正整数 k 。
输出格式
输出多行,对于每一组数据,第一行输出一个数 n 表示树的点数,接下来输出 n-1 行,每行两个数 u 和 v ,用空格隔开,表示编号为 u 的点和编号为 v 的点之间有一条树边。编号从 1 开始。相邻两组数据的答案不需要用任何空行隔开。
输出保证答案一定存在,你输出的 n 必须是正整数且不得超过 60 对每个询问你只需要输出任意一个合法的解即可。
样例输出
b.in
4
2 3
1 2
1 4
b.out
16
对于
20%
的数据,
k≤60
;
对于另外
10%
的数据,只有一组询问且这个询问中的
k=118
;
对于另外
10%
的数据,只有一组询问且这个询问中的
k=536870912
;
对于另外
10%
的数据,只有一组询问且这个询问中的
k=14007
;
对于另外
20%
的数据,
k≤105
;
对于所有数据,
1≤k≤109
。
保证所有询问均有解。
数据组数在 1 到 1000 之间。
利用二进制的思想,
#include<iostream>
#include<cstdio>
using namespace std;
struct E{int from,to;} e[105];
int main()
{
freopen("b.in","r",stdin);
freopen("b.out","w",stdout);
int i,k;
while(scanf("%d",&k)!=EOF)
{
int ecnt=0,st=1;
for(i=1;i<=k;i<<=1)
{
if(i>1) e[++ecnt]=(E){st,ecnt+1};
if((i&k)&&((i<<1)<=k)) e[++ecnt]=(E){st,ecnt+1},st=ecnt+1;
}
printf("%d\n",ecnt+1);
for(i=1;i<=ecnt;i++) printf("%d %d\n",e[i].from,e[i].to);
}
return 0;
}
C
题目描述
给定一个长度为 n 的 01串 S ,所谓 01串 就是指所有字符都是 0 或者 1 的字符串。
对于两个 01串 a 和 b ,我们每次可以选择 a 中两个不同位置并交换这两个位置的字符,如果经过一系列操作可以让 a 和 b 完全相同,那么我们就称这两个串相似。
对于一个长度为 m 的串 s ,如果对于一个 x(1≤x≤n−m+1) , S 中第 x,x+1,…,x+m−1 个位置的字符依次连成的字符串与 s 相似,那么称 s 在 x 处与 S 匹配。
现在给定 S 以及若干个 s ,你要对每一个 s 求出有多少个 x 是的 s 在 x 处与 S 匹配。
输入格式
第一行一个 01串 表示 S 。 n 的值就是 S 的长度;
接下来一个正整数 q 表示询问的个数;
接下来 q 行,每行一个 01串 表示询问串。
输出格式
输出 q 行,每行一个数表示对应的 x 个数。
样例
c.in
1010
4
1
10
101
1010
c.out
2
3
1
1
样例解释
对于第一个询问,
x=1,3
满足条件;
对于第二个询问,
x=1,2,3
满足条件;
对于后两个询问,
x=1
满足条件。
数据范围
对于
20%
的数据, S 的长度不超过 100 ;
对于
50%
的数据,
q≤100
;
对于所有数据, S 非空且长度不超过
2∗105
,询问串非空且总长不超过
2∗105
,
1≤q≤2∗105
考试的时候无脑树状数组,结果 20 ,实际上暴力能得 50 ,而正解无非就是通过数组记录长度较小的串的结果,从而以空间换时间。
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int MAXL=450;
char S[MAXL*MAXL],s[MAXL*MAXL];
int q,SL,sL;
int preSum[MAXL*MAXL],r[MAXL][MAXL];
bool vis[MAXL];
int main()
{
freopen("c.in","r",stdin);
freopen("c.out","w",stdout);
int i,j;
scanf("%s",S+1);SL=strlen(S+1);
for(i=1;i<=SL;i++) preSum[i]=preSum[i-1]+S[i]-'0';
scanf("%d",&q);
while(q--)
{
int ans=0;
scanf("%s",s+1);sL=strlen(s+1);
int rv=0;
for(j=1;j<=sL;j++) rv+=s[j]-'0';
if(sL<MAXL)
{
if(!vis[sL])
{
vis[sL]=true;
for(j=1;j<=SL-sL+1;j++)
++r[sL][preSum[j+sL-1]-preSum[j-1]];
}
ans=r[sL][rv];
}
else
{
for(j=1;j<=SL-sL+1;j++)
if(rv==preSum[j+sL-1]-preSum[j-1])
++ans;
}
printf("%d\n",ans);
}
return 0;
}