[4.6校内训练赛]

来自FallDream的博客,未经允许,请勿转载,谢谢。


A.[cf577b]Modulo Sum

给定n个数,问是否有一个子序列相加的和取余m为0  n<=10^6,m<=1000

题解:很容易想到nmDP,(我写了暴力1950ms过了哈哈哈),另外m<n的时候肯定有可以整除m的,因为前缀和的余数最多只有m种。

代码丑,其实就是暴力加了一句话.....nmDP大家都会,就不重写了

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
inline int read()
{
    int x = 0 , f = 1; char ch = getchar();
    while(ch < '0' || ch > '9'){ if(ch == '-') f = -1;  ch = getchar();}
    while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
    return x * f;
}

int b[1005],n,m,f[1005][1005],mark[1005];

int main()
{
    n=read();m=read();
    if(m<n)return 0*puts("YES");
    for(int i=1;i<=n;i++)b[read()%m]++;
    n=0;
    for(int i=0;i<m;i++)mark[i]=-1;
    for(int i=0;i<m;i++)
    { 
        if(!b[i]) continue;
        ++n;
        for(int j=0;j<m;j++)
            f[n][j]|=f[n-1][j];
        for(int k=1;k<=b[i];k++)
        {
            int th=k*i%m;
            if(mark[th]==i) break;mark[th]=i;
            f[n][th]=1;
            for(int j=0;j<m;j++) 
                f[n][(th+j)%m]|=f[n-1][j];
    //        printf("%d
",th);
        }
    } 
    if(f[n][0]) puts("YES");
    else puts("NO");
    return 0;
}

B.[cf575b]Invariance of Tree

给定n个数pi,问是否能构造一棵树,对于每条边连接的i,j, pi和pj也有连边   n<=100000

题解:首先把i->pi连边,变成很多个环,如果存在一个点,那么全部连它即可。不然必须满足有一个长度为2的环,全部环都要是偶数,这样子就能对称的连起来。

#include<iostream>
#include<cstdio>
#include<cstring>
#define MN 100000
using namespace std;
inline int read()
{
    int x = 0 , f = 1; char ch = getchar();
    while(ch < '0' || ch > '9'){ if(ch == '-') f = -1;  ch = getchar();}
    while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
    return x * f;
}

int n,p[MN+5],q[MN+5],top=0,sz,len=-1;
bool mark[MN+5];
int flag,only,odd;

void work(int x)
{
    int y=flag;
    for(;!mark[x];x=p[x],y=p[y])
        printf("%d %d
",x,y),mark[x]=1;
}

int main()
{
    n=read();
    for(int i=1;i<=n;i++) p[i]=read();
    for(int i=1;i<=n;i++) if(!mark[i])
    {
        sz=1;mark[i]=1;q[++top]=i;
        for(int j=p[i];!mark[j];j=p[j]) mark[j]=1,sz++;
        if(sz==2) flag=i;
        else if(sz==1) only=i;
        else if(sz&1) odd=true;
    }
    if(only) 
    {
        puts("YES");
        for(int i=1;i<=n;i++)if(i!=only) printf("%d %d
",i,only);
        return 0;
    }
    if(odd||!flag)return 0*puts("NO");
    puts("YES");
    printf("%d %d
",flag,p[flag]);
    memset(mark,0,sizeof(mark));
    mark[flag]=1,mark[p[flag]]=1;
    for(int i=1;i<=n;i++)if(!mark[i]) work(i);
    return 0;
}

C.[cf651d]Image Preview

有n张,两种的照片围起来,一种照片看需要1s,另一种需要b+1s,你一开始在一号照片处,移动一次要a s,你只有Ts的时间,求你最多能看多少。

题解:显然情况只有三种,只看左右和同时看左右。前两种容易处理,剩下一种我们就二分一下最远点就行了。

#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
#define MN 1000000
#define INF 200000000
using namespace std;
inline int read()
{
    int x = 0 , f = 1; char ch = getchar();
    while(ch < '0' || ch > '9'){ if(ch == '-') f = -1;  ch = getchar();}
    while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
    return x * f;
}

int n,a,b,ans=0;ll f[MN+5],T;
char st[MN+5];

int get(ll x,int i)
{
//    cout<<"get"<<x<<" "<<i<<endl;
    int l=i+1-n,r=n,mid,ans=INF;
    while(l<=r)
    {
        mid=l+r>>1;
        if(f[mid]<=x) ans=mid,r=mid-1;
        else l=mid+1;
    }
    return ans;
}

int get2(ll x,int i)
{
//    cout<<"get2 "<<x<<" "<<i<<endl;
    int l=n+2,r=i+n-1,mid,ans=-INF;
    while(l<=r)
    {
        mid=l+r>>1;
        if(f[mid]<=x) ans=mid,l=mid+1;
        else r=mid-1;
    }
    return ans;
}

int main()
{
    n=read();a=read();b=read();T=read();
    scanf("%s",st+1);
    for(int i=1;st[i];i++) f[i]=f[i+n]=st[i]=='w'?b+1:1;
    for(int i=n;i;i--) f[i]+=f[i+1]+a;
    for(int i=n+2;i<=n<<1;i++) f[i]+=f[i-1]+a;
    for(int i=n+1;i<=n<<1;i++) 
    {
        if(f[i]>T) break;
        ans=max(ans,i-n);
        int L=get(T-f[i]+f[n+1]-a*(i-n-1),i);
    //    cout<<i<<" "<<L<<endl;
        ans=max(ans,i-L+1);
    }
    for(int i=n;i>=2;i--)
    {
        if(f[i]>T)break;
        ans=max(ans,n+2-i);
        int R=get2(T-f[i]+f[n+1]-a*(n+1-i),i);
    //    cout<<i<<" "<<R<<endl;
        ans=max(ans,R-i+1);
    }
    printf("%d
",ans);
    return 0;
}

D.[cf669d]Little Artem and Dance
一开始有n个数1-n,有两种变换,一种是全部移动,一种是把现在的12,34,56......位的数字相互交换,求最后的数字顺序   n,q<=1000000

题解:大模拟,移动容易处理,交换的话根据目前的状况特殊处理一下。

#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
#define MN 1000000
#define INF 200000000
using namespace std;
inline int read()
{
    int x = 0 , f = 1; char ch = getchar();
    while(ch < '0' || ch > '9'){ if(ch == '-') f = -1;  ch = getchar();}
    while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
    return x * f;
}

int rev=0,move=0,n,m,change=0;
int ans[1000005];

int main()
{
    n=read();m=read();
    for(int i=1;i<=m;i++)
    {
        int op=read();
        if(op==1) move=(move+read()+n)%n;
        else 
        {
            if(!rev) rev=(move&1)?1:2;
            else
            if(move&1)
            {
                 if(rev==2) change+=1;
                 rev=0;
            }
            else
            {
                if(rev==1) change-=1;
                rev=0;
            }
        }
    }
    for(int i=1;i<=n;i++)
    {
        int pos=i+move+change*((i&1)?2:-2);while(pos<n) pos+=n;
        if(rev){if((pos&1)^(move&1)==(rev&1))pos--;else pos++;}
        pos=(pos+n-1)%n+1;
        ans[pos]=i;
    }
    for(int i=1;i<=n;i++) printf("%d ",ans[i]);
    return 0;
}

E.[cf687b]Remainers Game
给定k和n个数ai,你可以知道所有x%ai的值,求是否可以知道x%k的值。

题解:根据中国剩余定理,你可以猜出x%k的值,当且仅当存在m1,m2,m3..mm使得m1m2m3...mm=k且m1,m2,m3互质。所以分解x=p1^k1*p2^k2....,只要对于每个pi^ki都有数是他的倍数就行了。这样的话直接求lcm就好了

#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
#define MN 1000000
#define INF 200000000
using namespace std;
inline int read()
{
    int x = 0 , f = 1; char ch = getchar();
    while(ch < '0' || ch > '9'){ if(ch == '-') f = -1;  ch = getchar();}
    while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
    return x * f;
}

int n,k,a[MN+5],mn=0;
bool b[MN+5],has[MN+5];
int last[MN+5],num[MN+5];

inline ll gcd(ll x,ll y){return (!y)?x:gcd(y,x%y);}

int main()
{
    n=read();k=read();if(!k)return 0*puts("Yes");
    for(int i=1;i<=n;i++)
    {
        a[i]=read();
        if(a[i]%k==0)return 0*puts("Yes");
    }
    ll ans=1;
    for(int i=1;i<=n;i++)
          ans=1LL*ans/gcd(ans,a[i])*a[i]%k;      
    if(!ans)puts("Yes");
    else puts("No");
    return 0;
}
原文地址:https://www.cnblogs.com/FallDream/p/xunlian46.html