题目名称 |
正确答案 |
序列问题 |
长途旅行 |
英文名称 |
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.
懒得写hash map水过
#include<cstdio> #include<iostream> #include<map> #include<cstring> #include<algorithm> #define maxn 30010 using namespace std; int n,m,p,q,falg; char ss[maxn]; string s[maxn],ans; map<string,int>f; void Solve1(){ for(int i=1;i<=n;i++){ int x=f[s[i]]; if(x!=p)continue; string now;now.clear(); for(int k=0;k<m;k++) now+=(s[i][k]=='Y'?'N':'Y'); int y=f[now]; if(y==q){ falg=1;ans=s[i];break; } } } void Solve2(){ for(int i=1;i<=n;i++){ int x=f[s[i]]; if(x!=p)continue; string now;now.clear(); for(int k=0;k<m;k++) now+=(s[i][k]=='Y'?'N':'Y'); int y=f[now]; if(y==q){ falg=1;ans=s[i];break; } } } void Solve3(){ for(int i=n;i>=1;i--){ int x=f[s[i]]; if(x!=q)continue; string now;now.clear(); for(int k=0;k<m;k++) now+=(s[i][k]=='Y'?'N':'Y'); int y=f[now]; if(y==p){ falg=1;ans=now;break; } } } void Solve4(){ int A[510]={0}; while(1){ string now,noww; now.clear();noww.clear(); for(int i=1;i<=m;i++) if(A[i])now+='Y'; else now+='N'; for(int i=1;i<=m;i++) if(A[i])noww+='N'; else noww+='Y'; int x=f[now],y=f[noww]; if(!x&&!y){ falg=1;ans=now;break; } int can=0; for(int i=m;i>=1;i--) if(A[i]==0){ A[i]=1;can=1; for(int j=i+1;j<=m;j++) A[j]=0;can=1;break; } if(!can)break; } } 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",ss); for(int j=0;j<m;j++) s[i]+=ss[j]; f[s[i]]++; } sort(s+1,s+1+n); if(p&&q)Solve1(); else if(p&&!q)Solve2(); else if(!p&&q)Solve3(); else if(!p&&!q)Solve4(); if(falg)cout<<ans<<endl; else cout<<"-1"<<endl; 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暴力
/*暴力30*/ #include<cstdio> #define maxn 1010 using namespace std; int n,a[maxn],A[maxn],B[maxn],ans; void Dfs(int now,int x,int y){ if(now==n+1){ if(A[A[0]]>=B[1])return; if(x==y&&A[0]&&B[0]) ans++;return; } Dfs(now+1,x,y); A[++A[0]]=now;Dfs(now+1,x^a[now],y);A[A[0]--]=0; B[++B[0]]=now;Dfs(now+1,x,y&a[now]);B[B[0]--]=0; } int main(){ freopen("sequence.in","r",stdin); freopen("sequence.out","w",stdout); scanf("%d",&n); if(n>50){ printf("0 ");return 0; } for(int i=1;i<=n;i++) scanf("%d",&a[i]); Dfs(1,0,1023); printf("%d ",ans); fclose(stdin);fclose(stdout); return 0; }
dp
/* 再一次没有写出dp来 感觉noip出个dp题要跪了22333 这是个问题.... 想到了 f g 分别表示啥 然后发现 有重复 会重复计算很多 然后发现可以定义成 一定选了这个的状态 然后又有一个问题 枚举分开点的话 又会漏了许多 这就尴尬了..... 解决方法是确定g选这个 然后f用前缀和 嗯 很机智 */ #include<cstdio> #include<cstring> #define maxn 1010 using namespace std; int n,a[maxn],b[maxn]; long long f[maxn][1024],g[maxn][1024],sum[1024],ans; int main() { freopen("sequence.in","r",stdin); freopen("sequence.out","w",stdout); scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&a[i]); for(int i=1;i<=n;i++) b[i]=a[n-i+1]; for(int i=0;i<n;i++){ for(int j=0;j<1024;j++) f[i+1][j^a[i+1]]+=sum[j]; f[i+1][a[i+1]]++; for(int j=0;j<1024;j++) sum[j]+=f[i+1][j]; } memset(sum,0,sizeof(sum)); for(int i=0;i<n;i++){ for(int j=0;j<1024;j++) g[i+1][j&b[i+1]]+=sum[j]; g[i+1][b[i+1]]++; for(int j=0;j<1024;j++) sum[j]+=g[i+1][j]; } memset(sum,0,sizeof(sum)); for(int i=1;i<=n;i++){ for(int j=0;j<1024;j++) sum[j]+=f[i][j]; for(int j=0;j<1024;j++) ans+=sum[j]*g[n-i][j]; } printf("%I64d ",ans); return 0; }
/*超时爆空间的压位高精 好吧dp写丑了 导致高精不好办了2333*/ #include<cstdio> #include<iostream> #include<cstring> #define maxn 1001 #define ll long long #define bas 1000000000 using namespace std; int n,a[maxn],b[maxn]; ll f[maxn][1024][40],g[maxn][1024][40],sum[1024][40],ans[40],A[40]; void Add(ll a[40],ll b[40]){ int len=max(a[0],b[0]),c[40]={0}; for(int i=1;i<=len;i++) c[i]=a[i]+b[i]; for(int i=1;i<=len;i++) if(c[i]>=bas){ c[i+1]++;c[i]%=bas; } if(c[len+1])len++;c[0]=len; for(int i=0;i<=c[0];i++)a[i]=c[i]; } void Mul(ll a[40],ll b[40]){ memset(A,0,sizeof(A)); for(int i=1;i<=a[0];i++){ for(int j=1;j<=b[0];j++){ A[i+j-1]+=a[i]*b[j]; if(A[i+j-1]>=bas){ A[i+j]+=A[i+j-1]/bas; A[i+j-1]%=bas; } } int len=i+b[0]-1; while(A[len+1]){ len++;A[len+1]+=A[len]/bas;A[len]%=bas; } } for(int i=a[0]+b[0];i>=1;i--) if(A[i]){ A[0]=i;break; } } int main() { freopen("sequence.in","r",stdin); freopen("sequence.out","w",stdout); cin>>n; for(int i=1;i<=n;i++) cin>>a[i]; for(int i=1;i<=n;i++) b[i]=a[n-i+1]; A[0]=1;A[1]=1; for(int i=0;i<n;i++){ for(int j=0;j<1024;j++) Add(f[i+1][j^a[i+1]],sum[j]); Add(f[i+1][a[i+1]],A); for(int j=0;j<1024;j++) Add(sum[j],f[i+1][j]); } memset(sum,0,sizeof(sum)); for(int i=0;i<n;i++){ for(int j=0;j<1024;j++) Add(g[i+1][j&b[i+1]],sum[j]); Add(g[i+1][b[i+1]],A); for(int j=0;j<1024;j++) Add(sum[j],g[i+1][j]); } memset(sum,0,sizeof(sum)); for(int i=1;i<=n;i++){ for(int j=0;j<1024;j++) Add(sum[j],f[i][j]); for(int j=0;j<1024;j++){ Mul(sum[j],g[n-i][j]); Add(ans,A); } } printf("%lld",ans[ans[0]]); for(int i=ans[0]-1;i>=1;i--) printf("%09lld",ans[i]); printf(" "); return 0; }
(dp写丑了 导致高精炸了2333)
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.
暴力dp+骗分40
#include<cstdio> #include<iostream> #include<cstring> #include<queue> #define maxn 51 #define maxm 20010 #define ll long long using namespace std; ll cas,n,m,T,G[maxn][maxn][110],f[maxm][maxn]; ll vis[maxn],dis[maxn],diss[maxn],c[maxn]; queue<ll>q; void Cl(){ memset(G,0,sizeof(G)); memset(f,0,sizeof(f)); } void SPFA(){ memset(dis,127/3,sizeof(dis)); memset(vis,127/3,sizeof(vis)); while(!q.empty())q.pop(); vis[1]=1;dis[1]=0;q.push(1); while(!q.empty()){ ll k=q.front();q.pop(); vis[k]=1;if(c[k]>n+5)break; for(ll i=1;i<=n;i++) for(ll j=1;j<=G[k][i][0];j++){ ll v=i,t=G[k][i][j]; if(dis[v]>dis[k]+t){ dis[v]=dis[k]+t; if(vis[v]==0){ q.push(v);vis[v]=1;c[v]++; } } } } } void SPFA2(){ memset(diss,127/3,sizeof(diss)); memset(vis,127/3,sizeof(vis)); memset(c,0,sizeof(c)); while(!q.empty())q.pop(); vis[n]=1;diss[n]=0;q.push(n); while(!q.empty()){ ll k=q.front();q.pop(); vis[k]=1;if(c[k]>n+5)break; for(ll i=1;i<=n;i++) for(ll j=1;j<=G[k][i][0];j++){ ll v=i,t=G[k][i][j]; if(diss[v]>dis[k]+t){ diss[v]=dis[k]+t; if(vis[v]==0){ q.push(v);vis[v]=1;c[v]++; } } } } } void Solve1(){ f[0][1]=1; for(ll i=0;i<=T;i++) for(ll j=1;j<=n;j++) for(ll k=1;k<=n;k++) for(ll l=1;l<=G[j][k][0];l++) f[i+G[k][j][l]][j]=f[i][k]||f[i+G[k][j][l]][j]; if(f[T][n])printf("Possible "); else printf("Impossible "); } void Solve2(){ SPFA();ll falg=0; for(ll i=1;i<=n;i++)if(!falg) for(ll j=1;j<=n;j++)if(!falg) for(ll k=1;k<=G[i][j][0];k++){ ll x=dis[i],y=diss[j]; if((T-x-y-G[i][j][k])%(G[i][j][k]*2)==0){ falg=1;break; } } if(falg)printf("Possible "); else printf("Impossible "); } int main() { freopen("travel.in","r",stdin); freopen("travel.out","w",stdout); cin>>cas; while(cas--){ cin>>n>>m>>T; Cl();ll u,v,t; for(ll i=1;i<=m;i++){ cin>>u>>v>>t;u++;v++; G[u][v][++G[u][v][0]]=t; G[v][u][++G[v][u][0]]=t; } if(T<=10000)Solve1();else Solve2(); } fclose(stdin);fclose(stdout); return 0; }
/* 30分的bfs很好想(然而奇葩的我写的dp...跑的好像很慢) 那个状态是 dis[i][j] 1到i 经过时间j 存在不存在 关键是 j 很大 状态会很大 时间会很慢 考虑重复走环的情况 假设 我们从0连出去一个环 大小为x 那么这个环保证了一定能走 如果到 y 时刻能到 n-1 那么y ±x时刻也能到 根据这个性质 我们压缩第三维 %x 意义下 重定义状态dis[i][j]表示 1到 i 走的路程 s s%x为j 且s最小 那么最后的状态 dis[n-1][j] j==T%x 就只0 - n-1 走的路程余数为j的最短路 显然 走T的时间走到n-1的余数和走dis的余数相同 那么就有 dis+kx==T 意思就是 在绕几遍x就好了 因为最后是+kx 所以尽量使dis小 跑最短路 */ #include<cstdio> #include<iostream> #include<queue> #include<cstring> #define maxn 110 #define maxm 50010 #define ll long long using namespace std; ll cas,n,m,T,num,head[maxn],mx,dis[maxn][maxm],f[maxn][maxm]; struct edge{ ll v,t,pre; }e[maxn*2]; struct node{ ll x,s; }; queue<node>q; void Cl(){ num=0;mx=0x7fffffff; memset(f,0,sizeof(f)); memset(head,0,sizeof(head)); memset(dis,127/3,sizeof(dis)); } void Add(ll from,ll to,ll dis){ num++;e[num].pre=head[from]; e[num].v=to;e[num].t=dis; head[from]=num; } void SPFA(){ dis[1][0]=0;f[1][0]=1; q.push((node){1,0}); while(!q.empty()){ ll k=q.front().x; ll s=q.front().s; q.pop();f[k][s]=0; for(ll i=head[k];i;i=e[i].pre){ ll v=e[i].v,y=s+e[i].t;y%=mx; if(dis[v][y]>dis[k][s]+e[i].t){ dis[v][y]=dis[k][s]+e[i].t; if(f[v][y]==0){ f[v][y]=1;q.push((node){v,y}); } } } } } int main() { freopen("travel.in","r",stdin); freopen("travel.out","w",stdout); cin>>cas; while(cas--){ cin>>n>>m>>T; ll u,v,t;Cl(); for(ll i=1;i<=m;i++){ cin>>u>>v>>t; u++;v++;Add(u,v,t);Add(v,u,t); if(u==1||v==1)mx=min(mx,t*2); } if(mx==0x7fffffff){ printf("Impossible ");continue; } SPFA(); if(dis[n][T%mx]<=T)printf("Possible "); else printf("Impossible "); } return 0; }