1. 程式人生 > >2014-9-9 NOIP模擬賽

2014-9-9 NOIP模擬賽

dff 多人 insert include 區域 lld == ner set

東方幻想鄉系列模擬賽
Stage 1
命題 Nettle
審題 Barty ccy1991911 FlanS39 Wagner

技術分享
T2
高精除高精,從來沒寫過,不知道怎麽寫,我就用大數減小數ans次,果斷超時。
T4
Tarjan的板子題,好久沒寫,中間出現了一些小錯誤
    ①是盡管有雙向邊,Tarjan函數中也不必排除雙向邊
    ②Tarjan算法有時候不能一步完成,需要做最多n次,用循環解決
    ③問題是關於這個題目的雖然輸入n代表有n個點,但是下面的連邊中有些點根本沒出現過,所以設一個數組記錄有效點。
View Code

Problem 1 東風谷早苗(robot.cpp/c/pas)

題目描述 在幻想鄉,東風谷早苗是以高達控聞名的高中生宅巫女。某一天,早苗終於入手了最新款的
鋼達姆模型。作為最新的鋼達姆,當然有了與以往不同的功能了,那就是它能夠自動行走,
厲害吧 (好吧, 我自重) 。 早苗的新模型可以按照輸入的命令進行移動, 命令包含’E’、 ’S’、 ’W’、 ’N’
四種,分別對應四個不同的方向,依次為東、南、西、北。執行某個命令時,它會向著對應
方向移動一個單位。作為新型機器人,自然不會只單單執行一個命令,它可以執行命令串。
對於輸入的命令串,每一秒它會按照命令行動一次。而執行完命令串最後一個命令後,會自

動從頭開始循環。在 0 時刻時早苗將鋼達姆放置在了(0,0)的位置,並且輸入了命令串。她想要
知道 T 秒後鋼達姆所在的位置坐標。
輸入格式 第 1 行:一個字符串,表示早苗輸入的命令串,保證至少有 1 個命令
第 2 行:一個正整數 T
輸出格式 第 1 行:兩個整數,表示 T 秒時,鋼達姆的坐標
輸入樣例 NSWWNSNEEWN
12
輸出樣例 -1 3
數據範圍 對於 60%的數據:T <= 500,000 且命令串長度 <= 5,000
對於 100%的數據:T <= 2,000,000,000 且命令串長度<= 5,000
註意 向東移動,坐標改變改變為(X+1,Y);
向南移動,坐標改變改變為(X,Y-1);
向西移動,坐標改變改變為(X-1,Y);
向北移動,坐標改變改變為(X,Y+1);

技術分享
/*
    由於命令串是重復執行的,所以我們可以先計算出一個周期後機器人的移動量。比如在第一個周期後,機器人從(0,0)移動到(△X,△Y),那麽每個周期之後機器人坐標改變為(X+△X,Y+△Y)。設 T/N = A…B,那麽機器人一共會運行的周期數為 A,所以在 A 個周期後,機器人的坐標為(A*△X,A*△Y)。之後機
*/
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int x,y,t,bei;
char ch[5010];
int main(){
    freopen("robot.in","r",stdin);
    freopen("robot.out","w",stdout);
    scanf("%s%d",ch+1,&t);
    int len=strlen(ch+1);
    for(int i=1;i<=len;i++){
        if(ch[i]==E)x++;
        if(ch[i]==S)y--;
        if(ch[i]==W)x--;
        if(ch[i]==N)y++;
    }
    bei=t/len;
    x*=bei;y*=bei;
    int len2=t%len;
    for(int i=1;i<=len2;i++){
        if(ch[i]==E)x++;
        if(ch[i]==S)y--;
        if(ch[i]==W)x--;
        if(ch[i]==N)y++;
    }
    printf("%d %d",x,y);
}
100分

Problem 2 西行寺幽幽子(spring.cpp/c/pas)


題目描述 在幻想鄉,西行寺幽幽子是以貪吃聞名的亡靈。不過幽幽子可不是只會吃,至少她還管理著
亡靈界。話說在幽幽子居住的白玉樓有一顆常年不開花的櫻樹——西行妖。幽幽子決定去收集
人間的春度,聚集起來讓西行妖開花。很快,作為幽幽子家園藝師的魂魄妖夢收集到了 M 個
單位的春度。並且在這段時間裏,幽幽子計算出要讓西行妖開出一朵花需要 N 個單位的春度。
現在幽幽子想要知道,使用所有的春度,能夠讓西行妖開出多少朵花。
輸入格式 第 1 行:一個正整數 M
第 2 行:一個正整數 N
N,M 的位數不超過 L,L 的範圍在題目後面給出
輸出格式 第 1 行:一個整數 ans,表示能開出花的朵數
輸入樣例 73861758
12471
輸出樣例 5922
數據範圍 對於 60%的數據:L <= 2,000 且 ans <= 2,000
對於 100%的數據:L <= 20,000 且 ans <= 2,000,000,000
2010-9-11 Touhou Contest Stage 1 By Nettle
第 3 頁 / 共 4 頁

技術分享
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=20010;
int ans;
struct node{
    int len;
    int zu[maxn];
}m,n;
char M[maxn],N[maxn];
bool compare(){
    if(m.len==1&&m.zu[1]==0)return 1;
    if(m.len==0)return 1;
    if(m.len<n.len)return 1;
    if(m.len>n.len)return 0;
    for(int i=1;i<=m.len;i++){
        if(m.zu[i]>n.zu[i])return 0;
        if(m.zu[i]<n.zu[i])return 1;
    }return 0;
}
node jian(){
    node res;
    //memset(res,0,sizeof(res));
    res.len=0;
    for(int i=1;i<=m.len;i++)res.zu[i]=0;
    for(int i=n.len,j=m.len;i>=1;i--,j--){
        if(m.zu[j]<n.zu[i]){
            m.zu[j]+=10;
            m.zu[j-1]--;
        }
        m.zu[j]-=n.zu[i];
    }
    for(int i=m.len;i>=2;i--){
        if(m.zu[i]<0){
            m.zu[i]+=10;
            m.zu[i-1]--;
        }
    }
    int start=1;
    for(int i=1;i<=m.len;i++)
        if(m.zu[i]!=0){
            start=i;
            break;
        }
    for(int i=start;i<=m.len;i++)
        res.zu[++res.len]=m.zu[i];
    return res;
}
int main(){
    freopen("spring.in","r",stdin);
    freopen("spring.out","w",stdout);
    scanf("%s%s",M+1,N+1);
    m.len=strlen(M+1);
    n.len=strlen(N+1);
    for(int i=1;i<=m.len;i++)m.zu[i]=M[i]-0;
    for(int i=1;i<=n.len;i++)n.zu[i]=N[i]-0;
    while(1){
        if(compare())break;//如果m比n小結束 
        m=jian();
        ans++;
    }
    printf("%d",ans);
}
60分 從 0 開始枚舉累加,直到當前值超過了 N。 技術分享
/*二分答案*/
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
long long a[100010],a1[100010];
struct node{
    int len,zu[100010];
    node operator * (const long long &now) const {
        node res;res.len=0;
        memset(a,0,sizeof(a));
        memset(a1,0,sizeof(a1));
        for(int i=1,j=len;i<=len;i++,j--)a[i]=zu[j];
        for(int i=1;i<=len;i++){
            a1[i]+=a[i]*now;
            a1[i+1]+=a1[i]/10;
            a1[i]%=10;
        }
        int len1=len;
        while(a1[len1+1]){
            len1++;
            a1[len1+1]+=a1[len1]/10;
            a1[len1]%=10;
        }
        res.len=len1;
        for(int i=len1,j=1;i>=1;i--,j++)res.zu[j]=a1[i];
        return res;
    }
}m,n,Now;
bool compare(){
    if(Now.len<m.len)return 1;
    if(Now.len>m.len)return 0;
    for(int i=1;i<=m.len;i++){
        if(Now.zu[i]<m.zu[i])return 1;
        if(Now.zu[i]>m.zu[i])return 0;
    }
    return 1;
}
char M[20010],N[20010];
int main(){
    freopen("spring.in","r",stdin);
    freopen("spring.out","w",stdout);
    //freopen("Cola.txt","r",stdin);
    scanf("%s%s",M+1,N+1);
    int len1=strlen(M+1),len2=strlen(N+1);
    m.len=len1;n.len=len2;
    for(int i=1;i<=len1;i++)m.zu[i]=M[i]-0;
    for(int i=1;i<=len2;i++)n.zu[i]=N[i]-0;
    long long l=0,r=2000000000,ans;
    while(l<=r){
        long long mid=(l+r)/2;
        Now=n*mid;
        if(compare()){//如果now<=m 
            ans=mid;
            l=mid+1;
        }
        else r=mid-1;
    }
    printf("%lld",ans);
}
100分 二分答案

Problem 3 琪露諾(iceroad.cpp/c/pas)


題目描述 在幻想鄉,琪露諾是以笨蛋聞名的冰之妖精。某一天,琪露諾又在玩速凍青蛙,就是用冰把
青蛙瞬間凍起來。但是這只青蛙比以往的要聰明許多,在琪露諾來之前就已經跑到了河的對
岸。於是琪露諾決定到河岸去追青蛙。小河可以看作一列格子依次編號為 0 到 N,琪露諾只能
從編號小的格子移動到編號大的格子。而且琪露諾按照一種特殊的方式進行移動,當她在格
子 i 時,她只會移動到 i+L 到 i+R 中的一格。你問為什麽她這麽移動,這還不簡單,因為她是
笨蛋啊。每一個格子都有一個冰凍指數 A[i],編號為 0 的格子冰凍指數為 0。當琪露諾停留在
那一格時就可以得到那一格的冰凍指數 A[i]。琪露諾希望能夠在到達對岸時,獲取最大的冰凍
指數,這樣她才能狠狠地教訓那只青蛙。但是由於她實在是太笨了,所以她決定拜托你幫它
決定怎樣前進。開始時,琪露諾在編號 0 的格子上,只要她下一步的位置編號大於 N 就算到
達對岸。
輸入格式 第 1 行:3 個正整數 N, L, R
第 2 行:N+1 個整數,第 i 個數表示編號為 i-1 的格子的冰凍指數 A[i-1]
輸出格式 第 1 行:一個整數,表示最大冰凍指數。保證不超過 2^31-1
第 2 行:空格分開的若幹個整數,表示琪露諾前進的路線,最後輸出-1 表示到達對岸
輸入樣例 5 2 3
0 12 3 11 7 -2
輸出樣例 11
0 3 -1
數據範圍 對於 60%的數據:N <= 10,000
對於 100%的數據:N <= 200,000
對於所有數據 -1,000 <= A[i] <= 1,000 且 1 <= L <= R <= N
註意 此題采用 Special Judge

技術分享
#include<iostream>
#include<cstdio>
using namespace std;
int n,a[400010],dp[400010],l,r,g[400010];
void show(int now){
    
    if(g[now])show(g[now]);
    if(now<=n)
    printf("%d ",now);
}
int main(){
    freopen("iceroad.in","r",stdin);
    freopen("iceroad.out","w",stdout);
    scanf("%d%d%d",&n,&l,&r);
    for(int i=0;i<=n;i++)scanf("%d",&a[i]);
    for(int i=1;i<=n+(r-l+1);i++){
        for(int j=l;j<=r;j++){
            if(i-j>=0){
                if(dp[i]<dp[i-j]+a[i]){
                    dp[i]=dp[i-j]+a[i];
                    g[i]=i-j;
                }
            }
        }
    }
    int ans=0,mark=0;
    for(int i=n;i<=n+(r-l+1);i++){
        if(dp[i]>ans){
            ans=dp[i];
            mark=i;
        }
    }
    cout<<ans<<endl;
    show(mark);
    if(mark>n)cout<<-1;
}
60分 未優化dp 技術分享
/*單調隊列優化dp*/
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int inf = 0x3f3f3f3f;
int n,l,r,a[200010],q[200010],head,tail,dp[200010],g[200010];
int st[200010],top;
int main(){
    //freopen("Cola.txt","r",stdin);
    freopen("iceroad.in","r",stdin);
    freopen("iceroad.out","w",stdout);
    scanf("%d%d%d",&n,&l,&r);
    for(int i=0;i<=n;i++)scanf("%d",&a[i]);
    head=1;tail=0;
    for(int i=1;i<=n;i++){
        dp[i]=-inf;
        if(q[head]==i-r-1&&head<=tail)head++;
        if(i-l>=0){
            while(dp[i-l]>dp[q[tail]]&&tail>=head)tail--;
            q[++tail]=i-l;
        }
        if(head<=tail){
            dp[i]=dp[q[head]];
            g[i]=q[head];
        }
        dp[i]+=a[i];
    }
    int mx=-inf,mark;
    for(int i=n-r+1;i<=n;i++)
        if(dp[i]>mx){
            mx=dp[i];
            mark=i;
        }
    printf("%d\n0 ",dp[mark]);
    while(1){
        if(mark==0)break;
        st[++top]=mark;
        mark=g[mark];
    }
    for(int i=top;i>=1;i--)printf("%d ",st[i]);
    printf("-1");
}
100分 單調隊列優化dp

Problem 4 上 白澤慧音(classroom.cpp/c/pas)


題目描述 在幻想鄉,上白澤慧音是以知識淵博聞名的老師。春雪異變導致人間之裏的很多道路都被大
雪堵塞,使有的學生不能順利地到達慧音所在的村莊。因此慧音決定換一個能夠聚集最多人
數的村莊作為新的教學地點。人間之裏由 N 個村莊(編號為 1..N)和 M 條道路組成,道路分
為兩種一種為單向通行的,一種為雙向通行的,分別用 1 和 2 來標記。如果存在由村莊 A 到
達村莊 B 的通路,那麽我們認為可以從村莊 A 到達村莊 B,記為(A,B)。當(A,B)和(B,A)同時滿足
時,我們認為 A,B 是絕對連通的,記為<A,B>。絕對連通區域是指一個村莊的集合,在這個集
合中任意兩個村莊 X,Y 都滿足<X,Y>。現在你的任務是,找出最大的絕對連通區域,並將這個絕
對連通區域的村莊按編號依次輸出。 若存在兩個最大的, 輸出字典序最小的, 比如當存在 1,3,4
和 2,5,6 這兩個最大連通區域時,輸出的是 1,3,4。
輸入格式 第 1 行:兩個正整數 N,M
第 2..M+1 行:每行三個正整數 a,b,t, t = 1 表示存在從村莊 a 到 b 的單向道路,t = 2 表示村莊
a,b 之間存在雙向通行的道路。保證每條道路只出現一次。
輸出格式 第 1 行: 1 個整數,表示最大的絕對連通區域包含的村莊個數。
第 2 行:若幹個整數,依次輸出最大的絕對連通區域所包含的村莊編號。
2010-9-11 Touhou Contest Stage 1 By Nettle
第 4 頁 / 共 4 頁
輸入樣例 5 5
1 2 1
1 3 2
2 4 2
5 1 2
3 5 1
輸出樣例 3
1 3 5
數據範圍 對於 60%的數據:N <= 200 且 M <= 10,000
對於 100%的數據:N <= 5,000 且 M <= 50,000

技術分享
#include<iostream>
#include<cstdio>
using namespace std;
int n,m,head[100010],num,belong[5010],ans2[5010],ans1[5010],ans[5010];
int sz[5010],dfn[5010],low[5010],st[5010],cnt,top,group;
int ans_num=0,mx=0;
bool vis[5010],ask[5010],appear[5010];
struct node{
    int pre,to;
}e[100010];
void Insert(int from,int to){
    e[++num].to=to;
    e[num].pre=head[from];
    head[from]=num;
}
void Tarjan(int v){
    
    low[v]=dfn[v]=++cnt;st[++top]=v;
    vis[v]=1;
    for(int i=head[v];i;i=e[i].pre){
        int to=e[i].to;
        if(ask[to])continue;
        if(!dfn[to]){
            Tarjan(to);
            low[v]=min(low[to],low[v]);
        }
        else if(low[v]>dfn[to]){
            if(vis[v])low[v]=dfn[to];
        }
    }
    if(dfn[v]==low[v]){
        group++;
        while(st[top]!=v){
            belong[st[top]]=group;ask[st[top]]=1;
            vis[st[top]]=0;
            sz[group]++;
            top--;
        }
        sz[group]++;
        vis[v]=0;
        belong[v]=group;
        ask[v]=1;
        top--;
    }
}
bool compare(){
    for(int i=1;i<=mx;i++)
        if(ans1[i]>ans2[i])return 1;
    return 0;
}
void Swap(){
    for(int i=1;i<=mx;i++)ans1[i]=ans2[i];
}
int main(){
    freopen("classroom.in","r",stdin);
    freopen("classroom.out","w",stdout);
    //freopen("Cola.txt","r",stdin);
    scanf("%d%d",&n,&m);
    int x,y,z;
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&x,&y,&z);
        if(x==y)continue;
        appear[x]=appear[y]=1;
        if(z==1)Insert(x,y);
        if(z==2){
            Insert(x,y);
            Insert(y,x);
        }
    }
    
    for(int i=1;i<=n;i++){
        if(!ask[i]&&appear[i]){
            Tarjan(i);
        }
    }
    /*cout<<group<<endl;
    for(int i=1;i<=n;i++)
    cout<<belong[i]<<‘ ‘;*/
    for(int i=1;i<=group;i++){
        if(sz[i]>mx){
            ans_num=1;
            ans[ans_num]=i;
            mx=sz[i];
        }
        else if(sz[i]==mx){
            ans_num++;
            ans[ans_num]=i;
        }
    }
    printf("%d\n",mx);
    int sum=0;
    for(int i=1;i<=ans_num;i++){//枚舉每一種可能的答案 
        sum=0;
        for(int j=1;j<=n;j++){
            if(belong[j]==ans[i])
                ans2[++sum]=j;
        }
        if(i==1||compare())Swap();//如果ans1的字典序比ans2大,那麽交換過來 
    }
    for(int i=1;i<=mx;i++)printf("%d ",ans1[i]); 
}
100分 Tarjan求強連通分量

2014-9-9 NOIP模擬賽