題目名稱 |
正確答案 |
序列問題 |
長途旅行 |
英文名稱 |
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.
暴力50:

/* 自己還是太弱~沒看出來要用hash 只是覺得自己的作法慢~ QAQ 50分暴力 先排序 一樣的縮成一種 然后枚舉正確答案是哪個 q==0 p==0的情況沒考慮到~ */ #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define maxn 30010 #define maxm 510 using namespace std; int n,m,p,q,cnt; string g[maxn]; struct node{ int len; string s; }k[maxn]; int cmp(int a[maxm],int b[maxm]){ for(int i=1;i<=m;i++){ if(a[i]<b[i])return 1; if(a[i]>b[i])return 0; } return 1; } 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++)cin>>g[i]; sort(g+1,g+1+n); int l=1,r; for(r=2;r<=n;r++){ if(g[r]==g[l])continue; k[++cnt].s=g[l]; k[cnt].len=r-l;l=r; } k[++cnt].s=g[l]; k[cnt].len=r-l; int falg=0; for(int i=1;i<=cnt;i++){ if(k[i].len!=p)continue; int sum=0; string x;x.clear(); for(int j=0;j<m;j++) if(k[i].s[j]=='Y')x+='N'; else x+='Y'; for(int j=1;j<=cnt;j++) if(k[j].s==x){ sum+=k[j].len; break; } if(sum==q){ cout<<k[i].s; falg=1;break; } } if(falg==0) for(int i=1;i<=cnt;i++){ if(k[i].len!=q)continue; int sum=0; string x;x.clear(); for(int j=0;j<m;j++) if(k[i].s[j]=='Y')x+='N'; else x+='Y'; for(int j=1;j<=cnt;j++) if(k[j].s==x){ sum+=k[j].len; break; } if(sum==p){ cout<<x; falg=1;break; } } if(!falg)printf("-1\n"); return 0; }
正解hash:

/* 正解hash 思路和之前的有相似之處 先排序 只不過沒有類似離散化的處理 把每個人的答案放入hash表 這里用鏈表處理了碰撞的情況 然后同樣的枚舉正確答案 這不過用了hash表加速 對於pq==0的情況 枚舉答案 按字典序小的來 那難道不會T到飛嗎 不成了2^500的了嗎 答案是不會的 這里的枚舉是針對pq==0的情況來的 結束的條件是 找到與每個人都不一樣的(存在一個即可)的就停下 所以枚舉最多30000次 */ #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define maxn 100010 #define mod 10007 #define MOD 23333 #define bas 19 #define BAS 119 using namespace std; int n,m,p,q,hash[maxn],HASH[maxn],ans,falg; int num,head[maxn],cnt[maxn],t,T; struct edge{ int v,pre; }e[maxn*2]; struct node{ char s[510]; bool operator < (const node &x) const { return strcmp(s,x.s)<0; } }a[maxn]; void Insert(int from,int to){ for(int i=head[from];i;i=e[i].pre) if(e[i].v==to){ cnt[i]++;return; } num++;e[num].v=to; e[num].pre=head[from]; head[from]=num; cnt[num]++; } int Query(int from,int to){ for(int i=head[from];i;i=e[i].pre) if(e[i].v==to)return cnt[i]; return 0; } void Yan(){ for(int i=1;i<=n;i++){ t=0,T=0; for(int j=0;j<m;j++){ t=t*bas+(a[i].s[j]=='Y');t%=mod; T=T*BAS+(a[i].s[j]=='Y');T%=MOD; } hash[i]=t;HASH[i]=T; Insert(t,T); } for(int i=1;i<=n;i++){ if(Query(hash[i],HASH[i])==p){ int t=0,T=0; for(int j=0;j<m;j++){ t=t*bas+(a[i].s[j]=='N');t%=mod; T=T*BAS+(a[i].s[j]=='N');T%=MOD; } if(Query(t,T)==q){ ans=i; falg=1;break; } if(falg)break; } } if(falg)printf("%s\n",a[ans].s); else printf("-1\n"); } void Li(){ for(int i=1;i<=n;i++){ int t=0,T=0; for(int j=0;j<m;j++){ t=t*bas+(a[i].s[j]=='Y');t%=mod; T=T*BAS+(a[i].s[j]=='Y');T%=MOD; } hash[i]=t;HASH[i]=T; Insert(t,T); } for(int i=n;i>=1;i--){ if(Query(hash[i],HASH[i])==q){ t=0,T=0; for(int j=0;j<m;j++){ t=t*bas+(a[i].s[j]=='N');t%=mod; T=T*BAS+(a[i].s[j]=='N');T%=MOD; } if(Query(t,T)==p){ ans=i; falg=1;break; } if(falg)break; } } if(falg){ for(int i=0;i<m;i++) if(a[ans].s[i]=='N')printf("Y"); else printf("N"); } else printf("-1\n"); } void Feng(){ for(int i=1;i<=n;i++){ t=0,T=0; for(int j=0;j<m;j++){ t=t*bas+(a[i].s[j]=='Y');t%=mod; T=T*BAS+(a[i].s[j]=='Y');T%=MOD; } Insert(t,T); t=0;T=0; for(int j=0;j<m;j++){ t=t*bas+(a[i].s[j]=='N');t%=mod; T=T*BAS+(a[i].s[j]=='N');T%=MOD; } Insert(t,T); } char r[510]; for(int i=0;i<m;i++)r[i]='N'; while(1){ t=0,T=0; for(int i=0;i<m;i++){ t=t*bas+(r[i]=='Y');t%=mod; T=T*BAS+(r[i]=='Y');T%=MOD; } if(Query(t,T)==0){ falg=1;break; } for(int i=m-1;i>=0;i--) if(r[i]=='Y')r[i]='N'; else{ r[i]='Y';break; } } if(falg)printf("%s\n",r); else printf("-1\n"); } 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",a[i].s); sort(a+1,a+1+n); if(p)Yan(); else if(q)Li(); else Feng(); 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分暴力枚舉集合不說了 其實這題需要高精的.... 維護i到n &值為j的方案數 維護1到i ^值為j的方案數 然后枚舉斷點 乘起來 */ #include<iostream> #include<cstdio> #include<cstring> #define maxn 2050 #define ll long long using namespace std; ll n,a[maxn],b[maxn],sum[maxn],f[maxn][maxn+200],g[maxn][maxn+200],ans; int main() { freopen("sequence.in","r",stdin); freopen("sequence.out","w",stdout); cin>>n; for(ll i=1;i<=n;i++) cin>>a[i]; for(ll i=1;i<=n;i++) b[i]=a[n-i+1]; for(ll i=1;i<=n;i++){ for(ll j=0;j<=2048;j++) f[i][j]=sum[j^a[i]]; f[i][a[i]]++; for(ll j=0;j<=2048;j++) sum[j]+=f[i][j]; } memset(sum,0,sizeof(sum)); for(ll i=0;i<n;i++){ for(ll j=0;j<=2048;j++) g[i+1][j&b[i+1]]+=sum[j]; g[i+1][b[i+1]]++; for(ll j=0;j<=2048;j++) sum[j]+=g[i+1][j]; } memset(sum,0,sizeof(sum)); for(ll i=1;i<n;i++){ for(ll j=0;j<2048;j++) sum[j]+=f[i][j]; for(ll j=0;j<2048;j++) ans+=sum[j]*g[n-i][j]; } cout<<ans; return 0; }
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.
暴力dp30:

/* 暴力dp 30 狀態f[i][j]表示到了i號節點走了j的狀態是否存在 可以水過T比較小的數據 */ #include<iostream> #include<cstdio> #include<cstring> #define maxn 1000010 using namespace std; int T,t,n,m,f[51][maxn],g[51][51]; void Clear(){ memset(f,0,sizeof(f)); memset(g,0,sizeof(g)); } int main() { freopen("travel.in","r",stdin); freopen("travel.out","w",stdout); scanf("%d",&T); while(T--){ scanf("%d%d%d",&n,&m,&t); int u,v,s;Clear(); for(int i=1;i<=m;i++){ scanf("%d%d%d",&u,&v,&s); u++;v++; g[u][v]=g[v][u]=s; } f[1][0]=1; for(int j=1;j<=t;j++) for(int i=1;i<=n;i++) for(int k=1;k<=n;k++){ if(!g[i][k]||j-g[i][k]<0)continue; f[i][j]=f[i][j]||f[k][j-g[i][k]]; } if(f[n][t])printf("Possible\n"); else printf("Impossible\n"); } return 0; }
正解SFPA:

/* 正解是最短路~~~~ 一開始以為圖論 后來認為是dp 沒想到最后又回到圖論了~~ 前面dp做法的瓶頸很顯然是T太大~ 但是出入的邊的權值和要小的多 所以會在某個環了轉圈 假設有一條路徑走一遍的時間為t 中間有一個長度為p的環 那這條路可以認為是t+p*k長度的 所以我們只需要維護這個多出來的t就好了 先選一個環 保險起見 選從零出發的最小的環 長度設為x 定義dis[i][j] 表示到了i 時間為j+k*x 且k最小 這里跑最短路找最小 為什么找最小呢 因為只有當dis[n][T%x]<=T 時才可以 所以為了盡量滿足條件 維護最小的dis */ #include<iostream> #include<cstdio> #include<cstring> #include<queue> #define maxn 210 #define ll long long using namespace std; ll T,t,n,m,num,head[maxn],dis[maxn][maxn*100],mxx,f[maxn][maxn]; struct node{ ll v,t,pre; }e[maxn*2]; struct point{ int v,s; }; queue<point>q; void Clear(){ memset(head,0,sizeof(head)); memset(f,0,sizeof(f)); num=0; } ll min(ll a,ll b){ return a<b?a:b; } void Add(ll from,ll to,ll dis){ num++;e[num].v=to; e[num].t=dis; e[num].pre=head[from]; head[from]=num; } void SPFA(){ memset(dis,127/3,sizeof(dis)); q.push((point){1,0}); f[1][0]=1;dis[1][0]=0; while(!q.empty()){ ll x=q.front().v; ll s=q.front().s; q.pop();f[x][s]=0; for(int i=head[x];i;i=e[i].pre){ ll v=e[i].v; ll di=s+e[i].t; di%=mxx;//這里分清誰做下標誰是dis值 if(dis[v][di]>s+e[i].t){ dis[v][di]=s+e[i].t; if(f[v][di]==0){ f[v][di]=1; q.push((point){v,di}); } } } } } int main() { freopen("travel.in","r",stdin); freopen("travel.out","w",stdout); cin>>T; while(T--){ cin>>n>>m>>t; ll u,v,s;Clear(); mxx=0x7fffffff; for(int i=1;i<=m;i++){ cin>>u>>v>>s; u++;v++; Add(u,v,s);Add(v,u,s); if(u==1||v==1)mxx=min(mxx,s); } if(mxx==0x7fffffff){//不連通 printf("Impossible\n"); continue; } mxx*=2; SPFA(); if(dis[n][t%mxx]<=t)printf("Possible\n"); else printf("Impossible\n"); } return 0; }