首页 > 其他 > 详细

NOIP2017题解

时间:2018-09-07 21:56:44      阅读:227      评论:0      收藏:0      [点我收藏+]

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题解

原文:https://www.cnblogs.com/ZH-comld/p/9607257.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!