三道水题

三道水题

今天考的三道题都十分的水,然而……

这也暴露出我组合数学及数论基本概念不熟练,还是写一下这两道水题吧。

A

时间限制:1s
内存限制:256M

题目描述

给定一个长度为 n 的序列 ai

现在对于一个数 x ,我们每次可以选择一个 k ,将 x 变为 x+ak 或者 xak ,一个数 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% 的数据 n2
对于 70% 的数据 n100
对于所有数据, 1n,m105 ,对于所有 1kn 1ak2311 ,对于所有 1km 1bk2311


不难发现: 对于 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% 的数据, k60
对于另外 10% 的数据,只有一组询问且这个询问中的 k=118
对于另外 10% 的数据,只有一组询问且这个询问中的 k=536870912
对于另外 10% 的数据,只有一组询问且这个询问中的 k=14007
对于另外 20% 的数据, k105
对于所有数据, 1k109
保证所有询问均有解。
数据组数在 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(1xnm+1) , S 中第 x,x+1,,x+m1 个位置的字符依次连成的字符串与 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% 的数据, q100
对于所有数据, S 非空且长度不超过 2105 ,询问串非空且总长不超过 2105 1q2105


考试的时候无脑树状数组,结果 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值