1. 程式人生 > >NOIP2017題解

NOIP2017題解

分開 include 細節 amp def sin 挖掘 老鼠 模擬題

T1小凱的疑惑

小凱手中有兩種面值的金幣,兩種面值均為正整數且彼此互素。每種金幣小凱都有 無數個。在不找零的情況下,僅憑這兩種金幣,有些物品他是無法準確支付的。現在小 凱想知道在無法準確支付的物品中,最貴的價值是多少金幣?註意:輸入數據保證存在 小凱無法準確支付的商品。

Solution

2017數論題,感覺是歷史以來最難的一道。

對於ax+by=c(a>=0,b>=0)當方程無解是,必定為x<0&&y>0且下一組解x>0&&y<0那x為-1,y為a-1,那c就是a*b-a-b。

Code

#include<iostream>
#include
<algorithm> using namespace std; long long a,b; int main() { cin>>a>>b; cout<<a*b-a-b; }

T2時間復雜度

題面太長不粘了

模擬題,註意細節。。

Code

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int t,vis[1009],tag,tag2,n,ans,num,tot,s[109
],st[109],top,maxx=10000000,yy,zz; char c,cc[109],x,y[10],z[10]; int main() { cin>>t; while(t--) { memset(vis,0,sizeof(vis)); scanf("%d",&n); tag=tag2=tot=num=ans=top=0; cin>>cc; int p=strlen(cc); if(cc[2]==n)
for(int j=4;j<=p;++j) if(cc[j]>=0&&cc[j]<=9) num=num*10+cc[j]-0; else break; for(int i=1;i<=n;++i) { cin>>c; if(c==F) { cin>>x>>y>>z; yy=zz=0; if(vis[x])tag=1; vis[x]=1; s[++top]=x; if(y[0]!=n) { int o=strlen(y); for(int k=0;k<o;++k) yy=yy*10+y[k]-0; } else yy=maxx; if(z[0]!=n) { int o=strlen(z); for(int k=0;k<o;++k) zz=zz*10+z[k]-0; } else zz=maxx; if(yy<maxx&&zz==maxx) { st[top]=1; tot++; } else if(yy>zz) { st[top]=-1; tag2++; } else { st[top]=0; } if(!tag2)ans=max(ans,tot); } else { if(!top)tag=1; if(st[top]==1)tot--; else if(st[top]==-1)tag2--; vis[s[top]]=0; top--; } } if(top||tag) { cout<<"ERR\n"; continue; } if(ans==num)cout<<"Yes\n"; else cout<<"No\n"; } return 0; }

T3逛公園

T4奶酪

現有一塊大奶酪,它的高度為 hh,它的長度和寬度我們可以認為是無限大的,奶酪 中間有許多 半徑相同 的球形空洞。我們可以在這塊奶酪中建立空間坐標系,在坐標系中, 奶酪的下表面為z = 0z=0,奶酪的上表面為z = hz=h。

現在,奶酪的下表面有一只小老鼠 Jerry,它知道奶酪中所有空洞的球心所在的坐 標。如果兩個空洞相切或是相交,則 Jerry 可以從其中一個空洞跑到另一個空洞,特別 地,如果一個空洞與下表面相切或是相交,Jerry 則可以從奶酪下表面跑進空洞;如果 一個空洞與上表面相切或是相交,Jerry 則可以從空洞跑到奶酪上表面。

位於奶酪下表面的 Jerry 想知道,在 不破壞奶酪 的情況下,能否利用已有的空洞跑 到奶酪的上表面去?

Solution

水題 ,n^2並查集搞一下就可以了。

Code

#include<iostream>
#include<cstdio>
#include<cmath>
#define N 1012
using namespace std;
int n,t,f[N];
double h,r;
struct ssd{
    double x,y,z;
}a[N];
double calc(int i,int j){
    return sqrt((a[i].x-a[j].x)*(a[i].x-a[j].x)+(a[i].y-a[j].y)*(a[i].y-a[j].y)+(a[i].z-a[j].z)*(a[i].z-a[j].z));
}
int find(int x){return f[x]=f[x]==x?x:find(f[x]);} 
int main(){
    scanf("%d",&t);
    while(t--){
        scanf("%d%lf%lf",&n,&h,&r);
        for(int i=0;i<=n+1;++i)f[i]=i;
        for(int i=1;i<=n;++i){
         scanf("%lf%lf%lf",&a[i].x,&a[i].y,&a[i].z);
         for(int j=1;j<i;++j)
           if(calc(i,j)<=r*2){
               int xx=find(i),yy=find(j);
               if(xx!=yy)f[xx]=yy;
           }
           if(a[i].z-r<=0){
                 int xx=find(i),yy=find(0);
                 if(xx!=yy)f[xx]=yy;
           }
           if(a[i].z+r>=h){
               int xx=find(i),yy=find(n+1);
               if(xx!=yy)f[xx]=yy;
           }
        }
        if(find(0)==find(n+1))printf("Yes\n");
        else printf("No\n");
    }
    return 0;
} 

T5寶藏

參與考古挖掘的小明得到了一份藏寶圖,藏寶圖上標出了 n 個深埋在地下的寶藏屋, 也給出了這 n 個寶藏屋之間可供開發的m 條道路和它們的長度。

小明決心親自前往挖掘所有寶藏屋中的寶藏。但是,每個寶藏屋距離地面都很遠, 也就是說,從地面打通一條到某個寶藏屋的道路是很困難的,而開發寶藏屋之間的道路 則相對容易很多。

