[大山中学模拟赛] 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
原文地址:https://www.cnblogs.com/wohenshuai/p/5866107.html