[NOIP2017集训日记 2017.9-11]

前言:

这次NOIP好像考不好就退役?

所以写点日记记录生活 好过什么都没留下 给以后做点怀念

 

2017.9.5

 

This Problem Is Too Simple!

这个超好的题

只要你想到离散化 O(N2logN) 草过是没有什么毛病的

可是你要想到精益求精 所以这样做不好

其实有O(Nlog N)做法

对于每一种颜色的 影响的是以这个点为端点的一条链 然后对于询问x y可以差分出四条链

我们关键是维护一条链怎么办 发现如果这个点+1了 子树内的肯定也+1

那么就是维护一个树状数组 这个点打一个+1标记 这个子树外的第一个点打一个-1标记 然后用树状数组维护前缀和即可

好像常数还没写O(N2logN)好...

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;

const int Maxn=500010;

struct EDGE{int x,y,next;}edge[Maxn]; int len,first[Maxn];
void ins(int x,int y){len++; edge[len].x=x; edge[len].y=y; edge[len].next=first[x]; first[x]=len;}

struct node1
{
  int t,op,x,c;
  node1(){}
  node1(int _t,int _x,int _c,int _op){t=_t; x=_x; c=_c; op=_op;}
}Q1[Maxn*4]; int Q1len=0;
struct node2
{
  int t,x,y,c;
  node2(){}
  node2(int _t,int _x,int _y,int _c){t=_t; x=_x; y=_y; c=_c;}
}Q2[Maxn*4]; int Q2len=0;

int L[Maxn],R[Maxn],id=0; int fa[Maxn][21],dep[Maxn];

void Dfs(int x,int f)
{
  L[x]=++id;
  for(int k=first[x];k!=-1;k=edge[k].next)
  {
    int y=edge[k].y; if(y==f) continue;
    fa[y][0]=x; dep[y]=dep[x]+1; Dfs(y,x);
  }R[x]=id;
}

bool Cmp1(const node1 &x,const node1 &y)
{
  if(x.c!=y.c) return x.c<y.c;
  return x.t<y.t;
}

bool Cmp2(const node2 &x,const node2 &y)
{
  if(x.c!=y.c) return x.c<y.c;
  return x.t<y.t;
}

bool Cmp3(const node2 &x,const node2 &y){return x.t<y.t;}

int N,q; int preC[Maxn],C[Maxn],Clen=0; int ans[Maxn];

int tr[Maxn]; int low_bit(int x){return x&(-x);}
void Add(int x,int c){while(x<=N){tr[x]+=c; x+=low_bit(x);}}
int Query(int x){int ans=0; while(x>=1){ans+=tr[x]; x-=low_bit(x);} return ans;}
void Del(int x){while(x<=N){tr[x]=0; x+=low_bit(x);}}

int LCA(int x,int y)
{
  if(dep[x]<dep[y]) swap(x,y);
  int deep=dep[x]-dep[y];
  for(int i=20;i>=0;i--) if(deep>=(1<<i)) deep-=(1<<i),x=fa[x][i];
  if(x==y) return x;
  for(int i=20;i>=0;i--) if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
  return fa[x][0];
}

