2017-4-6校内训练

cf训练赛

A.题目大意:给出n个数,问能否选出若干个数,使得这些数的和为m的倍数。(n<=10^6,m<=10^3)

思路:用f[i][j]表示前i个数是否能选出若干个和模m为j的数,直接DP,当f[i][0]为true时直接返回,虽然看上去是O(nm),但我们可以发现,当n超过m时答案必为真,证明如下:每次加入一个数x,若x不能被前i-1个数表示,我们令f[i][x]=true;若x能被前i-1个数表示,则2x能被前i个数表示,以此类推,每次加入x,至少会有一个原来为false的被设为了true,否则说明我们找到了m的倍数。总复杂度O(m^2)。

#include<cstdio>
inline int read()
{
    int x;char c;
    while((c=getchar())<'0'||c>'9');
    for(x=c-'0';(c=getchar())>='0'&&c<='9';)x=(x<<3)+(x<<1)+c-'0';
    return x;
}
#define MN 1000
int u[2][MN+5];
int main()
{
    int n,m,x,i;
    n=read();m=read();
    while(n--)
    {
        u[n&1][x=read()%m]=1;
        for(i=0;i<m;++i)if(u[n&1^1][i])u[n&1][i]=u[n&1][(i+x)%m]=1;
        if(u[n&1][0])return 0*puts("YES");
    }
    puts("NO");
}

B.题目大意:给出一个1~n的置换,要求构造一棵n个点的树,使得树上每条边两端点编号置换后得到的边仍然出现在树中。(n<=10^5)

思路:先把置换拆成若干个循环,如果有长度为1的循环,那么其他所有点连到这个点即可,否则只有所有循环长度均为偶数且存在长度为2的循环才可以构造,选出一个长度为2的循环,其他所有点根据在循环中位置的奇偶分别连向这个循环的两个点,最后把这两个点自己连起来即可。

#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
inline int read()
{
    int x;char c;
    while((c=getchar())<'0'||c>'9');
    for(x=c-'0';(c=getchar())>='0'&&c<='9';)x=(x<<3)+(x<<1)+c-'0';
    return x;
}
#define MN 100000
int a[MN+5],u[MN+5],cnt;
vector<int> v[MN+5];
int main()
{
    int n=read(),i,j,s,s1=0,s2=0,o=0;
    for(i=1;i<=n;++i)a[i]=read();
    for(i=1;i<=n;++i)if(!u[i])
    {
        for(++cnt,j=i;!u[j];u[j]=1,j=a[j])v[cnt].push_back(j);
        s=v[cnt].size();
        if(s==1)s1=cnt;
        if(s==2)s2=cnt;
        o|=s&1;
    }
    if(!s1&&(o||!s2))return 0*puts("NO");
    puts("YES");
    for(i=1;i<=cnt;++i)
        if(s1)for(j=0;j<v[i].size();++j){if(i!=s1)printf("%d %d
",v[i][j],v[s1][0]);}
        else if(i!=s2)for(j=0;j<v[i].size();++j)printf("%d %d
",v[i][j],v[s2][j&1]);
    if(!s1)printf("%d %d
",v[s2][0],v[s2][1]);
}

C.题目大意:你有n张图片,你一开始翻到第1张,每次从第i张切换到第i-1或第i+1张需要a的时间(1可以切换到n,n可以切换到1),每张图片的朝向可能不太对,如果碰见朝向不对的,你需要花b时间转过来,每次你碰见第一次看到的图片,你会花1时间看它,问在t时间内最多看多少张图片。(n<=500,000)

思路:只会有两种情况,一种先往左边翻,然后再翻到右边,另一种先翻到右边再翻到左边,若只考虑先翻右的(另一种同理),可以预处理出从第1张翻到左边每一张时的花费,从左往右枚举右端点,显然左端点也递增,拿两个指针直接统计即可,总复杂度O(n)。

#include<cstdio>
#include<algorithm>
using namespace std;
#define MN 500000
char g[MN+5];
int n,a,b,t,ans,f[MN+5];
void solve()
{
    int i,j,s;
    for(i=n;i--;)f[i]=f[i+1]+a+b*(g[i]=='w')+1;
    for(i=j=s=0;i<n;++i,s+=a)
    {
        s+=b*(g[i]=='w')+1;
        if(s>t)break;
        ans=max(ans,i+1);
        if(s+i*a<=t)
        {
            while(j<=i||s+f[j]+i*a>t)++j;
            ans=max(ans,i+1+n-j);
        }
    }
}
int main()
{
    scanf("%d%d%d%d%s",&n,&a,&b,&t,g);
    solve();
    for(int i=1;i<n-i;++i)swap(g[i],g[n-i]);
    solve();
    printf("%d",ans);
}

D.题目大意:一个1~n的排列一开始为1,2,3,…,n,支持两种操作:1.循环右移k位;2.交换当前第1个和第2个,第3个和第4个……保证n为偶数。(n<=1,000,000,操作数<=2,000,000)

思路:把排列编号为0~n-1,两种操作分别表示为x=(x+k)mod n,x=x xor 1,显然如果奇偶性相同,偏移量也相同,两种情况都算一下即可,复杂度O(n)。

#include<cstdio>
inline int read()
{
    int x,f=1;char c;
    while((c=getchar())<'0'||c>'9')if(c=='-')f=0;
    for(x=c-'0';(c=getchar())>='0'&&c<='9';)x=(x<<3)+(x<<1)+c-'0';
    return f?x:-x;
}
#define MN 1000000
int ans[MN+5];
int main()
{
    int n=read(),i,t,x,s0=0,s1=1;
    for(i=read();i--;)
    {
        t=read();
        if(t==1)x=read(),s0=(s0+x)%n,s1=(s1+x)%n;
        else s0^=1,s1^=1;
    }
    for(i=1;i<=n;++i)ans[(i+(i&1?s0:s1-1)+n)%n]=i;
    for(i=1;i<=n;++i)printf("%d ",ans[i%n]);
}

E.题目大意:给出n个数ci和一个数k,问是否对于任意的x,若确定所有x%ci,则可确定x%k。(n,k,ci<=1,000,000)

思路:显然要确定x%k的条件为k|lcm(ci),筛法预处理后把ci和k分解质因数,对每个质因子统计一下即可。

#include<cstdio>
#include<algorithm>
using namespace std;
inline int read()
{
    int x;char c;
    while((c=getchar())<'0'||c>'9');
    for(x=c-'0';(c=getchar())>='0'&&c<='9';)x=(x<<3)+(x<<1)+c-'0';
    return x;
}
#define MN 1000000
int f[MN+5],p[MN+5],pn,s[MN+5];
int main()
{
    int n,k,i,j,cnt;
    for(i=2;i<=MN;++i)
    {
        if(!f[i])f[i]=i,p[++pn]=i;
        for(j=1;i*p[j]<=MN;++j){f[i*p[j]]=p[j];if(i%p[j]==0)break;}
    }
    n=read();k=read();
    while(n--)for(i=read();i>1;)
    {
        for(j=f[i],cnt=0;f[i]==j;i/=j)++cnt;
        s[j]=max(s[j],cnt);
    }
    while(k>1)
    {
        for(j=f[k],cnt=0;f[k]==j;k/=j)++cnt;
        if(s[j]<cnt)return 0*puts("No");
    }
    puts("Yes");
}
原文地址:https://www.cnblogs.com/ditoly/p/20170406C.html