NOIP2014提升組 題解報告

D1

T1 無線網路發射器選址

題目大意:找一個矩形,使其覆蓋的目標點最大。

題目過水,直接暴力搞過去,代碼就不貼了。
但我TM竟然有個地方SB了,調了半天才發現輸入有問題:node

scanf("%d%d%d",&x,&y,&t[x][y]);//這是我原來的寫法.....

T2 尋找道路

題目大意:給你一個有向圖,找一條從起點到終點的最短路徑,且路徑上的全部點的出邊所指向的點都直接或間接與終點連通。

先處理出全部知足條件的點,而後在上面跑死了的\(spfa\)便可。
注意這裏咱們不是直接去找與終點相連的點,由於這樣會漏掉一些點(別問我怎麼知道的)。
因此咱們能夠用到不了終點的點去跟新其它點便可。ios

\(Code:\)算法

const int N=2e5+5;
vector<int>G1[N],G2[N];
//G1正圖  G2反圖
bool T[N],f[N];
int n,m,x,y,s,t,d[N];
queue<int>q;
inline void bfs()
{
    q.push(t);T[t]=f[t]=1;
    while(!q.empty())
    {
        int x=q.front();q.pop();
        for(rg int i=0;i<G2[x].size();++i)
        {
            int v=G2[x][i];
            if(!T[v]) T[v]=f[v]=1,q.push(v);
        }
    }
    for(rg int i=1;i<=n;++i)
        if(!T[i])
            for(rg int j=0;j<G2[i].size();++j)
            {
                int v=G2[i][j];
                if(f[v]) f[v]=0;
            }
    while(!q.empty()) q.pop();
    q.push(s);memset(d,0x3f,sizeof d);d[s]=0;
    while(!q.empty())
    {
        int x=q.front();q.pop();
        for(rg int i=0;i<G1[x].size();++i)
        {
            int v=G1[x][i];
            if(f[v] && d[v]>d[x]+1)
            {
                d[v]=d[x]+1;
                q.push(v);
            }
        }
    }
}

T3 解方程

題目大意:求給定一元\(n\)次方程在\([1,m]\)上的整數解。

我最開始還覺得要打高精度,其實能夠用秦九韶算法來解決。
這個算法能夠將一元\(n\)次多項式的求值問題轉化爲\(n\)個一次式的算法,而後……本身看度娘吧
總之,這個算法可讓咱們在\(O(nm)\)的時間內解決該問題。(過程當中能夠隨便膜一個數如192*****來簡化運算)app

\(Code:\)優化

#include<cstdio>
#include<vector>
using namespace std;
#define int long long
const int p=19260817;
int n,m,a[105];
vector<int>Ans;
inline int read()//騷氣讀入
{
    int f=1,x=0;register char c=getchar();
    while(c>'9' || c<'0') {if(c=='-') f=-1;c=getchar();}
    while(c>='0' && c<='9') x=((x<<3)+(x<<1)+(c^'0'))%p,c=getchar();
    return x*f%p;//記得取膜
}
signed main()
{
    n=read(),m=read();
    for(int i=0;i<=n;++i) a[i]=read();
    for(int i=1;i<=m;++i)
    {
        int ans=0;
        for(int j=n;j>=1;--j)
            ans=(ans+a[j])*i%p;
        ans=(ans+a[0])%p;
        if(!ans) Ans.push_back(i);
    }
    printf("%lld\n",(int)Ans.size());
    for(int i=0;i<(int)Ans.size();++i) printf("%lld\n",Ans[i]);
    return 0;
}

D2

T1 生活大爆炸版石頭剪刀布

題目大意:石頭剪刀布

巨水的模擬,貼一下代碼就好了~~this

\(Code:\)spa

#include<cstdio>
using namespace std;
const int N=205;
int n,na,nb,a[N],b[N],c[5][5],cnta,cntb;
//0表示「剪刀」,1表示「石頭」,2表示「布」,3表示「蜥蜴人」,  4表示「斯波克
int main()
{
    scanf("%d%d%d",&n,&na,&nb);
    for(int i=1;i<=na;++i) scanf("%d",&a[i]);
    for(int i=1;i<=nb;++i) scanf("%d",&b[i]);
    c[1][0]=c[2][1]=c[0][2]=c[0][3]=c[1][3]=c[2][4]=c[3][4]=c[3][2]=c[4][0]=c[4][1]=1;
    for(int i=1;i<=n;++i)
    {
        int A=i%na,B=i%nb;
        if(!A) A=na;
        if(!B) B=nb;
        // printf("%d %d\n",a[A],b[B]);
        cnta+=c[a[A]][b[B]],cntb+=c[b[B]][a[A]];
        // printf("cnt:%d %d\n",cnta,cntb);
    }
    printf("%d %d",cnta,cntb);
    return 0;
}