int main()
{
  
  scanf("%d%d",&N,&q);
  
  for(int i=1;i<=N;i++) scanf("%d",&preC[i]),C[++Clen]=preC[i];
  for(int i=1;i<=N;i++) Q1[++Q1len]=node1(0,i,preC[i],1);
  
  len=0; memset(first,-1,sizeof(first));
  for(int i=1;i<N;i++){int x,y; scanf("%d%d",&x,&y); ins(x,y); ins(y,x);}
  id=0; dep[1]=1; Dfs(1,0);
  
  for(int j=1;j<=20;j++) for(int i=1;i<=N;i++) fa[i][j]=fa[fa[i][j-1]][j-1];
  
  for(int i=1;i<=q;i++)
  {
    char ch; scanf("\n%c",&ch);
    if(ch=='C')
    {
      int x,c; scanf("%d%d",&x,&c); C[++Clen]=c;
      if(c!=preC[x])
      {
        Q1[++Q1len]=node1(i,x,preC[x],-1);
        Q1[++Q1len]=node1(i,x,c,1); preC[x]=c;
      }
    }
    else
    {
      int x,y,c; scanf("%d%d%d",&x,&y,&c);
      Q2[++Q2len]=node2(i,x,y,c); C[++Clen]=c;
    }
  }
  
  sort(Q1+1,Q1+Q1len+1,Cmp1);
  sort(Q2+1,Q2+Q2len+1,Cmp2);
  sort(C+1,C+Clen+1);
  
  Clen=unique(C+1,C+Clen+1)-(C+1);
  int l1=1,l2=1,r1=0,r2=0;
  for(int i=1;i<=Clen;i++)
  {
    int x=C[i];
    while(Q1[r1+1].c==C[i] && r1<=Q1len) r1++;
    while(Q2[r2+1].c==C[i] && r2<=Q2len) r2++;
    
    int last=l1;
    while(l1<=r1 && l2<=r2)
    {
      if(Q1[l1].t<Q2[l2].t)
      {
        Add(R[Q1[l1].x]+1,(-1)*Q1[l1].op); Add(L[Q1[l1].x],1*Q1[l1].op);
        l1++;
      }
      else
      {
        int r=LCA(Q2[l2].x,Q2[l2].y);
        ans[Q2[l2].t]=Query(L[Q2[l2].x])+Query(L[Q2[l2].y])-Query(L[r])-Query(L[fa[r][0]]);
        l2++;
      }
    }
    
    while(l1<=r1) l1++;
    while(l2<=r2)
    {
      int r=LCA(Q2[l2].x,Q2[l2].y);
      ans[Q2[l2].t]=Query(L[Q2[l2].x])+Query(L[Q2[l2].y])-Query(L[r])-Query(L[fa[r][0]]);
      l2++;
    }
    
    while(last<=r1) Del(R[Q1[last].x]+1),Del(L[Q1[last].x]),last++;
  }
  
  sort(Q2+1,Q2+Q2len+1,Cmp3);
  for(int i=1;i<=Q2len;i++) printf("%d\n",ans[Q2[i].t]);
  return 0;
}
View Code

 

下午就乱搞了一下 写了一个拉格朗日插值

 

[HNOI2004]打鼹鼠

其实两个点只要相离的距离和<=时间差的话就可以到 那么就是求一条最长链

按时间排序DP好了

#include <bits/stdc++.h>
using namespace std;
const int Maxn=10010;
struct node{int t,x,y;node(){}node(int _t,int _x,int _y){t=_t; x=_x; y=_y;}}Q[Maxn]; int Qlen;
bool Cmp(const node &x,const node &y){return x.t<y.t;}
int F[Maxn]; int N,M;
int main()
{
  scanf("%d%d",&N,&M); Qlen=0;
  for(int i=1;i<=M;i++){int t,x,y; scanf("%d%d%d",&t,&x,&y); Q[++Qlen]=node(t,x,y);}
  sort(Q+1,Q+Qlen+1,Cmp);
  for(int i=1;i<=M;i++) F[i]=1;
  for(int i=M;i>=1;i--)
  {
    for(int j=i+1;j<=M;j++)
    {
      int d=abs(Q[i].x-Q[j].x)+abs(Q[i].y-Q[j].y);
      if(d<=Q[j].t-Q[i].t) F[i]=max(F[i],F[j]+1);
    }
  }
  int ans=0; for(int i=1;i<=N;i++) ans=max(ans,F[i]);
  return printf("%d\n",ans),0;
}
View Code

 

2017.9.6

 

[Snoi2017]礼物

矩阵乘法

$F[i]=\Sigma_{1}^{i-1}F[i-1]+i^K$

