11.14的一套题

全国信息学奥林匹克联赛(NOIP2014)复赛

模拟题Day1

 

题目名称

正确答案

序列问题

长途旅行

英文名称

answer

sequence

travel

输入文件名

answer.in

sequence.in

travel.in

输出文件名

answer.out

sequence.out

travel.out

时间限制

1s

1s

1s

空间限制

256M

256M

256M

测试点数目

20

20

10

测试点分值

5

5

10

是否有部分分

题目类型

传统

传统

传统

是否有SPJ

 

1.正确答案

【题目描述】

小H与小Y刚刚参加完UOIP外卡组的初赛,就迫不及待的跑出考场对答案。

“吔,我的答案和你都不一样!”,小Y说道,”我们去找神犇们问答案吧”。

外卡组试卷中共有m道判断题,小H与小Y一共从其他n个神犇那问了答案。之后又从小G那里得知,这n个神犇中有p个考了满分,q个考了零分,其他神犇不为满分或零分。这可让小Y与小H犯了难。你能帮助他们还原出标准答案吗?如有多解则输出字典序最小的那个。无解输出-1。

 

【输入格式】

第一行四个整数n, m, p, q,意义如上描述。

接下来n行,每一行m个字符’N’或’Y’,表示这题这个神犇的答案。

 

【输出格式】

仅一行,一个长度为m的字符串或是-1。

 

【样例输入】

2 2 2 0

YY

YY

 

【样例输出】

YY

 

【数据范围】

30% : n <= 100.

60% : n <= 5000 , m <= 100.

100% : 1 <= n <= 30000 , 1 <= m <= 500.  0 <= p , q 且 p + q <= n.

/*
MAP
80分
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#define maxn 30010
using namespace std;
int n,m,p,q;
char in[510];
string s[maxn],t,r,w,ans;
map<string,int>f;
int cmp(int a,int b)
{
    for(int i=0;i<m;i++)
    {
        if(s[a][i]==s[b][i])continue;
        return s[a][i]<s[b][i];
    }
    return 0;
}
int Cmp(int a,int b)
{
    for(int i=0;i<m;i++)
    {
        if(s[a][i]==s[b][i])continue;
        return s[a][i]>s[b][i];
    }
    return 0;
}
void work1()
{
    int flag=0;
    for(int i=0;i<m;i++)t+='N';
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<m;j++)
        {
            if(s[i][j]=='Y')t[j]='N';
            if(s[i][j]=='N')t[j]='Y';
        }
        int sum=f[s[i]],tot=f[t];
        if(sum==p&&tot==q&&(!flag||cmp(i,flag)))flag=i;
    }
    if(flag)
    {
        for(int i=0;i<m;i++)
          cout<<s[flag][i];
        cout<<endl;
    }
    else cout<<"-1"<<endl;
}
void work2()
{
    int flag=0;
    for(int i=0;i<m;i++)t+='N';
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<m;j++)
        {
            if(s[i][j]=='Y')t[j]='N';
            if(s[i][j]=='N')t[j]='Y';
        }
        int sum=f[t],tot=f[s[i]];
        if(sum==p&&tot==q&&(!flag||Cmp(i,flag)))flag=i;
    }
    if(flag)
    {
        for(int i=0;i<m;i++)
        {
            if(s[flag][i]=='Y')cout<<"N";
            if(s[flag][i]=='N')cout<<"Y";
        }
        cout<<endl;
    }
    else cout<<"-1"<<endl;
}
void work3()
{
    for(int i=0;i<m;i++)
      r+='N',w+='Y';
    while(1)
    {
        int ha=f[r],he=f[w];
        if(ha==p&&he==q)
        {
            for(int i=0;i<m;i++)
              cout<<r[i];
            cout<<endl;
            return;
        }
        int flag=-1;
        for(int i=m-1;i>=0;i--)
        {
            if(r[i]=='N')
            {
                flag=i;
                break;
            }
        }
        if(flag==-1)break;
        r[flag]='Y';w[flag]='N';
        for(int i=flag+1;i<m;i++)
          r[i]='N',w[i]='Y';
        /*
        for(int i=0;i<m;i++)cout<<r[i];
        cout<<endl;
        */
    }
    cout<<"-1"<<endl;
}
int main()
{
    freopen("answer.in","r",stdin);
    freopen("answer.out","w",stdout);
    scanf("%d%d%d%d",&n,&m,&p,&q);
    for(int i=1;i<=n;i++)
    {
        scanf("%s",in);
        for(int j=0;j<m;j++)s[i]+=in[j];
        ++f[s[i]];
    }
    if(p)work1();
    else if(q)work2();
    else work3();
    return 0;
}
View Code
/*
对于p!=0||q!=0的
枚举给的中哪个是正确(错误)的
看以这个为答案有多少正确 错误并判断是否等于p,q
开始时给其排一遍序 找到就停下

对于p==0&&q==0枚举答案 
为了保证刚开始让答案全是N
每次看这次的答案 是否有和他全相同的或全不一样的 若没有就是符合的答案 
否则 从后往前找N 找到后把他变成Y 后面的都变成N 在判断 
这样大约在O(n)内就可以出答案 
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#define maxn 30010
using namespace std;
int n,m,p,q;
char in[510];
string s[maxn],t,r,w,ans;
map<string,int>f;
void work1()
{
    int flag=0;
    for(int i=0;i<m;i++)t+='N';
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<m;j++)
        {
            if(s[i][j]=='Y')t[j]='N';
            if(s[i][j]=='N')t[j]='Y';
        }
        int sum=f[s[i]],tot=f[t];
        if(sum==p&&tot==q)
        {
            flag=i;
            break;
        }
    }
    if(flag)
    {
        for(int i=0;i<m;i++)
          cout<<s[flag][i];
        cout<<endl;
    }
    else cout<<"-1"<<endl;
}
void work2()
{
    int flag=0;
    for(int i=0;i<m;i++)t+='N';
    for(int i=n;i>=1;i--)
    {
        for(int j=0;j<m;j++)
        {
            if(s[i][j]=='Y')t[j]='N';
            if(s[i][j]=='N')t[j]='Y';
        }
        int sum=f[t],tot=f[s[i]];
        if(sum==p&&tot==q)
        {
            flag=i;
            break;
        }
    }
    if(flag)
    {
        for(int i=0;i<m;i++)
        {
            if(s[flag][i]=='Y')cout<<"N";
            if(s[flag][i]=='N')cout<<"Y";
        }
        cout<<endl;
    }
    else cout<<"-1"<<endl;
}
void work3()
{
    for(int i=0;i<m;i++)
      r+='N',w+='Y';
    while(1)
    {
        int ha=f[r],he=f[w];
        if(ha==p&&he==q)
        {
            for(int i=0;i<m;i++)
              cout<<r[i];
            cout<<endl;
            return;
        }
        int flag=-1;
        for(int i=m-1;i>=0;i--)
        {
            if(r[i]=='N')
            {
                flag=i;
                break;
            }
        }
        if(flag==-1)break;
        r[flag]='Y';w[flag]='N';
        for(int i=flag+1;i<m;i++)
          r[i]='N',w[i]='Y';
        /*
        for(int i=0;i<m;i++)cout<<r[i];
        cout<<endl;
        */
    }
    cout<<"-1"<<endl;
}
int main()
{
    freopen("answer.in","r",stdin);
    freopen("answer.out","w",stdout);
    scanf("%d%d%d%d",&n,&m,&p,&q);
    for(int i=1;i<=n;i++)
    {
        scanf("%s",in);
        for(int j=0;j<m;j++)s[i]+=in[j];
        ++f[s[i]];
    }
    sort(s+1,s+n+1);
    if(p)work1();
    else if(q)work2();
    else work3();
    return 0;
}

2.序列问题

【题目描述】

小H是个善于思考的学生,她正在思考一个有关序列的问题。

她的面前浮现出了一个长度为n的序列{ai},她想找出两个非空的集合S、T。

这两个集合要满足以下的条件:

1. 两个集合中的元素都为整数,且都在 [1, n] 里,即Si,Ti ∈ [1, n]。

2. 对于集合S中任意一个元素x,集合T中任意一个元素y,满足x < y。

3. 对于大小分别为p, q的集合S与T,满足

            a[s1] xor a[s2] xor a[s3] ... xor a[sp] = a[t1] and a[t2] and a[t3] ... and a[tq].

小H想知道一共有多少对这样的集合(S,T),你能帮助她吗?

 

【输入格式】

       第一行,一个整数n

       第二行,n个整数,代表ai。

 

【输出格式】

       仅一行,表示最后的答案。

 

【样例输入】

4

1 2 3 3

 

【样例输出】

4

 

【样例解释】

S = {1,2}, T = {3}, 1 ^ 2 = 3 = 3 (^为异或)

S = {1,2}, T = {4},  1 ^ 2 = 3 = 3

S = {1,2}, T = {3,4}  1 ^ 2 = 3 & 3 = 3 (&为与运算)

S = {3}, T = {4}  3 = 3 = 3