T2 聯合權值

題目大意:給你一顆樹,邊權均爲\(1\),每一個點有點權\(w_i\)(\(w_i \le 2 \times 10^4\)),求\(Max_{dis(u,v)=2}w[u] \times w[v]\)\(\sum_{dis(u,v)=2}w[u] \times w[v]\)。(\(n \le 2 \times 10^5\)

直接 \(O(n^2)\) 暴力枚舉是確定過不了的,要想一些奇技淫巧來優化。code

要求\(u,v\)之間距離爲\(1\),又是在樹上,那麼它們之間必然有\(1\)箇中轉點。因而咱們能夠枚舉該點。get

\(n\)的範圍巨大,怎麼能高效地跟新答案呢?這裏有一個神奇的公式:string

\(S = \sum_{i=1}^n a_i\) , 則有

\(( (S - a_1) + (S - a_2) + \cdots + (S - a_n) ) \times 2 = (a_1+a_2+ \cdots +a_{n-1}+a_n)^2 - (a_1^2+a_2^2+ \cdots +a_{n-1}^2+a_n^2)\)

由於咱們加的是雙向邊,因此對於一個節點\(x\)與它相連的全部點之間均可以互相構成聯合權值

將其表示出來:

  • \(S\)爲與它相連的全部點的權,那麼任意一個點能造成的聯合權值爲\(S - a_i\)
  • 那麼以該點爲中轉點所造成的聯合權值,爲\(( (S - a_1) + (S - a_2) + \cdots + (S - a_n) ) \times 2\)...這不就是上面那個式子嗎?因而,咱們就獲得了一個優秀的 \(O(n)\)作法。

\(Code:\)

#include<cstdio>
#include<vector>
#include<algorithm>
#define rg register
#define ll long long
struct ios{
    template<typename TP>
    inline ios operator >> (TP &x)
    {
        TP f=1;x=0;rg char c=getchar();
        for(;c>'9' || c<'0';c=getchar()) if(c=='-') f=-1;
        for(;c>='0' && c<='9';c=getchar()) x=(x<<3)+(x<<1)+(c^'0');
        x*=f;
        return *this;
    }
    template<typename TP>
    inline ios operator << (TP x)
    {
        char s[66];rg int cnt=0;
        if(!x) putchar('0');
        if(x<0) x=-x,putchar('-');
        while(x) ++cnt,s[cnt]=x%10+'0',x/=10;
        while(cnt) putchar(s[cnt]),--cnt;
        return *this;
    }
    inline ios operator << (char x)
    {
        putchar(x);
        return *this;
    }
}io;
const int N=2e5+5;
std::vector<int>G[N];
const int p=10007;
int n,a,b,w[N],ans,sum;
int main()
{
    io>>n;
    for(rg int i=1;i<n;++i)
    {
        io>>a>>b;
        G[a].push_back(b),G[b].push_back(a);
    }
    for(rg int i=1;i<=n;++i) io>>w[i];
    // for(rg int i=1;i<=n;++i) io<<w[i]<<' ';
    // io<<'\n';
    /*--------------------------以某個節點爲中轉點的聯合權值之和等於權值和的平方減去權值的平方和----------------------*/
    for(rg int i=1;i<=n;++i)
    {
        int max_1=0,max_2=0;
        int he=0,pf=0;
        for(int k=0;k<(int)G[i].size();++k)
        {
            int v=G[i][k];
            // io<<i<<' '<<G[i].size()<<' '<<k<<' '<<v<<'\n';
            if(w[v]>max_1) max_2=max_1,max_1=w[v];
            else if(w[v]>max_2) max_2=w[v];
            he=(he+w[v])%p,pf=(pf+w[v]*w[v])%p;
        }
        he=he*he%p;
        // io<<'h'<<'h'<<max_1<<' '<<max_2<<'\n';
        ans=std::max(ans,max_1*max_2);
        sum=(sum+he-pf+p)%p;//注意he-pf可能爲負數
    }
    io<<ans<<' '<<sum;
    return 0;
}

T3 飛揚的小鳥

題目大意:(首先,請確保你知道\(CodeVs-flappy bird\))給你每一個位置點擊上升的距離和不點擊降低的距離、每一個管道的位置。求能不能經過以及若是能經過,最少點擊數爲多少。

很顯然能夠看出,這題是\(DP\)(求最優解,數據範圍比較大,還不能貪心,除了\(DP\),還真就沒有作法了)

  • \(dp[i][j]\)表示橫座標爲\(i\)時高度爲\(j\)的最少點擊次數,用$ + \infty$來表示不可能達到這個狀態。
  • 每一個管道的位置不能走,其餘地方就按照規則來走,注意小鳥撞到天花板不會掛,只有高度爲\(0\)是纔會掛,因此特判一下。

\(Code:\)

#include<cstdio>
#include<algorithm>
#include<cmath>
#define rg register
#define ll long long
#define cg c=getchar()
using namespace std;
struct ios{
    template<typename TP>
    inline ios operator >> (TP &x)
    {
        TP f=1;x=0;rg char cg;
        for(;c>'9' || c<'0';cg) if(c=='-') f=-1;
        for(;c>='0' && c<='9';cg) x=(x<<3)+(x<<1)+(c^'0');
        x*=f;
        return *this;
    }
    template<typename TP>
    inline ios operator << (TP x)
    {
        char s[66];rg int cnt=0;
        if(x<0) x=-x,putchar('-');
        if(!x) putchar('0');
        while(x) s[++cnt]=x%10+'0',x/=10;
        while(cnt) putchar(s[cnt--]);
        return *this;
    }
    inline ios operator << (char s)
    {
        putchar(s);
        return *this;
    }
}io;
const int N=1e4;
const int M=1e3;
const int inf=0x3f3f3f3f;
int n,m,k,cnt=1;
struct node{
    int x,y;
}a[N];
struct Node{
    int pos,L,H;
    inline bool operator < (const Node b) const {
        return pos<b.pos;
    }
}g[N];
int dp[N][M],vis[N][M];
int main()
{
    io>>n>>m>>k;
    for(rg int i=0;i<n;++i) io>>a[i].x>>a[i].y;
    for(rg int i=1;i<=k;++i)
    {
        io>>g[i].pos>>g[i].L>>g[i].H;
        for(int j=1;j<=g[i].L;++j) vis[g[i].pos][j]=1;
        for(int j=g[i].H;j<=m;++j) vis[g[i].pos][j]=1;
    }
    sort(g+1,g+k+1);
    //io<<'h'<<'h'<<'h'<<'\n';
    for(rg int i=1;i<=n;++i)
        for(rg int j=1;j<=m;++j) dp[i][j]=inf;
    for(rg int i=1;i<=n;++i)
    {
    //    io<<'h'<<'h'<<'h'<<'\n';
        int L=1,R=m;
        if(g[cnt].pos==i) L=g[cnt].L+1,R=g[cnt].H-1,++cnt;
        // if(i==5) io<<L<<' '<<R<<'\n';
        int x=a[i-1].x,y=a[i-1].y;
        for(rg int j=L;j<=R;++j)
        {
            if(j==m)
            {
                for(rg int l=1;l<=m;++l)
                {
                    if(!vis[i-1][l])
                    {
                        int w=ceil(1.0*(m-l)/x);
                        if(j==l) w=1;
                        dp[i][j]=min(dp[i][j],dp[i-1][l]+w);
                    }
                }
                continue;
            }
            // if(i==5) io<<j+y<<' '<<y<<'\n';
            if(j+y<=m && !vis[i-1][j+y]) dp[i][j]=min(dp[i][j],dp[i-1][j+y]);
            int w=j/x;
            for(rg int l=1;l<=w;++l)
                if(j-x*l>0 && !vis[i-1][j-x*l])
                    dp[i][j]=min(dp[i][j],dp[i-1][j-x*l]+l);
        }
    //    io<<'h'<<'h'<<'h'<<'\n';
    }
    int ans=inf;
    // for(int i=m;i>=0;--i)
    // // for(int i = 0; i <= n; ++i)
    // {
    //     // for (int j = 0; j <= m; ++j)
    //     for(int j=0;j<=n;++j)
    //     {
    //         if(dp[j][i]==inf) io<<'*'<<' ';
    //         else io<<dp[j][i]<<' ';
    //     }
    //     io<<'\n';
    // }
    for(rg int i=1;i<=m;++i) ans=min(ans,dp[n][i]);
    if(ans==inf)
    {
        io<<0<<'\n';
        for(rg int i=1;i<=k;++i)
        {
            int p=g[i].pos;
            for(rg int j=1;j<=m;++j)
            {
                if(dp[p][j]!=inf) break;
                if(j==m)
                {
                    io<<i-1<<'\n';
                    return 0;
                }
            }
        }
    }
    io<<1<<'\n'<<ans;
    return 0;
}

然鵝你發現這個達不到上界的\(O(nm^2)\)\(DP\)只能水\(80\)分。(不過聯賽時有\(80\)分也不錯了
那該如何優化呢?
咱們發現,小鳥每次上升屢次,但只能降低一次(保證不超邊界時)。
上升屢次,不就至關因而多重揹包嗎?
那麼降低一次,就能夠看作\(01\)揹包。
有了這個思路,咱們就能夠成功地把複雜度將爲\(O(nm)\)

\(Code:\)

#include<cstdio>
#include<cstring>
#include<algorithm>
#define rg register
#define ll long long
#define cg c=getchar()
using namespace std;
struct ios{
    template<typename TP>
    inline ios operator >> (TP &x)
    {
        TP f=1;x=0;rg char cg;
        for(;c>'9' || c<'0';cg) if(c=='-') f=-1;
        for(;c>='0' && c<='9';cg) x=(x<<3)+(x<<1)+(c^'0');
        x*=f;
        return *this;
    }
    template<typename TP>
    inline ios operator << (TP x)
    {
        char s[66];rg int cnt=0;
        if(x<0) x=-x,putchar('-');
        if(!x) putchar('0');
        while(x) s[++cnt]=x%10+'0',x/=10;
        while(cnt) putchar(s[cnt--]);
        return *this;
    }
    inline ios operator << (char s)
    {
        putchar(s);
        return *this;
    }
}io;
const int N=1e4+5;
const int M=1e3+5;
const int inf=0x3f3f3f3f;
int n,m,k;
struct node{
    int x,y;
}a[N];
int dp[N][M<<1],vis[N],L[N],R[N];
int main()
{
    // freopen("1.in","r",stdin);
    io>>n>>m>>k;
    for(rg int i=1;i<=n;++i) io>>a[i].x>>a[i].y,L[i]=1,R[i]=m;
    for(rg int i=1;i<=k;++i)
    {
        int a,b,c;io>>a>>b>>c;
        vis[a]=1,L[a]=b+1,R[a]=c-1;
    }
    memset(dp,0x3f,sizeof dp);
    for(rg int i=1;i<=m;++i) dp[0][i]=0;
    for(rg int i=1;i<=n;++i)
    {
        int x=a[i].x,y=a[i].y;
        for(rg int j=x+1;j<=m+x;++j)
            dp[i][j]=min(dp[i-1][j-x]+1,dp[i][j-x]+1);
        for(rg int j=m+1;j<=m+x;++j)
            dp[i][m]=min(dp[i][m],dp[i][j]);
        for(rg int j=1;j+y<=m;++j)
            dp[i][j]=min(dp[i][j],dp[i-1][j+y]);
        // io<<i<<' '<<L<<' '<<R<<'\n';
        for(rg int j=1;j<=L[i]-1;++j) dp[i][j]=inf;
        for(rg int j=R[i]+1;j<=m;++j) dp[i][j]=inf;
    }
    int ans=inf;
    // io<<inf<<'\n';
    // for(int i=m;i>=0;--i)
    // // for(int i = 0; i <= n; ++i)
    // {
    //     // for (int j = 0; j <= m; ++j)
    //     for(int j=0;j<=n;++j)
    //       {
    //         if(dp[j][i]==inf) io<<'*'<<' ';
    //         else io<<dp[j][i]<<' ';
    //     }
    //     io<<'\n';
    // }
    for(rg int i=1;i<=m;++i) ans=min(ans,dp[n][i]);
    if(ans>=inf)
    {
        io<<0<<'\n';
        int i,j;
        for(i=n;i>=1;--i)
        {
            for(j=1;j<=m;++j)
            {
                if(dp[i][j]<inf) break;
            }
            if(j<=m) break;
        }
        ans=0;
        for(int j=1;j<=i;++j)
            if(vis[j]) ++ans;
        io<<ans;
    }
    else io<<1<<'\n'<<ans;
    return 0;
}
相關文章
相關標籤/搜索