然后维护个前缀和 就可以矩阵乘法了

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int Maxn=15;
const int Mod=1000000007;
struct Matrix
{
  LL A[Maxn+3][Maxn+3];
  Matrix(){memset(A,0,sizeof(A));}
  friend Matrix operator *(Matrix x,Matrix y)
  {
    Matrix z;
    for(int k=0;k<=15;k++)
      for(int i=0;i<=15;i++)
        for(int j=0;j<=15;j++)
          z.A[i][j]=(z.A[i][j]+x.A[i][k]*y.A[k][j]%Mod)%Mod;
    return z;
  }
  friend Matrix operator ^(Matrix x,LL k)
  {
    Matrix z; for(int i=0;i<=15;i++) z.A[i][i]=1LL;
    while(k)
    {
      if(k&1) z=z*x;
      x=x*x; k>>=1;
    }
    return z;
  }
}P,B;
int C[Maxn][Maxn],K; LL N;
int main()
{
  scanf("%lld%d",&N,&K);  if(N==1){printf("1\n"); return 0;}
  C[0][0]=C[1][1]=C[1][0]=1; for(int i=1;i<=K;i++) C[i][0]=1;
  for(int i=2;i<=K;i++) for(int j=1;j<=i;j++) C[i][j]=C[i-1][j-1]+C[i-1][j];
   
  for(int i=0;i<=K+2;i++) P.A[0][i]=1LL;
   
  for(int i=0;i<=K;i++)
  {
    for(int j=0;j<=i;j++) B.A[j][i]=C[i][j];
  }
   
  for(int j=0;j<=K;j++) B.A[j][K+2]=B.A[j][K+1]=C[K][j];
  B.A[K+2][K+1]=1; B.A[K+2][K+2]=2;
   
  return printf("%lld\n",(P*(B^(N-1))).A[0][K+1]),0;
}
View Code

 

 

[Snoi2017]一个简单的询问

其实就是莫队搞一下 有点搞笑

比如说一个数据L1 R1 L2 R2

那么你就可以拆成[1,l1) [1,r1] [1,r2) [1,r2]

然后乘一下就可以了

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdlib>
#include <cmath>
 
using namespace std;
 
typedef long long LL;
 
const int Maxn=500010;
 
struct node
{
  int l,r,id,op;
  node(){}
  node(int _l,int _r,int _id,int _op){l=_l; r=_r; id=_id; op=_op;}
}Q[Maxn]; int Qlen=0; int A[Maxn],N,q; LL ans[Maxn];
 
int Block[Maxn],k,C1[Maxn],C2[Maxn];
 
bool Cmp(const node &x,const node &y)
{
  if(Block[x.l]!=Block[y.l]) return x.l<y.l;
  return x.r<y.r;
}
 
int main()
{
   
  scanf("%d",&N);
  for(int i=1;i<=N;i++) scanf("%d",&A[i]);
  scanf("%d",&q); Qlen=0;
  for(int i=1;i<=q;i++)
  {
    int l1,l2,r1,r2; scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
    Q[++Qlen]=node(r1,r2,i,1);
    Q[++Qlen]=node(r1,l2-1,i,-1);
    Q[++Qlen]=node(l1-1,r2,i,-1);
    Q[++Qlen]=node(l1-1,l2-1,i,1);
  }
   
  k=(int)(ceil(sqrt(N)));
  for(int i=1;i<=N;i++) Block[i]=(i-1)/k;
  for(int i=1;i<=Qlen;i++) if(Q[i].l>Q[i].r) swap(Q[i].l,Q[i].r);
   
  sort(Q+1,Q+Qlen+1,Cmp);
   
  int l=0; int r=0; LL s=0;
  for(int i=1;i<=Qlen;i++)
  {
    while(l<Q[i].l){l++; C1[A[l]]++; s+=C2[A[l]];}
    while(r<Q[i].r){r++; C2[A[r]]++; s+=C1[A[r]];}
     
    while(l>Q[i].l){C1[A[l]]--; s-=C2[A[l]]; l--;}
    while(r>Q[i].r){C2[A[r]]--; s-=C1[A[r]]; r--;}
    ans[Q[i].id]+=Q[i].op*s;
  }
  for(int i=1;i<=q;i++) printf("%lld\n",ans[i]);
  return 0;
}
View Code

 

posted @ 2017-09-05 19:59  wohenshuai  阅读(234)  评论(0编辑  收藏  举报