zoukankan      html  css  js  c++  java
  • NOIP2017题解

    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
    ";
                continue;
            }
            if(ans==num)cout<<"Yes
    ";
            else cout<<"No
    ";
        }
        return 0;
    }

    T3逛公园

    T4奶酪

    现有一块大奶酪,它的高度为 hh,它的长度和宽度我们可以认为是无限大的,奶酪 中间有许多 半径相同 的球形空洞。我们可以在这块奶酪中建立空间坐标系,在坐标系中, 奶酪的下表面为z = 0z=0,奶酪的上表面为z = hz=h。

    现在,奶酪的下表面有一只小老鼠 Jerry,它知道奶酪中所有空洞的球心所在的坐 标。如果两个空洞相切或是相交,则 Jerry 可以从其中一个空洞跑到另一个空洞,特别 地,如果一个空洞与下表面相切或是相交,Jerry 则可以从奶酪下表面跑进空洞;如果 一个空洞与上表面相切或是相交,Jerry 则可以从空洞跑到奶酪上表面。

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

    Solution

    水题 ,可以把上界和下界都看成一个点,n^2并查集搞一下就可以了。

    听说这题爆longlong,反正我开double直接开根,精度没什么问题

    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
    ");
            else printf("No
    ");
        }
        return 0;
    } 

    T5宝藏

    如果我们设dp[s]表示当前选择集合为s时的最小代价,这个状态是有问题的,说不好听点它压根就是错的,因为它压根就没有考虑每个节点的深度。

    我们可以这么考虑问题,我们以深度作为阶段,每次从当前选择集合中扩展一些点作为第i层。

    这时我们就可以设计状态了,令dp[i][s]表示当前深度做到了i,当前选择集合为s时的最优方案。

    转移时可以枚举扩展出哪些点,然后直接转移。

    这样的复杂度是3^n*n^2。

    但这样太慢,可以利用lowbit等技巧优化常数

    Code

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #define N 13
    #define inf 0x3f3f3f3f
    using namespace std;
    typedef long long ll;
    int dp[N][1<<N],n,m,x,y,l,e[N][N],ji[1<<N],f[1<<N],b[N],tag[N],be[1<<N],ans;
    inline int rd(){
        int x=0;char c=getchar();
        while(!isdigit(c))c=getchar();
        while(isdigit(c)){
            x=(x<<1)+(x<<3)+(c^48);c=getchar();
        }
        return x;
    }
    int main(){
        n=rd();m=rd();
        memset(e,0x3f,sizeof(e));
        for(int i=1;i<=m;++i){
            x=rd();y=rd();l=rd();
            e[x][y]=e[y][x]=min(e[x][y],l);
        }
        memset(dp,0x3f,sizeof(dp));
        for(int i=0;i<n;++i)ji[1<<i]=i,dp[0][1<<i]=0;ans=inf;
        ans=min(ans,dp[0][(1<<n)-1]);
        for(int i=1;i<=n;++i)
          for(int s=1;s<(1<<n);++s){
              int top=0;
              memset(b,0x3f,sizeof(b));
            for(int j=1;j<=n;++j)if(!(s&(1<<j-1))){
                tag[top]=1<<j-1;
                for(int S=s;S;S-=(S&-S))if(e[j][ji[S&-S]+1]!=inf)b[top]=min(b[top],e[j][ji[S&-S]+1]*i);
                top++;
            }
            for(int j=1;j<(1<<top);++j){
                f[j]=f[j-(j&-j)]+b[ji[j&-j]];
                if(f[j]>=inf)f[j]=inf;
                be[j]=be[j-(j&-j)]|tag[ji[j&-j]];
                dp[i][s|be[j]]=min(dp[i][s|be[j]],dp[i-1][s]+f[j]);    
            }
          ans=min(ans,dp[i][(1<<n)-1]);
        }
        printf("%d",ans);
        return 0;
    } 

    T6列队

    Solution

    观察到最后一列很特殊,考虑分开维护。

    对于每行的前m-1个点开线段树,里面记个size有表示这个位置没人。

    那么整体向做移动怎么维护?

    其实不用管它,毕竟对于前m-1列只会从后面插入。

    那么对于一颗线段树,我们的操作只有从后面插入一个节点,从集合删去一个节点和查找第K个元素。

    在最后一列开线段树,操作时分类讨论一下。

    注意:n和m不要弄反,初始化size要为m-1不是m!!!

    Code

    #include<iostream>
    #include<cstdio>
    #define N 300002
    using namespace std;
    typedef long long ll;
    int size[N],T[N],tot,q;
    long long x,y,n,m;
    struct node{ll first;bool second;};
    struct seg{ll num;int l,r,sum;}tr[N*20];
    node find(int &cnt,int l,int r,int k){
        if(!cnt)cnt=++tot;tr[cnt].sum++;
        if(l==r){
            if(tr[cnt].num)return node{tr[cnt].num,0};
            else return node{l,1};
        }
        int mid=(l+r)>>1,num=mid-l+1-tr[tr[cnt].l].sum;
        if(num>=k)return find(tr[cnt].l,l,mid,k);
        else return find(tr[cnt].r,mid+1,r,k-num);
    } 
    void add(int &cnt,int l,int r,int x,ll y){
        if(!cnt)cnt=++tot;
        if(l==r){tr[cnt].num=y;return;}
        int mid=(l+r)>>1;
        if(mid>=x)add(tr[cnt].l,l,mid,x,y);
        else add(tr[cnt].r,mid+1,r,x,y);
    }
    inline int rd(){
        int x=0;char c=getchar();while(!isdigit(c))c=getchar();
        while(isdigit(c))x=(x<<1)+(x<<3)+(c^48),c=getchar();return x;
    }
    int main(){
        n=rd();m=rd();q=rd();
        for(int i=1;i<=n;++i)size[i]=m-1;//care
        size[n+1]=n;
        for(int i=1;i<=q;++i){
            x=rd();y=rd();
            if(y==m){
                node tmp=find(T[n+1],1,n+q,x);
                ll num=tmp.first;
                if(tmp.second)num=num*m;
                printf("%lld
    ",num);
                add(T[n+1],1,n+q,++size[n+1],num);
            }
            else{
                node tmp=find(T[x],1,m+q,y);
                ll num=tmp.first;
                if(tmp.second)num=num+(x-1)*m;
                printf("%lld
    ",num);
                tmp=find(T[n+1],1,n+q,x);
                ll num2=tmp.first;
                if(tmp.second)num2=num2*m;
                add(T[x],1,m+q,++size[x],num2);
                add(T[n+1],1,n+q,++size[n+1],num);
            }
        }
        return 0;
    }
  • 相关阅读:
    初识 visJs (基于html5 canvas开发的可视化框架)
    VueJs
    VueJS 使用i18n做国际化切换中英文
    vue-cli项目接口地址可配置化(多环境部署)一处修改多处适用
    vue + element-ui 制作下拉菜单(可配置路由、可根据路由高亮list、可刷新自动展开定位路由)
    vue-cli -- > 项目基本构建的方法
    javascript代码工具库
    HTML5新功能之六 《Web通信、WebSockets和跨文档消息传输》
    《响应式Web设计:HTML5和CSS3实战》 读书笔记
    HTML5新功能之二 《Geolocation获取地理位置》
  • 原文地址:https://www.cnblogs.com/ZH-comld/p/9607257.html
Copyright © 2011-2022 走看看