zoukankan      html  css  js  c++  java
  • [大山中学模拟赛] 2016.9.10

    liao出了一场比赛 简直想吃狗屎 说好的noip难度?我操

    因为有四道题 我简单的提一下就好了

    [HNOI2007]梦幻岛宝珠

    这一题liao也没说怎么想到这种dp的 吃屎吧

    F[i][j]表示2^i*j的重量 那么的话 F[30][1000]就够了

    然后对于重量为2^i 我们就对当前F[i][j]做一次采药

    然后对于怎么转到下一个状态 ,只用考虑两种情况

    1.限制重量转成二进制对于当前位i来说是0的 但是j对于当前位是1的 显然要进位 则F[i+1][j>>2+1]=max(F[i][j]);

    2.其他的情况 F[i+1][j>>2]=max(F[i][j]);

    每一次都保证以后合法 然后F[31][0]就是答案

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<algorithm>
    #include<cstdlib>
    #include<cmath>
    #define Maxn 110
    using namespace std;
    typedef long long LL;
    struct node
    {
      LL a,b,val;
      node(){a=b=val=0;}
    }A[Maxn];
    bool Cmp(const node &x,const node &y){return x.b<y.b;}
    LL N,W; LL F[32][Maxn*10]; LL bit[Maxn];
    int main()
    {
      while(scanf("%lld%lld",&N,&W)!=EOF)
      {
        if(N==-1&&W==-1) break;
        for(LL i=1;i<=100;i++) A[i].a=A[i].b=A[i].val=0;
        for(LL i=1;i<=N;i++)
        {
          scanf("%lld%lld",&A[i].a,&A[i].val);
          while(A[i].a%2==0) A[i].a/=2,A[i].b++;
        }
        sort(A+1,A+N+1,Cmp); LL l=1,r; memset(F,0,sizeof(F));
        bit[0]=1; for(LL i=1;i<=30;i++) bit[i]=bit[i-1]*2;
        for(LL i=0;i<=30;i++)
        {
          if(A[l].b==i&&l<=N)
          {
            r=l; while(A[r+1].b==A[r].b&&r<=N) r++;
            for(LL j=l;j<=r;j++)
              for(LL k=1000;k>=A[j].a;k--)
                F[i][k]=max(F[i][k],F[i][k-A[j].a]+A[j].val);
             
            l=r+1;
          }
          for(LL j=1000;j>=0;j--)
          {
            if(((j&1)==1)&&((W&bit[i])==0)) F[i+1][(j>>1)+1]=max(F[i+1][(j>>1)+1],F[i][j]);
            else F[i+1][(j>>1)]=max(F[i+1][(j>>1)],F[i][j]);
          }
        }
        printf("%lld
    ",F[31][0]);
      }
      return 0;
    }
    View Code
    跳蚤

    第二题简直恶心 因为是子串 所以我们草了个后缀数组之后 在字典序的表上 二分字典序二分长度 得出子串,,以当前sa[i]开始的子串

    然后我们可以用height数组 得出前缀 那么想想 后面的不合法要切开的 就是与sa[i]公共前缀和公共前缀+1要切开

    当然公共前缀还要看子串的长度 如果子串比较小 公共前缀比较大也不合法 也要切

    然后就变成了很多条线段 要找出K-1个点断开问可不可以

    把左端点从左到右排序 当前下一条线段的右端点小于我当前的右端点 那么min一下右端点 就是在下一条的右端点切开

    如果当前的左端点小于我的右端点 那么切不了 只好重新切一个

    每一次切都按照最小的右端点切 这样肯定最优 这个只可意会不可言传

    然后没了 时间复杂度我也不知道多少不会算反正能过

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<algorithm>
    #include<cstdlib>
    #include<cmath>
    #define Maxn 100010
    using namespace std;
    int K; char st[Maxn]; int A[Maxn]; int N;
    int fir[Maxn],sec[Maxn],rank[Maxn],height[Maxn],R[Maxn],sa[Maxn];
    void Rsort(int *use,int *in,int *out,int size,int num)
    {
      for(int i=0;i<=size;i++) R[i]=0;
      for(int i=1;i<=num;i++) R[use[in[i]]]++;
      for(int i=1;i<=size;i++) R[i]+=R[i-1];
      for(int i=num;i>=1;i--) out[R[use[in[i]]]--]=in[i];
    }
    void SA()
    {
      for(int i=1;i<=N;i++) rank[i]=i;
      Rsort(A,rank,sa,200,N);
      rank[sa[1]]=1; for(int i=2;i<=N;i++) rank[sa[i]]=rank[sa[i-1]]+(A[sa[i-1]]!=A[sa[i]]);
      int p=0; int cnt=1;
      while(cnt<N)
      {
        for(int i=1;i<=N;i++)
        {
          fir[i]=rank[i];
          sec[i]=(i+(1<<p))>N?0:rank[(i+(1<<p))];
          sa[i]=i;
        }
        Rsort(sec,sa,rank,N,N);
        Rsort(fir,rank,sa,N,N);
        rank[sa[1]]=1; for(int i=2;i<=N;i++) rank[sa[i]]=rank[sa[i-1]]+(fir[sa[i]]!=fir[sa[i-1]]||sec[sa[i]]!=sec[sa[i-1]]);
        p++; cnt=rank[sa[N]];
      }
    }
    void Height()
    {
      int k=0;
      for(int i=1;i<=N;i++)
      {
        if(k>0) k--;
        if(rank[i]!=1)
        {
          int pos=sa[rank[i]-1];
          while(A[pos+k]==A[i+k]) k++;
        }
        height[rank[i]]=k;
      }
       
    }
     
    int fur[Maxn];
    int main()
    {
      scanf("%d",&K);
      scanf("%s",st+1); N=strlen(st+1);
      for(int i=1;i<=N;i++) A[i]=st[i]-'a';
      SA();
      Height();
       
      int L=1; int R=N; pair<int,int>ans;
      while(L<=R)
      {
        int mid=(L+R)>>1;
        int l=1; int r=N-sa[mid]+1; int ret=-1;
        while(l<=r)
        {
          int m=(l+r)>>1;
          for(int i=1;i<=N;i++) fur[i]=N+1;
          int p=mid; int x,y,h; h=m;
          while(p<=N)
          {
            x=sa[p]; y=sa[p]+h;
            fur[x]=min(fur[x],y);
            p++; h=min(h,height[p]);
          }
           
          int cnt=0; int minr=0;
          for(int i=1;i<=N;i++)
          {
            if(fur[i]!=N+1)
            {
              if(fur[i]==i){cnt=K+1; break;}
              if(minr<=i) minr=fur[i],cnt++;
              else minr=min(minr,fur[i]);
            }
          }
          if(cnt<K){ret=m; r=m-1;}
          else l=m+1;
        }
        if(ret==-1) L=mid+1;
        else{R=mid-1; ans=make_pair(mid,ret);}
      }
      for(int i=sa[ans.first];i<sa[ans.first]+ans.second;i++) printf("%c",st[i]);
      printf("
    ");
      return 0;
    }
    /*
    2
    ababa
    ba
    30%  k<=5,len<=30
    100%  k<=100000,len<=100000
    */
    View Code
    排队计划

    这道题其实应该想到的 逆序对就线段树或者树状数组咯 然后发现每次改变pos这个位置的时候前面的逆序对个数不会影响 后面大于A[pos]这些位置的逆序对个数也不会影响 直接每次找出小于A[pos]的位置 然后改成INT_MAX 减掉答案就好了 我所说的逆序对个数是向右统计的

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<algorithm>
    #include<cstdlib>
    #include<climits>
    #include<vector>
    #define Maxn 500010
    using namespace std;
    typedef long long LL;
    const LL inf=1e9;
    LL N,M; LL A[Maxn];
    LL tot=0; LL root; LL lc[Maxn*4],rc[Maxn*4],C[Maxn*4],Minx[Maxn*4];
     
    LL tr[Maxn];
    LL low_bit(LL x){return x&(-x);}
    void Add(LL x)
    {
      while(x<=tot)
      {
        tr[x]++;
        x+=low_bit(x);
      }
    }
    LL Q(LL x)
    {
      LL ans=0;
      while(x>=1)
      {
        ans+=tr[x];
        x-=low_bit(x);
      }
      return ans;
    }
     
    void Link1(LL &u,LL L,LL R,LL k)
    {
      if(!u) u=++tot;
      if(L==R){Minx[u]=L; return ;}
      LL mid=(L+R)>>1;
      if(k<=mid) Link1(lc[u],L,mid,k);
      else Link1(rc[u],mid+1,R,k);
      if(!lc[u]) Minx[u]=Minx[rc[u]];
      else if(!rc[u]) Minx[u]=Minx[lc[u]];
      else
      {
        if(A[Minx[lc[u]]]>A[Minx[rc[u]]]) Minx[u]=Minx[rc[u]];
        else Minx[u]=Minx[lc[u]];
      }
    }
    void Clear()
    {
      memset(C,0,sizeof(C));
      memset(lc,0,sizeof(lc));
      memset(rc,0,sizeof(rc));
      memset(Minx,0,sizeof(Minx));
      root=tot=0;
    }
     
    LL ans[Maxn];
    void Change(LL u,LL L,LL R,LL k)
    {
      if(L==R){Minx[u]=L; return ;}
      LL mid=(L+R)>>1;
      if(k<=mid) Change(lc[u],L,mid,k);
      else Change(rc[u],mid+1,R,k);
      if(A[Minx[lc[u]]]>A[Minx[rc[u]]]) Minx[u]=Minx[rc[u]];
      else Minx[u]=Minx[lc[u]];
    }
    LL Query1(LL u,LL L,LL R,LL l,LL r)
    {
      if(L>R) return 0;
      if(L==l&&R==r) return Minx[u];
      LL mid=(L+R)>>1;
      if(r<=mid) return Query1(lc[u],L,mid,l,r);
      else if(l>mid) return Query1(rc[u],mid+1,R,l,r);
      else
      {
        LL k1=Query1(lc[u],L,mid,l,mid);
        LL k2=Query1(rc[u],mid+1,R,mid+1,r);
        return A[k1]>A[k2]?k2:k1;
      }
    }
    vector<LL>V;
    LL mp(LL x)
    {
      LL L=0; LL R=tot-1;
      while(L<=R)
      {
        LL mid=(L+R)>>1;
        if(x==V[mid]) return mid;
        else if(x<V[mid]) R=mid-1;
        else L=mid+1;
      }
    }
    int main()
    {
      scanf("%lld%lld",&N,&M);
      for(LL i=1;i<=N;i++) scanf("%lld",&A[i]),V.push_back(A[i]);
      sort(V.begin(),V.end()); tot=unique(V.begin(),V.end())-(V.begin());
      for(LL i=N;i>=1;i--)
      {
        ans[i]=Q(mp(A[i]));
        Add(mp(A[i])+1);
      }
      LL sum=0;
      for(LL i=1;i<=N;i++) sum+=ans[i];
      printf("%lld
    ",sum);
       
      Clear();
      for(LL i=1;i<=N;i++) Link1(root,1,N,i);
      for(LL i=1;i<=M;i++)
      {
        LL x; scanf("%lld",&x); LL pos=A[x];
        LL minx; while(A[minx=Query1(root,1,N,x,N)]<=pos&&pos!=INT_MAX)
        {
          A[minx]=INT_MAX;
          Change(root,1,N,minx);
          sum-=ans[minx];
        }
        printf("%lld
    ",sum);
      }
      return 0;
    }
    /*
    6 2
    160 163 164 161 167 160
    2
    3
    */
    View Code
    [Zjoi2016]小星星

    liao有病出了道容斥原理 我只知道这么一回事但没做过题

    怎么说好呢 树上的每一个点都可以映射原图上的每个点  只要判断边可不可以到就好

    那么的话一共十七个点 我们想 不合法的情况肯定会重复映射 那么的话就是扣掉每一个点的并集 也就是容斥原理 那么用 不用扣点的再跑一次减去所有不合法的并集即可

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<algorithm>
    #include<cstdlib>
    #include<cmath>
    #include<vector>
    #define Maxn 20
    using namespace std;
    typedef long long LL;
    struct node
    {
      LL x,y,next;
    }edge[Maxn*2]; LL len,first[Maxn];
    void ins(LL x,LL y){len++; edge[len].x=x; edge[len].y=y; edge[len].next=first[x]; first[x]=len;}
    LL N,M; bool v[Maxn][Maxn]; vector<LL>V; LL F[Maxn][Maxn];
    void Dfs(LL x,LL fa)
    {
      for(LL k=first[x];k!=-1;k=edge[k].next)
      {
        LL y=edge[k].y; LL s=0;
        if(y!=fa) Dfs(y,x);
      }
      for(LL i=0;i<V.size();i++)
      {
        F[x][i]=1;
        for(LL k=first[x];k!=-1;k=edge[k].next)
        {
          LL y=edge[k].y;
          if(y!=fa)
          {
            LL sum=0;
            for(LL j=0;j<V.size();j++) if(v[V[i]][V[j]]) sum+=F[y][j];
            F[x][i]*=sum;
          }
        }
      }
    }
    int main()
    {
      scanf("%lld%lld",&N,&M); memset(v,0,sizeof(v));
      for(LL i=1;i<=M;i++){LL x,y; scanf("%lld%lld",&x,&y); v[x][y]=v[y][x]=1;}
      len=0; memset(first,-1,sizeof(first));
      for(LL i=1;i<N;i++){LL x,y; scanf("%lld%lld",&x,&y); ins(x,y); ins(y,x);} LL ans=0;
      for(LL i=0;i<(1<<N);i++)
      {
        V.clear();
        for(LL j=1;j<=N;j++) if(!(i&(1<<(j-1)))) V.push_back(j);
        memset(F,0,sizeof(F)); Dfs(1,0);
        LL s=0; for(LL j=0;j<V.size();j++) s+=F[1][j];
        if(!((N-V.size())%2)) ans+=s;
        else ans-=s;
      }
      return printf("%lld
    ",ans),0;
    }
    
    View Code
  • 相关阅读:
    js 带表情的评论输入框问题
    js 元素到指定的相对定位的父元素的距离
    html分享QQ,微信,显示分享图片,标题,简介
    网络空间安全0x01志向
    优秀的前端
    float探究
    转载的。。。
    判断有木有环
    居中(纯css方式)
    一个闭包的很好的考题, 闭包+递归
  • 原文地址:https://www.cnblogs.com/wohenshuai/p/5866107.html
Copyright © 2011-2022 走看看