11.24模擬賽

全國信息學奧林匹克聯賽(NOIP2014NOIP2014)複賽

模擬題 Day1Day1

長樂一中

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

【題目描述】

HH 與小 YY 剛剛參加完 UOIPUOIP 外卡組的初賽,就迫不及待的跑出考場對答案。

“吔,我的答案和你都不一樣!”,小 YY 說道,”我們去找神犇們問答案吧”。

外卡組試卷中共有 mm 道判斷題,小 HH 與小 YY 一共從其他 nn 個神犇那問了答案。之後又從小 GG 那裏得知,這 nn 個神犇中有 pp 個考了滿分,qq 個考了零分,其他神犇不爲滿分或零分。

這可讓小 YY 與小 HH 犯了難。你能幫助他們還原出標準答案嗎?如有多解則輸出字典序最小的那個。無解輸出1-1

【輸入格式】

第一行四個整數 nn, mm, pp, qq,意義如上描述。

接下來 nn 行,每一行 mm 個字符N’N’Y’Y’,表示這題這個神犇的答案。

【輸出格式】

僅一行,一個長度爲 mm 的字符串或是1-1

【樣例輸入】

2 2 2 0
YY
YY

【樣例輸出】

YY

【數據範圍】

3030% : n<=100.n <= 100.

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

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

2.序列問題

【題目描述】

HH 是個善於思考的學生,她正在思考一個有關序列的問題。

她的面前浮現出了一個長度爲 nn 的序列ai{ai},她想找出兩個非空的集合 STS、T

這兩個集合要滿足以下的條件:

  1. 兩個集合中的元素都爲整數,且都在 [1,n][1, n] 裏,即 SiTi[1,n]Si,Ti ∈ [1, n]

  2. 對於集合 SS 中任意一個元素 xx,集合 TT 中任意一個元素 yy,滿足 x<yx < y

  3. 對於大小分別爲 pp, qq 的集合 SSTT,滿足

a[s1]a[s1] xorxor a[s2]a[s2] xorxor a[s3]...a[s3] ... xorxor a[sp]=a[t1]a[sp] = a[t1] andand a[t2]a[t2] andand a[t3]...a[t3] ... andand a[tq].a[tq].

HH 想知道一共有多少對這樣的集合(S,T)(S,T),你能幫助她嗎?

【輸入格式】

第一行,一個整數 nn

第二行,nn 個整數,代表 aiai

【輸出格式】

僅一行,表示最後的答案。

【樣例輸入】

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

【數據範圍】

3030%: 1<=n<=101 <= n <= 10

6060%: 1<=n<=1001 <= n <= 100

100100%: 1<=n<=1000,0<=ai<10241 <= n <= 1000, 0 <= ai < 1024

3.長途旅行

【題目描述】

JYJY 是一個愛旅遊的探險家,也是一名強迫症患者。現在 JYJY 想要在 CC 國進行一次長途旅行,CC 國擁有 nn 個城市(編號爲 0,1,2...,n10,1,2...,n - 1),城市之間有 mm 條道路,可能某個城市到自己有一條道路,也有可能兩個城市之間有多條道路,通過每條道路都要花費一些時間。JYJY00 號城市開始出發,目的地爲 n1n – 1 號城市。由於 JYJY 想要好好參觀一下 CC 國,所以 JYJY 想要旅行恰好 TT 小時。爲了讓自己的旅行更有意思,JYJY 決定不在任何一個時刻停留(走一條到城市自己的路並不算停留)。JYJY 想知道是否能夠花恰好 TT 小時到達 n1n – 1 號城市(每個城市可經過多次)。現在這個問題交給了你。

若可以恰好到達輸出Possible“Possible”否則輸出Impossible“Impossible”。(不含引號)。

【輸入格式】

第一行一個正整數 CaseCase,表示數據組數。

每組數據第一行 33 個整數,分別爲 n,m,Tn, m, T

接下來 mm 行,每行 33 個整數 x,y,z,x, y, z,代表城市 xx 和城市 yy 之間有一條耗時爲 zz 的雙向邊。

【輸出格式】

對於每組數據輸出Possible”Possible”或者Impossible”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:110 -> 1 -> 2 :11

第二組:顯然偶數時間都是不可能的。

【數據範圍】

3030%: T<=10000T <= 10000

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

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


題解

轉自博客園-一入OI深似海,轉載地址:https://www.cnblogs.com/yanlifneg/p/5843482.html


T1

/*
自己還是太弱~沒看出來要用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;
}

T2

/*
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;
}

T3

暴力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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章