【数据范围】

    30%: 1 <= n <= 10

    60%: 1 <= n <= 100

    100%: 1 <= n <= 1000, 0 <= ai < 1024

/*
30分
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#define LL long long
#define maxx 1010
#define maxn (1<<11)-1+100
using namespace std;
int n;int a[maxn];
int f[maxx][maxn],g[maxx][maxn];
LL ans;
int init()
{
    int x=0,f=1;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
    return x*f;
}
void dfs(int x,int t,int sum)
{
    if(x==n+1)return;
    dfs(x+1,t,sum);
    f[x][sum^a[x]]++;
    dfs(x+1,t+1,sum^a[x]);
}
void DFS(int x,int t,int sum)
{
    if(x==0)return;
    DFS(x-1,t,sum);
    g[x][sum&a[x]]++;
    DFS(x-1,t+1,sum&a[x]);
}
int main()
{
    freopen("sequence.in","r",stdin);
    freopen("sequence.out","w",stdout);
    n=init();
    for(int i=1;i<=n;i++)a[i]=init();
    dfs(1,0,0);
    DFS(n,0,(1<<11)-1); 
    for(int i=1;i<=n;i++)
      for(int k=i+1;k<=n;k++)
        for(int j=0;j<=((1<<11)-1);j++)
          ans+=f[i][j]*g[k][j];
    cout<<ans<<endl;
    return 0;
}
View Code

粘过来的题解:

/*
T2:
30%:枚举每个数所在的集合或者不选,然后判定即可。复杂度 O(n*3^n)。
60%: Dp,两个数相等就相当于两个数的 xor 为 0。设 f[i][j][k=0..2]代表 处理到第 I 个数,
如果 k = 1 代表 and 值为 j,如果 k = 2 代表 xor 值为 j,如果 k = 0 则代表一个元素都没
取。所以很容易得到方程:
f[i][j][0] = f[i + 1][j][0]
f[i][j & ai][1] = f[i + 1][j][1] + f[i + 1][j][0] + f[i + 1][j & ai][1]
f[i][j ^ ai][2] = f[i + 1][j][1] + f[i + 1][j][2] + f[i + 1][j ^ ai][2];
最后 f[1][0][2]就是答案, 复杂度为 O(n * 1024 * 3)
DP 还可以分开用 f[i][j]和 g[i][j]表示前 i 个 xor 值为 j,后 i 个 and 值为 j 的方案数,
随后枚举分界点 k 来求总方案数。复杂度 O(n * 1024 * 3)。
100%:满分数据需要高精,答案位数较大,需要进行压位来防止 TLE,因为不知道答案的
位数究竟多大,压位后高精数组仍需要开的较大一些,所以原 DP 的数组滚动即可。
*/

AC代码:无

3.长途旅行

【题目描述】

JY是一个爱旅游的探险家,也是一名强迫症患者。现在JY想要在C国进行一次长途旅行,C国拥有n个城市(编号为0,1,2...,n - 1),城市之间有m条道路,可能某个城市到自己有一条道路,也有可能两个城市之间有多条道路,通过每条道路都要花费一些时间。JY从0号城市开始出发,目的地为n – 1号城市。由于JY想要好好参观一下C国,所以JY想要旅行恰好T小时。为了让自己的旅行更有意思,JY决定不在任何一个时刻停留(走一条到城市自己的路并不算停留)。JY想知道是否能够花恰好T小时到达n – 1号城市(每个城市可经过多次)。现在这个问题交给了你。

若可以恰好到达输出“Possible”否则输出“Impossible”。(不含引号)。

 

【输入格式】

第一行一个正整数Case,表示数据组数。

       每组数据第一行3个整数,分别为n, m, T。

       接下来m行,每行3个整数x, y, z,代表城市x和城市y之间有一条耗时为z的双向边。

 

【输出格式】

       对于每组数据输出”Possible”或者”Impossible”.

 

【样例输入】

2

3 3 11

0 2 7

0 1 6

1 2 5

2 1 10000

1 0 1

 

【样例输出】

Possible

Impossible

 

【样例解释】

       第一组:0 -> 1 -> 2 :11

       第二组:显然偶数时间都是不可能的。

 

【数据范围】

  30%:  T <= 10000

  另有30%: n <= 5 , m <= 10.

  100%: 2 <= n <= 50 , 1 <= m <= 100 , 1 <= z <= 10000 , 1 <= T <= 10^18 , Case <= 5.