小明的決心感動了考古挖掘的贊助商,贊助商決定免費贊助他打通一條從地面到某 個寶藏屋的通道,通往哪個寶藏屋則由小明來決定。

在此基礎上,小明還需要考慮如何開鑿寶藏屋之間的道路。已經開鑿出的道路可以 任意通行不消耗代價。每開鑿出一條新道路,小明就會與考古隊一起挖掘出由該條道路 所能到達的寶藏屋的寶藏。另外,小明不想開發無用道路,即兩個已經被挖掘過的寶藏 屋之間的道路無需再開發。

新開發一條道路的代價是:

L代表這條道路的長度,K代表從贊助商幫你打通的寶藏屋到這條道路起點的寶藏屋所經過的 寶藏屋的數量(包括贊助商幫你打通的寶藏屋和這條道路起點的寶藏屋) 。

請你編寫程序為小明選定由贊助商打通的寶藏屋和之後開鑿的道路,使得工程總代 價最小,並輸出這個最小值。

Solution

看到n非常小,考慮直接暴力枚舉每個起點,設dp[S]表示挖到的集合為S的最小花費,記憶化搜索即可,每次枚舉集合裏的一個點和一個新點(非常暴力對不對)。

Code

#include<iostream>
#include<cstring>
#include<cstdio>
#define inf 0x7f7f7f7f
using namespace std;
int dis[20],l[20][20],n,m,a,b,c;
long long dp[1<<12],ans;
void dfs(int S)
{
    for(int u=1;u<=n;++u)
      if(S&(1<<(u-1)))
        for(int v=1;v<=n;++v)
          if(!(S&(1<<(v-1)))&&l[u][v]!=inf&&dp[S|1<<(v-1)]>dp[S]+dis[u]*l[u][v])
          {
              dp[S|(1<<(v-1))]=dp[S]+dis[u]*l[u][v];
              dis[v]=dis[u]+1;
              dfs(S|(1<<(v-1)));
              dis[v]=inf;
          }
}
int main()
{
    scanf("%d%d",&n,&m);
    memset(l,0x7f,sizeof(l));
    for(int i=1;i<=m;++i)
    {
        scanf("%d%d%d",&a,&b,&c);
        l[a][b]=min(l[a][b],c);
        l[b][a]=l[a][b];
    }
    ans=0x7f7f7f7f;
    for(int i=1;i<=n;++i)
    {
       memset(dis,0x7f,sizeof(dis));
       memset(dp,0x7f,sizeof(dp));
       dp[1<<(i-1)]=0;
       dis[i]=1;
       dfs(1<<(i-1));
       ans=min(ans,dp[(1<<n)-1]);
       //cout<<dp[(1<<n)-1]<<" ";
    }
    cout<<ans;
    return 0;
}

T6列隊

Solution

觀察到最後一列很特殊,考慮分開維護。

對於每行的前m-1個點開線段樹,裏面記個size有表示這個位置沒人。

那麽整體向做移動怎麽維護?

其實不用管它,畢竟對於前m-1列只會從後面插入。

在最後一列開線段樹,操作時分類討論一下。

Code

#include<iostream>
#include<cstdio>
#define ls tr[root].l
#define rs tr[root].r
#define N 300002
using namespace std;
typedef long long ll;
ll tot,q,T[N],t[N],m,n,x,y;
ll rd(){
    ll x=0;char c=getchar();while(!isdigit(c))c=getchar();
    while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    return x;
}
struct fd{
    ll l,r,size,tag;
}tr[6000002];
void ins(ll &root,ll l,ll r,ll x,ll y){
    if(!root)root=++tot;
    if(l==r){
        tr[root].tag=y;
        tr[root].size=0;
        return;
    }
    int mid=(l+r)>>1;
    if(mid>=x)ins(ls,l,mid,x,y);
    else ins(rs,mid+1,r,x,y);
    tr[root].size=tr[ls].size+tr[rs].size;
}
pair<ll,bool> find(ll &root,ll l,ll r,ll k,ll tag){
    if(!root)root=++tot;tr[root].size++;
    if(l==r)
    if(r<=tag)return make_pair(l,1);
    else return make_pair(tr[root].tag,0); 
    int mid=(l+r)>>1,cnt=(mid-l+1)-tr[ls].size;
    if(cnt>=k)return find(ls,l,mid,k,tag);
    else return find(rs,mid+1,r,k-cnt,tag);
}
int main(){
    n=rd();m=rd();q=rd();
    for(int i=1;i<=n;++i)t[i]=m-1;
    t[n+1]=n;
    for(int i=1;i<=q;++i){
        x=rd();y=rd();
        if(y==m){
            pair<ll,bool> tmp=find(T[n+1],1,n+q,x,n);//在(n+1)棵線段樹中找x大 
            ll ji=tmp.first; 
            if(tmp.second)ji*=m;//乘上列 
            printf("%lld\n",ji);
            ins(T[n+1],1,n+q,++t[n+1],ji);//在這一列中插入 
        }
        else{
            pair<ll,bool>tmp=find(T[x],1,m+q,y,m-1);//在x行中找y大。 
            ll ji=tmp.first;
            if(tmp.second)ji+=(x-1)*m;
            printf("%lld\n",ji);
            pair<ll,bool>tmp2=find(T[n+1],1,n+q,x,n);
            ll ji2=tmp2.first;
            if(tmp2.second)ji2*=m;
            ins(T[x],1,m+q,++t[x],ji2);
            ins(T[n+1],1,n+q,++t[n+1],ji);
        }
    }
    return 0;
} 

NOIP2017題解