9.5noip模擬試題


 

題目名稱

正確答案

序列問題

長途旅行

英文名稱

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;
}
View Code

正解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;
}
View Code

 

 

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;
}
View Code

 

 

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;
}
View Code

正解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;
}
View Code

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM