1. 程式人生 > >NOIP2016提高組題解

NOIP2016提高組題解

DAY1

T1

很簡單的一個模擬演算法。
從0標號就可以用模,從1標號也可以通過特判(>n就減小於1就加)

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<iostream>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
typedef long long
ll; typedef double db; const int maxn=100000+10; char s[maxn][20]; int a[maxn],b[maxn]; int i,j,k,l,t,n,m,ans; char ch; int read(){//if read a big number,need ll int x=0,f=1; char ch=getchar(); while (ch<'0'||ch>'9'){ if (ch=='-') f=-1; ch=getchar(); } while (ch>='0'
&&ch<='9'){ x=x*10+ch-'0'; ch=getchar(); } return x*f; } char get(){ char ch=getchar(); while (ch<'a'||ch>'z') ch=getchar();//zi fu chuan jin bao han a-z return ch; } int main(){ freopen("toy.in","r",stdin);freopen("toy.out","w",stdout); n=read();m=read(); fo(i,1
,n){ a[i]=read(); b[i]=1; s[i][1]=get(); do{ ch=getchar(); if (ch<'a'||ch>'z') break; s[i][++b[i]]=ch; }while (1); } ans=1; fo(i,1,m){ j=read();k=read(); if (j==a[ans]) t=1;else t=0;//t biao shi shun hai shi ni if (t){ ans-=k; if (ans<=0) ans+=n; } else{ ans+=k; if (ans>n) ans-=n; } } fo(i,1,b[ans]) printf("%c",s[ans][i]); printf("\n"); fclose(stdin);fclose(stdout); return 0; }

T2

我們用d表示深度,a表示一條路線的長度。
對於路線S->T,若lca為V,拆成S->V以及V->T兩條樹鏈。
對於S->V上每一個點j,其答案可以加一的條件:
dSdj=wj
dS=dj+wj
右邊只與j有關。
同樣,對於V->T上每一個點j,其答案可以加一的條件:
dTdj=aiwj
dTai=djwj
右邊只與j有關。
因此思考離線做法,對於一條路線,在S和T分別打兩個tag,並在lca即V處回收tag。
用兩顆線段樹維護子樹內目前還沒被撤銷掉的dS和dT-ai的權值線段樹,權值可能為負數需要設定一個絕對值的最大值來加上,或者提前進行離散化。
這個線段樹是單點修改和查詢的,用線段樹僅僅是因為線段樹可以進行合併。
這個演算法帶log,如何線性知道一個子樹內某權值的數量?
維護一個桶,進入一個節點時得到該節點子樹所需權值的數量t1,退出該節點時在桶中得到該節點子樹所需權值的數量t2,那麼顯然該子樹內有t2-t1個所需權值。

T3

如果我們用2^n列舉每節課申請或不申請,那麼我們需要知道如何計算期望。
給你一個DAG,入度為0的點只有一個,每條邊有兩個權值a和b,從入度為0的點走到另一點所有的路徑中a的和乘b的積的和,這其實就是期望的模型。
已知一個點的E和P(E=(VP),這裡的V和P表示一條路徑上的權值和與概率積),現在該點有一條出邊(a,b),對該邊的終點的E和P的影響是?
對P的影響顯然是Pb,加上這個即可。
E?
(V+a)Pb
Eb+Pab
用這個方法遞推即可。
那麼影響答案的就是我們的決策,也就是是否申請。
設f[i,j,0~1]表示上完第i節課,申請了j次,第i節課是否申請的最小期望。
以申請了為例,假如決策時下一節課也申請。根據上面的式子,你會發現當前的概率與前面的決策均無關,因為如果申請了,那麼就有p[i]的概率在d[i],有1-p[i]的概率在c[i]。因此可以進行轉移。
上面的講法可能稍微複雜,其實知道期望的線性性也可以推一下。

DAY2

T1

預處理一個階乘包含了多少個某質數。
然後求一個組合數是否是k的倍數可以分解質因數。
最後再根據組合數是否為k的倍數產生的01貢獻矩陣做二維字首和,可以o(1)回答每個詢問。
這個方法不知道能不能跑過老爺機。
實際上,可以用組合數的公式
Cji=Cji1+Cj1i1
n^2的在模k意義下求組合數是否為k的倍數。

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<cstring>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
typedef long long ll;
typedef double db;
const int maxn=2000+10;
int a[maxn][maxn],c[maxn][maxn],sum[maxn][maxn];
int i,j,k,l,t,n,m,ca,ans;
int read(){//if read a big number,gai ll
    int x=0,f=1;
    char ch=getchar();
    while (ch<'0'||ch>'9'){
        if (ch=='-') f=-1;
        ch=getchar();
    }
    while (ch>='0'&&ch<='9'){
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*f;
}
int main(){
    freopen("problem.in","r",stdin);freopen("problem.out","w",stdout);
    ca=read();k=read();
    c[0][0]=1;
    fo(i,1,2000){
        c[i][0]=1;
        fo(j,1,i)
            c[i][j]=(c[i-1][j-1]+c[i-1][j])%k;
    }
    fo(i,0,2000)
        fo(j,0,2000){
            if (i<j) a[i][j]=0;
            else a[i][j]=(c[i][j]==0);
        }
    fo(i,0,2000)
        fo(j,0,2000){
            sum[i][j]=a[i][j];
            if (i>0) t=sum[i-1][j];else t=0;
            sum[i][j]+=t;
            if (j>0) t=sum[i][j-1];else t=0;
            sum[i][j]+=t;
            if (i>0&&j>0) t=sum[i-1][j-1];else t=0;
            sum[i][j]-=t;
        }
    while (ca--){
        n=read();m=read();
        ans=sum[n][m];
        printf("%d\n",ans);
    }
    fclose(stdin);fclose(stdout);
    return 0;
}

T2

結論一:長的一定先切。
證明:顯然。
結論二:對於相鄰兩次切的長度i和j(i>=j),切出來的ip和jp,將來一定是先切ip再切jp。
證明:
假如在t時刻切i,t+1時刻切j,那麼從t+2時刻開始兩者增長速度一致,因此可以只探究兩者在t+1時刻的長度。
前者:ip+q=ip+q
後者:(j+q)p=jp+qp
顯然前者大於後者。
再根據結論一,前者會先切。

根據結論二可以類推出i-ip比j-jp先切。
因此開三個佇列維護。
第一個佇列維護排序後的原佇列。
第二個佇列維護切出來的乘p。
第三個佇列維護切出來的乘1-p。
每次從三個佇列隊首取一個最大的就是要切的(結論一),然後切出的兩份分別加入後兩個佇列的隊尾(結論二)
這是線性的。
整體+q的影響可以考慮變成單個-q。

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<cstring>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
typedef long long ll;
typedef double db;
const ll inf=10000000000000000;
const int maxn=8000000+10;
int sta[100];
ll a[maxn],b[maxn],c[maxn],k,l,t,ans,x,y;
int i,j,n,m,u,v,tot,top,q,tt,root,h1,e1,h2,e2,h3,e3;
db p;
int read(){//if read a big number,gai ll
    int x=0,f=1;
    char ch=getchar();
    while (ch<'0'||ch>'9'){
        if (ch=='-') f=-1;
        ch=getchar();
    }
    while (ch>='0'&&ch<='9'){
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*f;
}
void write(ll x){
    if (!x){
        putchar('0');
        putchar(' ');
        return;
    }
    top=0;
    while (x){
        sta[++top]=x%10;
        x/=10;
    }
    while (top){
        putchar('0'+sta[top]);
        top--;
    }
    putchar(' ');
}
bool cmp(int a,int b){
    return a>b;
}
int main(){
    //freopen("earthworm.in","r",stdin);freopen("earthworm.out","w",stdout);
    n=read();m=read();q=read();u=read();v=read();tt=read();
    p=(db)u/v;
    fo(i,1,n) a[i]=read();
    sort(a+1,a+n+1,cmp);
    h1=h2=h3=1;e1=n;
    fo(i,1,m){
        if (h1<=e1) k=a[h1];else k=-inf;
        if (h2<=e2) l=b[h2];else l=-inf;
        if (h3<=e3) t=c[h3];else t=-inf;
        if (k>=l&&k>=t) ans=k,h1++;
        else if (l>=k&&l>=t) ans=l,h2++;
        else ans=t,h3++;
        x=floor((ans+(ll)(i-1)*q)*p);y=ans+(ll)(i-1)*q-x;
        b[++e2]=x-(ll)i*q;
        c[++e3]=y-(ll)i*q;
        if (i%tt==0) write(ans+(ll)(i-1)*q);
    }
    printf("\n");
    fo(i,1,n+m){
        if (h1<=e1) k=a[h1];else k=-inf;
        if (h2<=e2) l=b[h2];else l=-inf;
        if (h3<=e3) t=c[h3];else t=-inf;
        if (k>=l&&k>=t) ans=k,h1++;
        else if (l>=k&&l>=t) ans=l,h2++;
        else ans=t,h3++;
        if (i%tt==0) write(ans+(ll)m*q);
    }
    printf("\n");
    fclose(stdin);fclose(stdout);
    return 0;
}

T3

三點確定一條拋物線(注意三點共線以及兩點橫座標相同的情況)
狀壓DP,每次列舉兩隻豬確定發射一隻鳥的拋物線(也可以只射一隻豬,注意合法拋物線必須a<0)
這樣是2^n*n*n。
注意標號最小的豬一定要死,每次可以解決最小的豬,只列舉另一隻豬來確定拋物線。
2^n*n。
搜尋+最優性剪枝+記憶化也可以。

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<cstring>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
typedef long long ll;
typedef double db;
const int maxn=18+10;
const db eps=0.000000001;
db dx[maxn],dy[maxn];
int bz[maxn];
int f[(1<<19)+50];
bool vis[(1<<19)+50];
int i,j,k,l,t,n,m,ans,ca;
int read(){//if read a big number,gai ll
    int x=0,f=1;
    char ch=getchar();
    while (ch<'0'||ch>'9'){
        if (ch=='-') f=-1;
        ch=getchar();
    }
    while (ch>='0'&&ch<='9'){
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*f;
}
void dfs(int x,int y,int z){
    if (vis[z]){
        if (y+f[z]<ans) ans=y+f[z];
        return;
    }
    if (y>=ans) return;
    if (x==n+1){
        f[z]=0;
        ans=y;
        return;
    }
    if ((z&(1<<(x-1)))==0){
        dfs(x+1,y,z);
        return;
    }
    int i,j,t;
    bool pd[maxn];
    db a,b;
    fo(i,x,n) pd[i]=0;
    fo(i,x+1,n)
        if (!pd[i]&&(z&(1<<(i-1)))){
            pd[i]=1;
            t=0;
            if (dx[i]==dx[x]) continue;
            if (dy[i]/dx[i]==dy[x]/dx[x]) continue;
            b=(dy[i]*dx[x]*dx[x]-dy[x]*dx[i]*dx[i])/(dx[x]*dx[x]*dx[i]-dx[i]*dx[i]*dx[x]);
            a=(dy[i]-b*dx[i])/(dx[i]*dx[i]);
            if (a>=0) continue;
            t+=(1<<(x-1));t+=(1<<(i-1));
            fo(j,i+1,n)
                if (fabs(dy[j]-dx[j]*dx[j]*a-dx[j]*b)<eps){
                    if (z&(1<<(j-1))) t+=(1<<(j-1));
                    pd[j]=1;
                }
            dfs(x+1,y+1,z-t);
            if (f[z-t]+1<f[z]) f[z]=f[z-t]+1;
        }
    dfs(x+1,y+1,z-(1<<(x-1)));
    if (f[z-(1<<(x-1))]+1<f[z]) f[z]=f[z-(1<<(x-1))]+1;
    vis[z]=1;
}
int main(){
    freopen("angrybirds.in","r",stdin);freopen("angrybirds.out","w",stdout);
    ca=read();
    while (ca--){
        n=read();m=read();
        fo(i,0,(1<<n)-1) f[i]=n+1,vis[i]=0;
        fo(i,1,n) scanf("%lf%lf",&dx[i],&dy[i]);
        ans=n;
        dfs(1,0,(1<<n)-1);
        printf("%d\n",ans);
    }
    fclose(stdin);fclose(stdout);
    return 0;
}

相關推薦

NOIP2016提高題解

DAY1 T1 很簡單的一個模擬演算法。 從0標號就可以用模,從1標號也可以通過特判(>n就減小於1就加) #include<cstdio> #include<algorithm> #include<cmath

NOIP2016 提高部分題解

玩具謎題 題目 https://www.luogu.org/problemnew/show/P1563 程式碼 #include<bits/stdc++.h> using namespace std; int n,order;//命令 struct toy {

NOIP2016提高DAY2題解

class my_block{   public:     //If you are tired of looking at ADs     //Maybe this will be helpful to you: AdBlock(廣告攔截)     void cnt_plus(

NOIP2016提高Day1題解

前言 (我去年還是普及組蒟蒻呢……) 最近模擬考了一下NOIP2016提高組Day1,結果只搞了156分……太弱了。 T1:100分 T2:40分 T3:16分 其中T3的v打成n,然後就炸掉了,本來可以80(然後就上200了啊,T_T)。 玩具謎題

NOIP2016提高口胡題解

來來來,讓我們來口胡一波 Day1 T1 直接模擬呀!下標都-1然後用%就好了 T2 本次比賽最難的一道題。 目前據我所知有三種演算法 首先,我們考慮轉化題目。 把鏈拆成兩條,一條向上,一條向下 如果一個點x,可以到達它的s在下面,

NOIP2016 提高 第二天第二題 蚯蚓earthworm 題解

題目描述 本題中,我們將用符號[c]表示對c向下取整,例如:[3.0」= [3.1」=[3.9」=3。 蛐蛐國最近蚯蚓成災了!隔壁跳蚤國的跳蚤也拿蚯蚓們沒辦法,蛐蛐國王只好去請神刀手來幫他們消滅蚯蚓。 蛐蛐國裡現在共有n只蚯蚓(n為正整數)。每隻蚯蚓擁有長

NOIP2007 提高 題解

com 想是 設有 最長路徑 區間 space 輸出 als ons 2007 提高組題解 第一題 一開始還想是不是要用哈希表儲存呢,但仔細想了一會兒,那個數據量20W 用個快排序,時間是能過的。所以這道題用個STL的快排,再一個循環統計個數就OK了。但最後交上去評測時

NOIP2016提高】憤怒的小鳥(狀壓寬搜)

數組保存 結果 4.0 2.0 pac 之前 第一個 ... 預處理 題目描述 Kiana最近沈迷於一款神奇的遊戲無法自拔。 簡單來說,這款遊戲是在一個平面上進行的。 有一架彈弓位於(0,0)處,每次Kiana可以用它向第一象限發射一只紅色的小鳥,小鳥們的飛行軌跡均為形如的

洛谷 P2827 蚯蚓(NOIp2016提高D2T2)

mat 希望 c++ ffffff define fff 空行 特殊 到來 題目描述 本題中,我們將用符號?c?表示對c向下取整,例如:?3.0?=?3.1?=?3.9?=3。 蛐蛐國最近蚯蚓成災了!隔壁跳蚤國的跳蚤也拿蚯蚓們沒辦法,蛐蛐國王只好去請神刀手來幫他們消滅蚯蚓。

[NOIP2016提高]合數問題

() -a printf ++ 提高 urn 數論 tro ont 題目:UOJ#263、洛谷P2822、Vijos P2006、codevs5947。 題目大意:t組數據,每次給你n和m$\leq 2000$,求對於所有的$(0\leq i\leq n)$,$(0\le

NOIP2016提高】換教室

algorithm .org pan i++ mes ble 安排 turn 路徑 https://www.luogu.org/problem/show?pid=1850 題面很長,實質很水的一道期望DP題。題面自帶勸退效果。 首先用Floyd算出任意兩點的最短路徑。然後設

[NOIP2016提高]憤怒的小鳥

noi 二維 枚舉 def 拋物線 ++ scanf pre sizeof 題目:UOJ#265、洛谷P2831、Vijos P2008。 題目大意:有n頭豬,都在一個二維坐標系裏(每頭豬坐標為兩位小數)。規定每只鳥能從(0,0)處發射,且經過的拋物線一定為$y=ax^2

NOIP2016提高初賽(2)四、閱讀程序寫結果2、

cal 當前 閱讀 輸入 自己 字符 細節 一是 表示 #include <iostream> using namespace std; int main() { char a[100][100], b[100][100]; string c[100]; s

NOIP2016提高初賽(2)四、讀程序寫結果3、求最長回文子序列

所有 並且 names mes font esp mic abcd 大小 #include <iostream> using namespace std; int lps(string seq, int i, int j) { int len1, len2;

NOIP2016提高】 Day1 T3 換教室

scanf spa -s 提高 i++ double -1 教室 pan 題目鏈接:https://www.luogu.org/problemnew/show/P1850 此題正解為dp。 我們先用floyd處理出任意兩個教室之間的距離,用dis[i][j]表示。

[NOIp2016提高]天天愛跑步

秒拍 tin reg git clu getchar() tdi gist lin 題目大意:   有一棵n個點的樹,每個點上有一個攝像頭會在第w[i]秒拍照。   有m個人再樹上跑,第i個人沿著s[i]到t[i]的路徑跑,每秒鐘跑一條邊。   跑到t[i]的下一秒

NOIP2016提高A7.16】第三條跑道

以及 span const queue als TP 分析 namespace 技術分享 題目 數據範圍 分析 時限5000ms。 我們註意到\(a_{i}初始值以及x小於等於600且非零\) 也就是說,\(a_{i}\)的質因數一定小於600,而600以內的質因數只有

NOIP2016提高day2】蚯蚓

AC AS orm max 可能 printf display OS 例如 題目 本題中,我們將用符號 LcJ 表示對 c 向下取整,例如: L3.0J = L3.1J = L3.9J = 3 。 蛐蛐國最近蚯蚓成災了!隔壁跳蚤國的跳蚤也拿蚯蚓們沒辦法,蛐蛐國王只好去 請神

NOIP2016提高day1】?換教室

獲得 表示 牛牛 所在 進行 發現 更換 學校 而且 題目 對於剛上大學的牛牛來說,他面臨的第一個問題是如何根據實際情況申請合適的 課程。 在可以選擇的課程中,有 2n 節課程安排在 n 個時間段上。 在第 i ( 1 ≤ i ≤ n )個 時間段上,兩節內容相同的課程同時

NOIP2016提高復賽day2】天天愛跑步

分享 csdn .net ons src 情況 自己的 for net 題目 小 C 同學認為跑步非常有趣,於是決定制作一款叫做《天天愛跑步》的遊戲。 《天天愛跑步》是一個養成類遊戲,需要玩家每天按時上線,完成打卡任務。 這個遊戲的地圖可以看作一棵包含 n 個結點和 n ?