/*
30分
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define maxx 60
#define maxn 110
using namespace std;
int Case,n,m,T,topt;
int first[maxx];
int f[maxx][10010];
struct edge
{
    int to;
    int val;
    int next;
}e[maxn*2];
queue<int>q,t;
int init()
{
    int x=0,f=1;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
    return x*f;
}
void Clean()
{
    topt=0;
    for(int i=1;i<=n;i++)
      first[i]=0;
}
void add(int x,int y,int z)
{
    topt++;
    e[topt].to=y;
    e[topt].val=z;
    e[topt].next=first[x];
    first[x]=topt;
}
void spfa()
{
    while(!q.empty())q.pop();
    while(!t.empty())t.pop();
    for(int i=1;i<=n;i++)
      for(int j=0;j<=T;j++)
        f[i][j]=0;
    f[1][0]=1;q.push(1);t.push(0);
    while(!q.empty())
    {
        int now=q.front();q.pop();
        int c=t.front();t.pop();
        if(now==n&&c==T)
        {
            printf("Possible\n");
            return;
        }
        for(int i=first[now];i;i=e[i].next)
        {
            int to=e[i].to,tc=c+e[i].val;
            if(tc>T)continue;
            if(to==n&&tc==T)
            {
                printf("Possible\n");
                return;
            }
            if(!f[to][tc])f[to][tc]=1,q.push(to),t.push(tc);
        }
    }
    printf("Impossible\n");
}
int main()
{
    freopen("travel.in","r",stdin);
    freopen("travel.out","w",stdout);
    Case=init();
    while(Case--)
    {
        n=init();m=init();T=init();Clean();
        for(int i=1;i<=m;i++)
        {
            int x,y,z;
            x=init();y=init();z=init();
            x++,y++;add(x,y,z);add(y,x,z);
        }
        spfa();
    }
    return 0;
}
View Code
/*
如果 0 -> x -> n - 1 路径时间为 T,且 从 x 出发有一个时
间为 d 的环,则 一定存在一个 K 满足 K + p*d = T(至少 T 满足条件) ,
这样我们就能绕着环走 p 次就能构成一条时间为 T 的路径。

因为一定走0所以从与0相连的边中选一条作为环 则d=min(与0相连的边的权值)*2
-->(不一定最小)

dis[i][j]表示到i走了模d余j的路程中的最小的路程
最后如果dis[n][T%d]<=T 那么再多走上几个d就OK了 
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define LL long long
#define inf 1e17
#define maxx 110
#define maxn 110
using namespace std;
LL Case,n,m,T,topt,d;
LL first[maxx];
LL f[maxx][50010],dis[maxn][50010];
struct edge
{
    LL to;
    LL val;
    LL next;
}e[maxn*2];
queue<LL>q,t;
LL init()
{
    LL x=0,f=1;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
    return x*f;
}
void add(LL x,LL y,LL z)
{
    topt++;
    e[topt].to=y;
    e[topt].val=z;
    e[topt].next=first[x];
    first[x]=topt;
}
void spfa()
{
    for(LL i=1;i<=n;i++)
      for(LL j=0;j<=d;j++)
        dis[i][j]=inf,f[i][j]=0;
    q.push(1);t.push(0);dis[1][0]=0;f[1][0]=1;
    while(!q.empty())
    {
        LL now=q.front();q.pop();
        LL nc=t.front();t.pop();f[now][nc]=0;
        for(LL i=first[now];i;i=e[i].next)
        {
            LL to=e[i].to,tc=nc+e[i].val;tc%=d;
            if(dis[to][tc]>dis[now][nc]+e[i].val)
            {
                dis[to][tc]=dis[now][nc]+e[i].val;
                if(!f[to][tc])f[to][tc]=1,q.push(to),t.push(tc);
            }
        }
    }
    if(dis[n][T%d]<=T) printf("Possible\n");
    else printf("Impossible\n");
}
int main()
{
    freopen("travel.in","r",stdin);
    freopen("travel.out","w",stdout);
    Case=init();
    while(Case--)
    {
        n=init();m=init();T=init();d=inf;
        topt=0;for(LL i=1;i<=n;i++)first[i]=0;
        for(LL i=1;i<=m;i++)
        {
            LL x,y,z;
            x=init();y=init();z=init();
            x++,y++;add(x,y,z);add(y,x,z);
            if(x==1||y==1)d=min(d,z);
        }
        if(d==inf)
        {
            printf("Impossible\n");
            continue; 
        } 
        d=d*2;spfa();
    }
    return 0;
}

 

posted @ 2016-11-14 20:15  岂是蓬蒿人  阅读(227)  评论(0编辑  收藏  举报