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;
}
/*
对于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;
}
粘过来的题解:
/*
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;
}
/*
如果 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;
}