[暑假集训 2017.7-8]

2017.7.5

「美团 CodeM 初赛 Round B」黑白树

好妙啊这一题 一开始感觉就是贪心 又想用数据结构搞一搞 发现不是很对 然后再往贪心里面想

然后看了别人代码 才知道怎么做

首先一个最底下的点一定要按

那么我就有一段缓冲区可以选择按和不按 那么我们肯定选往上拉最长的按

我们只需要知道这么多就可以完成这题代码了

G[x]表示缓冲区还有多长 0的话就选缓冲区的点来按(包括端点)

F[x]表示到当前点最长还能拉多长上去

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>

using namespace std;
const int Maxn=100010;

int N,fa[Maxn],a[Maxn]; int d[Maxn];
queue<int>Q;

int F[Maxn],G[Maxn];

int main()
{
  memset(d,0,sizeof(d));
  scanf("%d",&N); for(int i=2;i<=N;i++) scanf("%d",&fa[i]),d[fa[i]]++;
  for(int i=1;i<=N;i++) scanf("%d",&a[i]);
  
  while(!Q.empty()) Q.pop();
  for(int i=1;i<=N;i++) if(d[i]==0) Q.push(i); memset(G,0,sizeof(G)); int ans=0;
  while(!Q.empty())
  {
    int x=Q.front();
    F[x]=max(F[x],a[x]);
    if(!G[x]){ans++; G[x]=max(G[x],F[x]);}
    d[fa[x]]--; G[fa[x]]=max(G[fa[x]],G[x]-1); F[fa[x]]=max(F[fa[x]],F[x]-1);
    if(d[fa[x]]==0) Q.push(fa[x]);
    Q.pop();
  }
  return printf("%d\n",ans),0;
}
View Code

 

 

「美团 CodeM 初赛 Round B」送外卖2

这题也质量很高 我也没在当时想出来 一开始以为每次只能带一个任务 感觉很无奈

关键是我们要想想当前点可以到达下一个点 而又避免用时间去做数组 或者去搞一些奇奇怪怪的东西 反正不能在时间上面去搞这道题

那么我们就考虑用数组记录到每个状态需要的最短时间 但是我们又要包含所有状态

要想到F[i][sta]表示到i这个点 任务的状态 状态用三进制表示 0为没有打算做这个任务 1表示携带着这个任务 2表示已经完成了这个任务

然后分别进行层内转移和层外转移即可

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

const int Maxp=1030;
const int Maxn=30;

int F[Maxn][60000]; int dis[Maxn][Maxn];

int bit[Maxp]; int N,M,Q;

struct R
{
  int s,t,l,r;
}q[Maxn];

int main()
{
  
  scanf("%d%d%d",&N,&M,&Q);
  memset(dis,63,sizeof(dis));
  for(int i=1;i<=M;i++)
  {
    int u,v,c; scanf("%d%d%d",&u,&v,&c);
    dis[u][v]=min(dis[u][v],c);
  }
  
  for(int i=1;i<=Q;i++) scanf("%d%d%d%d",&q[i].s,&q[i].t,&q[i].l,&q[i].r);
  
  for(int i=1;i<=N;i++) dis[i][i]=0;
  for(int k=1;k<=N;k++) for(int i=1;i<=N;i++) for(int j=1;j<=N;j++) dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
  
  bit[1]=1; for(int i=2;i<=Q+1;i++) bit[i]=bit[i-1]*3;
  
  memset(F,63,sizeof(F)); F[1][0]=0;
  for(int p=0;p<bit[Q+1];p++)
  {
    for(int i=1;i<=N;i++)
    {
      //层内转移
      for(int ed=1;ed<=N;ed++) F[ed][p]=min(F[ed][p],F[i][p]+dis[i][ed]);
      
      //层外转移
      for(int j=1;j<=Q;j++)
      {
        int sta=(p%bit[j+1])/bit[j];
        if(sta==2) continue;
        else if(sta==0)
        {
          if(F[i][p]+dis[i][q[j].s]<=q[j].r)
            F[q[j].s][p+bit[j]]=min(F[q[j].s][p+bit[j]],max(q[j].l,F[i][p]+dis[i][q[j].s])); 
        }
        else if(sta==1)
        {
          if(F[i][p]+dis[i][q[j].t]<=q[j].r)
            F[q[j].t][p+bit[j]]=min(F[q[j].t][p+bit[j]],F[i][p]+dis[i][q[j].t]);
        }
      }
    }
  }
  
  int ans=0;
  for(int i=1;i<=N;i++) for(int p=0;p<bit[Q+1];p++)
  {
    if(F[i][p]<99999999)
    {
      int s=0;
      for(int j=1;j<=Q;j++)
      {
        int sta=(p%bit[j+1])/bit[j];
        if(sta==2) s++; 
      }
      ans=max(ans,s);
    }
  }
  return printf("%d\n",ans),0;
}
View Code

 

2017.7.6

 

决战

 花了昨天下午和今天上午去写和调了这题

好像是经典题 GDSOI 2017的第四题跟这题有点像 然后就写了

主要就是两棵LCT 一个维护形态 一个维护权值 分成两棵树主要是因为形态树不能进行值翻转

具体就是:

一个形态树的splay对应权值树的splay 个数相同 而且形态树的splay的指针按照两棵树的中序遍历指向权值树的splay的指针

然后只用记录每个splay的根的指针 因为只需通过根的指针再通过中序遍历的性质就可以找到别人的指针

大概是这样 只是调的时候恶心点 就没了 其它看代码

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <stack>
#include <climits>
using namespace std;
typedef long long LL;
const LL Maxn=50010;
stack<LL>S; LL p[Maxn],rt;
struct EDGE{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;}
struct VAL
{
  LL fa[Maxn],siz[Maxn],son[Maxn][2],lazy[Maxn],Maxx[Maxn],Minx[Maxn],sum[Maxn],C[Maxn]; bool rev[Maxn];
  void init(LL u){fa[u]=0; son[u][0]=son[u][1]=0; lazy[u]=rev[u]=0; Maxx[u]=Minx[u]=0; sum[u]=siz[u]=0; C[u]=0;}
  bool Is_root(LL u){if( (son[fa[u]][0]!=u) && (son[fa[u]][1]!=u) ) return 1; return 0;}
  void update(LL u)
  {
    siz[u]=siz[son[u][0]]+siz[son[u][1]]+1;
    Maxx[u]=max(max(Maxx[son[u][0]],Maxx[son[u][1]]),C[u]);
    Minx[u]=min(min(Minx[son[u][0]],Minx[son[u][1]]),C[u]);
    sum[u]=sum[son[u][0]]+sum[son[u][1]]+C[u];
  }
  void Rotate(LL u)
  {
    LL y,z,g,dir;
    y=fa[u]; z=fa[y]; dir=son[y][1]==u; g=son[u][dir^1];
    if(!Is_root(y)) son[z][son[z][1]==y]=u; fa[u]=z;
    fa[y]=u; son[u][dir^1]=y;
    son[y][dir]=g; if(g) fa[g]=y;
    update(y);
  }
  void Push_down(LL u)
  {
    if(rev[u])
    {
      swap(son[u][0],son[u][1]);
      rev[son[u][0]]^=rev[u]; rev[son[u][1]]^=rev[u];
      rev[u]=0;
    }
    if(lazy[u])
    {
      if(son[u][0])
      {
        C[son[u][0]]+=lazy[u]; Maxx[son[u][0]]+=lazy[u];
        Minx[son[u][0]]+=lazy[u]; sum[son[u][0]]+=siz[son[u][0]]*lazy[u];
        lazy[son[u][0]]+=lazy[u];
      }
      
      if(son[u][1])
      {
        C[son[u][1]]+=lazy[u]; Maxx[son[u][1]]+=lazy[u];
        Minx[son[u][1]]+=lazy[u]; sum[son[u][1]]+=siz[son[u][1]]*lazy[u];
        lazy[son[u][1]]+=lazy[u];
      }
      lazy[u]=0;
    }
  }
  void Preserve(LL u)
  {
    while(!Is_root(u)) S.push(u),u=fa[u]; S.push(u);
    while(!S.empty()) Push_down(S.top()),S.pop();
  }
  void Splay(LL u)
  {
    Preserve(u);
    while(!Is_root(u))
    {
      LL y=fa[u];
      if(Is_root(y)) Rotate(u);
      else
      {
        LL z=fa[y];
        if((son[y][1]==u)==(son[z][1]==y)){Rotate(y); Rotate(u);}
        else Rotate(u),Rotate(u);
      }
    }
    update(u);
  }
  LL Find_rank(LL u,LL k)
  {
    Push_down(u);
    if((siz[son[u][0]]+1)==k) return u;
    else if(siz[son[u][0]]>=k) return Find_rank(son[u][0],k);
    else return Find_rank(son[u][1],k-siz[son[u][0]]-1);
  }
  
}val;
struct LCT
{
  LL son[Maxn][2],fa[Maxn],siz[Maxn]; bool rev[Maxn];
  void init(LL u){son[u][0]=son[u][1]=fa[u]=siz[u]=rev[u]=0;}
  bool Is_root(LL u){if( (son[fa[u]][0]!=u) && (son[fa[u]][1]!=u) ) return 1; return 0;}
  void update(LL u){siz[u]=siz[son[u][0]]+siz[son[u][1]]+1;}
  void Rotate(LL u)
  {
    LL y,z,g,dir;
    y=fa[u]; z=fa[y]; dir=son[y][1]==u; g=son[u][dir^1];
    if(!Is_root(y)) son[z][son[z][1]==y]=u; fa[u]=z;
    fa[y]=u; son[u][dir^1]=y;
    son[y][dir]=g; if(g) fa[g]=y;
    update(y);
  }
  void Push_down(LL u)
  {
    if(rev[u])
    {
      rev[son[u][0]]^=1; rev[son[u][1]]^=1;
      swap(son[u][0],son[u][1]);
      rev[u]=0;
    }
  }
  void Preserve(LL u)
  {
    while(!Is_root(u)) S.push(u),u=fa[u]; S.push(u);
    while(!S.empty()){Push_down(S.top()); S.pop();}
  }
  void Splay(LL u)
  {
    Preserve(u); rt=u;
    while(!Is_root(u))
    {
      LL y=fa[u];
      if(Is_root(y)) Rotate(u),rt=y;
      else
      {
        LL z=fa[y]; rt=z;
        if((son[y][1]==u)==(son[z][1]==y)){Rotate(y); Rotate(u);}
        else Rotate(u),Rotate(u);
      }
    }
    update(u);
  }
}lct;
void Splay(LL u)
{
  lct.Splay(u);
  LL k=val.Find_rank(p[rt],lct.siz[lct.son[u][0]]+1);
  val.Splay(k); p[u]=k;
}
void Access(LL u)
{
    LL last=0; LL lastv=0,o;
    while(u!=0)
    {
      Splay(u); o=p[u];
      
      p[lct.son[u][1]]=val.son[o][1];
      
      val.son[o][1]=lastv; val.update(o);
      if(lastv) val.fa[lastv]=o;
      lastv=o;
      
      lct.son[u][1]=last;
      lct.update(u);
      last=u;
      u=lct.fa[u];
    }
}

void Make_root(LL u){Access(u); Splay(u); lct.rev[u]^=1; val.rev[p[u]]^=1;}
void Split(LL x,LL y){Make_root(x); Access(y); Splay(y);}
LL N,M,r; char str[20];
void Dfs(LL x)
{
  for(LL k=first[x];k!=-1;k=edge[k].next)
  {
    LL y=edge[k].y;
    if(y==lct.fa[x]) continue ;
    lct.fa[y]=val.fa[y]=x;
    Dfs(y);
  }
}
int main()
{
  scanf("%lld%lld%lld",&N,&M,&r); len=0; memset(first,-1,sizeof(first));
  val.Minx[0]=LLONG_MAX; val.Maxx[0]=LLONG_MIN;
  for(LL i=1;i<=N;i++) lct.init(i),val.init(i);
  for(LL i=1;i<N;i++){LL x,y; scanf("%lld%lld",&x,&y); ins(x,y); ins(y,x);}
  for(LL i=1;i<=N;i++) p[i]=i; Dfs(r);
  for(LL i=1;i<=M;i++)
  {
    scanf("%s",str+1); LL x,y; scanf("%lld%lld",&x,&y);
    if(str[3]=='c')
    {
      LL w; scanf("%lld",&w); Split(x,y);
      val.lazy[p[y]]+=w; val.C[p[y]]+=w; val.Maxx[p[y]]+=w; val.Minx[p[y]]+=w; val.sum[p[y]]+=w*val.siz[p[y]];
    }
    else if(str[3]=='m') Split(x,y),printf("%lld\n",val.sum[p[y]]);
    else if(str[3]=='j') Split(x,y),printf("%lld\n",val.Maxx[p[y]]);
    else if(str[3]=='n') Split(x,y),printf("%lld\n",val.Minx[p[y]]);
    else if(str[3]=='v') Split(x,y),val.rev[p[y]]^=1;
  }
  return 0;
}
View Code

 

 

[Usaco2017 Open]Modern Art

我们可以找到每个数字的左上角和右下角 那么如果在这个数字内刷下的其他数字 那么其他数字就没可能是第一个

还要注意只有一个数字的时候 这个数字没可能是第一个刷下去的 因为别人没得刷 这个排列不合法

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <climits>
using namespace std;
const int Maxn=1010;
int N; int A[Maxn][Maxn];
struct node{int xm,xb,ym,yb;node(){xm=INT_MAX; ym=INT_MAX; xb=INT_MIN; yb=INT_MIN;}}num[Maxn*Maxn];
bool bo[Maxn*Maxn];
int main()
{
  freopen("a.in","r",stdin);
  freopen("a.out","w",stdout);
  scanf("%d",&N); bool bk=1; int last=0; 
  for(int i=1;i<=N;i++) for(int j=1;j<=N;j++)
  {
    scanf("%d",&A[i][j]);
    if(A[i][j])
    {
      if(!last) last=A[i][j];
      else if(last!=A[i][j]) bk=false;
      num[A[i][j]].xm=min(num[A[i][j]].xm,i);
      num[A[i][j]].xb=max(num[A[i][j]].xb,i);
      num[A[i][j]].ym=min(num[A[i][j]].ym,j);
      num[A[i][j]].yb=max(num[A[i][j]].yb,j);
    }
  }
  
  int ans=0; memset(bo,1,sizeof(bo));
  for(int n=1;n<=N*N;n++)
  {
    if(num[n].xm==INT_MAX) continue;
    else
    {
      for(int i=num[n].xm;i<=num[n].xb;i++)
        for(int j=num[n].ym;j<=num[n].yb;j++)
          if(A[i][j]!=n) bo[A[i][j]]=0;
    }
  }
  for(int n=1;n<=N*N;n++) ans+=bo[n];
  if(bk) ans--;
  
  return printf("%d\n",ans),0;
}
/*
4
2 2 3 0
2 7 3 7
2 7 7 7
0 0 0 0
*/
View Code

 

2017.7.7

工作评估

O(N^2)大好水题

#include<bits/stdc++.h>
using namespace std; const int Maxn=50010; int N,M; int D[Maxn],L[Maxn]; int main() {scanf("%d%d",&N,&M); for(int i=1;i<=N;i++) scanf("%d",&D[i]); for(int i=1;i<=N;i++) scanf("%d",&L[i]); for(int i=1;i<=M;i++) {int l,r,X0; scanf("%d%d%d",&l,&r,&X0); int last=X0; int maxx=X0; for(int j=l;j<=r;j++) {last+=D[j]; last=min(last,L[j]); if(last<X0) last=X0; maxx=max(maxx,last); }printf("%d\n",maxx); } return 0; }
View Code 

 

[Usaco2017 Open]Switch Grass

好恶心啊居然卡我时空

我们发现答案一定在最小生成树上的一条边

考虑每个点写个线段树 线段树叶子节点写个set 然后维护原树上的孩子到当前点的距离 线段树对颜色来开

然后就优化

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <set>
#include <climits>
#include <map>
#include <ctime>
#include <cmath>

using namespace std;

const int Maxn=200010;
inline int Read()
{
  char ch=getchar(); while(ch<'0' || ch>'9') ch=getchar();
  int p=0; while(ch>='0' && ch <='9') p=p*10+(ch-'0'),ch=getchar();
  return p;
}

struct EDGE{int x,y,next,d;}edge[Maxn*2],e[Maxn*2]; int len,first[Maxn];
void ins(int x,int y,int d){len++; edge[len].y=y; edge[len].d=d; edge[len].next=first[x]; first[x]=len;}
int fa[Maxn];
inline int Find(int x){return (x==fa[x])?fa[x]:fa[x]=Find(fa[x]);}
bool Cmp(const EDGE &x,const EDGE &y){return x.d<y.d;}

int N,M,K,Q; int Col[Maxn];

int lc[5500000],rc[5500000],C[5500000],rt[Maxn],tot;
multiset<int> dis[Maxn*2],ans;
int mp[5500000]; int tt=0;

void Link(int &u,int L,int R,int k,int c,int opt)
{
  if(!u) u=++tot;
  if(L==R)
  {
    int now=(mp[u]==0)?(mp[u]=++tt):mp[u];
    if(opt==1) dis[now].insert(c);
    else if(opt==-1) dis[now].erase(dis[now].find(c));
    if(dis[now].empty()) C[u]=0x3f3f3f3f; else C[u]=*dis[now].begin(); return ;
  }
  int mid=(L+R)>>1;
  if(k<=mid) Link(lc[u],L,mid,k,c,opt);
  else Link(rc[u],mid+1,R,k,c,opt);
  C[u]=min(C[lc[u]],C[rc[u]]);
}

int f[Maxn]; int pref[Maxn];
void Dfs(int x)
{
  for(int k=first[x];k!=-1;k=edge[k].next)
  {
    int y=edge[k].y;
    if(y!=f[x])
    {
      Link(rt[x],1,K,Col[y],edge[k].d,1);
      f[y]=x; pref[y]=edge[k].d;
      Dfs(y);
    }
  }
}

int Query(int u,int L,int R,int l,int r)
{
  if(!u) return 0x3f3f3f3f;
  if(l>r) return 0x3f3f3f3f;
  if(L==l&&R==r) return C[u];
  int mid=(L+R)>>1;
  if(r<=mid) return Query(lc[u],L,mid,l,r);
  else if(l>mid) return Query(rc[u],mid+1,R,l,r);
  else return min(Query(lc[u],L,mid,l,mid),Query(rc[u],mid+1,R,mid+1,r));
}    

int main()
{
  N=Read(); M=Read(); K=Read(); Q=Read(); len=0; memset(first,-1,sizeof(first));
  for(int i=1;i<=M;++i){e[i].x=Read(); e[i].y=Read(); e[i].d=Read();}
  sort(e+1,e+M+1,Cmp); for(int i=1;i<=N;i++) fa[i]=i; int s=0;
  for(int i=1;i<=M;++i)
  {
    int x=e[i].x; int y=e[i].y; int xx=Find(x); int yy=Find(y);
    if(xx!=yy)
    {
      ins(x,y,e[i].d),ins(y,x,e[i].d),fa[xx]=yy;
      s++; if(s==N-1) break;
    }
  }
  for(int i=1;i<=N;++i) Col[i]=Read();
  memset(C,63,sizeof(C)); Dfs(1);
  for(int i=1;i<=N;++i) ans.insert(min(Query(rt[i],1,K,1,Col[i]-1),Query(rt[i],1,K,Col[i]+1,K)));
  for(int i=1;i<=Q;++i)
  {
    int x,col; x=Read(); col=Read();
    ans.erase(ans.find(min(Query(rt[f[x]],1,K,1,Col[f[x]]-1),Query(rt[f[x]],1,K,Col[f[x]]+1,K))));
    ans.erase(ans.find(min(Query(rt[x],1,K,1,Col[x]-1),Query(rt[x],1,K,Col[x]+1,K))));
    if(f[x]) Link(rt[f[x]],1,K,Col[x],pref[x],-1);
    Col[x]=col;
    if(f[x]) Link(rt[f[x]],1,K,Col[x],pref[x],1);
    ans.insert(min(Query(rt[x],1,K,1,Col[x]-1),Query(rt[x],1,K,Col[x]+1,K)));
    ans.insert(min(Query(rt[f[x]],1,K,1,Col[f[x]]-1),Query(rt[f[x]],1,K,Col[f[x]]+1,K)));
    printf("%d\n",*ans.begin());
  }
  return 0;
}

/*
3 2 3 4
1 2 3
2 3 1
1 1 2
3 3
2 3
1 2
2 2
*/
View Code

 

2017.7.8

 

[CERC2016]Hangar Hurdles

被liao D飞了

很舒服的可以将每个格子标个数字然后求路径最小值最大

想到用最大生成树 然后在树上跑

由于各种问题代码重构了好久

liao的启发式合并完虐我的倍增

小的堆并到大的堆 然后不用记录路径 剩下的直接是一个树

每次小的翻倍 所以最多log层

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
#include <vector>
#include <climits>

using namespace std;
const int Maxn=1010;
struct node{int x,y,next,d;}edge[Maxn*Maxn*10]; int len,first[Maxn*Maxn];
void ins(int x,int y,int d){len++; edge[len].x=x; edge[len].y=y; edge[len].d=d; edge[len].next=first[x]; first[x]=len;}
bool Cmp(const node &x,const node &y){return x.d>y.d;}

int N; char str[Maxn][Maxn]; int b[Maxn][Maxn];

int dx[9]={-1,-1,0,1,1,1,0,-1};
int dy[9]={0,1,1,1,0,-1,-1,-1};

int ddx[5]={0,1}; int ddy[5]={1,0};

queue<pair<int,int> >Q; int dis[Maxn][Maxn];

int fa[Maxn*Maxn],siz[Maxn*Maxn]; int Find(int x){return (fa[x]==x)?fa[x]:Find(fa[x]);}

int pred[Maxn*Maxn]; int dep[Maxn*Maxn];

int LCA(int x,int y)
{
  if(dep[x]<dep[y]) swap(x,y);
  int deep=dep[x]-dep[y]; while(deep) x=fa[x],deep--;
  if(x==y) return x;
  while(fa[x]!=fa[y]) x=fa[x],y=fa[y];
  return fa[x];
}

void Dfs(int x)
{
  if(dep[x]) return ;
  if(x==fa[x]){dep[x]=1; return ;}
  else{Dfs(fa[x]); dep[x]=dep[fa[x]]+1; return ;}
}

int Count(int x,int f)
{
  int Minx=INT_MAX;
  while(x!=f) Minx=min(Minx,pred[x]),x=fa[x];
  return Minx;
}

int main()
{
  memset(dis,63,sizeof(dis));
  
  scanf("%d",&N); for(int i=1;i<=N;i++) for(int j=1;j<=N;j++)
  {
    scanf("\n%c",&str[i][j]);
    if(str[i][j]=='#') Q.push(make_pair(i,j)),dis[i][j]=0;
  }
  
  while(!Q.empty())
  {
    pair<int,int> x=Q.front();
    for(int k=0;k<8;k++)
    {
      pair<int,int> y=make_pair(x.first+dx[k],x.second+dy[k]);
      if(y.first>0 && y.first<=N && y.second>0 &&y.second<=N)
      {
        if(dis[y.first][y.second]>dis[x.first][x.second]+1)
        {
          dis[y.first][y.second]=dis[x.first][x.second]+1;
          Q.push(make_pair(y.first,y.second));
        }
      }
    }
    Q.pop();
  }
  
  for(int i=1;i<=N;i++) for(int j=1;j<=N;j++)
  {
    b[i][j]=min(dis[i][j],min(min(N-i+1,N-j+1),min(i,j)))*2-1;
    if(b[i][j]<0) b[i][j]=0;
  }
  
  len=0; memset(first,-1,sizeof(first));
  for(int i=1;i<=N;i++) for(int j=1;j<=N;j++) for(int k=0;k<2;k++)
  {
    int x=i+ddx[k]; int y=j+ddy[k];
    if(x>0 && y>0 && x<=N && y<=N) ins((i-1)*N+j,(x-1)*N+y,min(b[i][j],b[x][y]));
  }
  for(int i=1;i<=N*N;i++) fa[i]=i,siz[i]=1;
  sort(edge+1,edge+len+1,Cmp); int s=0; memset(pred,63,sizeof(pred));
  for(int i=1;i<=len;i++)
  {
    int xx=Find(edge[i].x); int yy=Find(edge[i].y);
    if(xx!=yy)
    {
      if(siz[xx]>siz[yy]) swap(xx,yy); fa[xx]=yy; pred[xx]=edge[i].d; siz[yy]+=siz[xx];
      s++; if(s==N*N-1) break;
    }
  }
  for(int i=1;i<=N*N;i++) Dfs(i);
  
  int q; scanf("%d",&q);
  while(q--)
  {
    int x1,y1,x2,y2; scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
    int u=(x1-1)*N+y1; int v=(x2-1)*N+y2;
    int f=LCA(u,v);
    printf("%d\n",min(Count(u,f),Count(v,f)));
  }
  return 0;
}
View Code

 

 

[CERC2016]Convex Contour

好搞笑刚刚学完必修四和差角公式 就要运用到计算机上了?

#include <bits/stdc++.h>
using namespace std;
const double pi=acos(-1);
int N; char str[30]; double ans;
void Count(char st)
{
  if(st=='T') ans+=1.0;
  if(st=='S') ans+=1.0;
  if(st=='C') ans+=pi/2.0;
}
double Dis(double x,double y){return sqrt(x*x+y*y);}
int main()
{
  scanf("%d",&N); scanf("%s",str+1);
  int l=N+1,r=0; for(int i=1;i<=N;i++) if(str[i]!='T'){l=i; break;} for(int i=N;i>=1;i--) if(str[i]!='T'){r=i; break;}
  ans=N-0.5*((str[1]=='C')+(str[N]=='C')); Count(str[1]); Count(str[N]);
  if(l!=1 && l!=N+1)
  {
    if(str[l]=='C')
    {
      double c=Dis(l-1,sqrt(3)/2.0-0.5);
      double b=sqrt(c*c-0.5*0.5);
      double calpha=0.5/c; double salpha=b/c;
      
      double aa=sqrt(3)/2-0.5; double bb=l-1;
      double cbeta=bb/c; double sbeta=aa/c;
      
      ans+=b+acos(salpha*cbeta+calpha*sbeta)*0.5;
    }
    if(str[l]=='S') ans+=Dis(l-1.5,1.0-sqrt(3)/2.0);
  }
  if(r!=N && r!=0)
  {
    if(str[r]=='C')
    {
      double c=Dis(N-r,sqrt(3)/2.0-0.5);
      double b=sqrt(c*c-0.5*0.5);
      double calpha=0.5/c; double salpha=b/c;
      
      double aa=sqrt(3)/2-0.5; double bb=N-r;
      double cbeta=bb/c; double sbeta=aa/c;
      
      ans+=b+acos(salpha*cbeta+calpha*sbeta)*0.5;
    }
    if(str[r]=='S') ans+=Dis(N-r-0.5,1.0-sqrt(3)/2.0);
  }
  
  if(l==N+1&&r==0) ans+=N-1;
  else{ans+=(r-l+1); ans-=0.5*((str[l]=='C')+(str[r]=='C'));}
  return printf("%.9lf\n",ans),0;
}
View Code

 

2017.7.9

[CERC2016]Jazz Journey

很爽做了接近一个小时做了出来

我们考虑那些旅程是单独的 也就是说 你买的飞机票 只是关于你和下一个地方 而与其他地方无关

那么我们可以单独挑出两个点来查看他们的往返情况 然后定制最优的策略

其实他们的往返可以用0和1表示 0就是去 1就是回

又发现双向的可以由两个单向叠加 也就是$min(0->1->0,0->1 + 1-> 0)$

所以把01成对的都可以用两个方式取完

分别是 0->1 1->0

然后剩下的全是0或者全是1 这样的话我们可以跑一直跑单向的

考虑为什么对呢 因为成对的 你跑起来 肯定比单向的要优 经过你上面的叠加后

那么就考虑怎么取对的问题 01对的个数是互补的 你取了多少个01 就剩下多少个10

那么就很简单了 你看看01和10那个优就先去哪个

剩下的还有一个要注意 就是假设你现在要 0->1->0 可以去到1的时候买双程票回来

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <map>
#include <vector>
#include <climits>

using namespace std;
typedef long long LL;

const LL Maxn=300010;

struct node
{
  vector<LL>V; LL c1,c2,c3,c4;
  node(){c1=c2=c3=c4=INT_MAX;}
}E[Maxn]; map<pair<LL,LL> ,LL> mp; LL tot=0;

LL N,K; LL A[Maxn];

LL ans=0;
LL Solve(LL k)
{

  if(E[k].c3<=E[k].c4)
  {
    LL a=0,b=0;
    for(LL i=0;i<E[k].V.size();i++) if(E[k].V[i]==0) a++; else b++;
    LL d1=0; LL d2=0; LL p=0;
    for(LL i=0;i<E[k].V.size();i++)
    {
      if(E[k].V[i]==0) p++;
      if(E[k].V[i]==1 && p) d1++,p--;
    }
    d2=min(a,b)-d1;
    return d1*E[k].c3+d2*E[k].c4+(a-min(a,b))*E[k].c1+(b-min(a,b))*E[k].c2;
  }
  else
  {
    LL a=0,b=0;
    for(LL i=0;i<E[k].V.size();i++) if(E[k].V[i]==0) a++; else b++;
    LL d1=0; LL d2=0; LL p=0;
    for(LL i=0;i<E[k].V.size();i++)
    {
      if(E[k].V[i]==1) p++;
      if(E[k].V[i]==0 && p) d2++,p--;
    }
    d1=min(a,b)-d2;
    return d1*E[k].c3+d2*E[k].c4+(a-min(a,b))*E[k].c1+(b-min(a,b))*E[k].c2;
  }
}

int main()
{
  freopen("a.in","r",stdin);
  freopen("a.out","w",stdout);
  scanf("%lld%lld",&N,&K); for(LL i=1;i<=K;i++) scanf("%lld",&A[i]);
  for(LL i=2;i<=K;i++)
  {
    LL x=A[i-1]; LL y=A[i];
    if(mp[make_pair(min(x,y),max(x,y))]==0) mp[make_pair(min(x,y),max(x,y))]=++tot; 
    LL now=mp[make_pair(min(x,y),max(x,y))];
    if(x>y) E[now].V.push_back(1);
    else E[now].V.push_back(0);
  }
  
  LL M; scanf("%lld",&M);
  for(LL i=1;i<=M;i++)
  {
    LL s,d,p; char t;
    scanf("%lld%lld",&s,&d);
    scanf("\n%c",&t);
    scanf("%lld",&p);
    LL now=mp[make_pair(min(s,d),max(s,d))];
    if(t=='O')
    {
      if(s>d) E[now].c2=min(E[now].c2,p);
      else E[now].c1=min(E[now].c1,p);
    }
    else if(t=='R')
    {
      if(s>d) E[now].c4=min(E[now].c4,p);
      else E[now].c3=min(E[now].c3,p);
    }
  }
  
  for(LL i=1;i<=tot;i++)
  {
    E[i].c1=min(E[i].c1,E[i].c3);
    E[i].c2=min(E[i].c2,E[i].c4);
    E[i].c3=min(E[i].c3,E[i].c1+E[i].c2);
    E[i].c4=min(E[i].c4,E[i].c1+E[i].c2);
  }
  
  for(LL i=1;i<=tot;i++)
    ans+=Solve(i);

  return printf("%lld\n",ans),0;
}
View Code

 

 

[Cerc2016]Appearance Analysis

简单找出行和列 按照加号位置hash

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <map>
using namespace std;
const int Maxn=110;
const int Mod=1000000007;
map<int,int>mp;
int N,M; char st[Maxn][Maxn],str[Maxn][Maxn];
int R,C;

int Get()
{
  int p=1; int s=0;
  for(int i=1;i<=R;i++) for(int j=1;j<=C;j++)
  {
    if(st[i][j]=='+') s=(s+p)%Mod;
    p=(p*2)%Mod;
  }
  return s;
}

char strB[Maxn][Maxn];
void Rotate()
{
  for(int i=1;i<=R;i++) for(int j=1;j<=C;j++) strB[j][R-i+1]=st[i][j],st[i][j]='\0';
  swap(R,C);
  for(int i=1;i<=R;i++) for(int j=1;j<=C;j++) st[i][j]=strB[i][j];
}

bool Check()
{
  int k1=Get(); Rotate();
  int k2=Get(); Rotate();
  int k3=Get(); Rotate();
  int k4=Get(); Rotate();
  
  if(mp[k1] || mp[k2] || mp[k3] || mp[k4]) return 1;
  else
  {
    mp[k1]=mp[k2]=mp[k3]=mp[k4]=1;
    return 0;
  }
}

int main()
{
  scanf("%d%d",&N,&M); for(int i=1;i<=N;i++) for(int j=1;j<=M;j++) scanf("\n%c",&str[i][j]);
  for(int j=2;j<=M;j++) if(str[2][j]=='#'){C=j-2; break;}
  for(int i=2;i<=N;i++) if(str[i][2]=='#'){R=i-2; break;}
 
  int ans=0;
  for(int stx=2;stx<=N;stx+=R+1)
  {
    for(int sty=2;sty<=M;sty+=C+1)
    {
      for(int i=1;i<=R;i++) for(int j=1;j<=C;j++) st[i][j]=str[stx+i-1][sty+j-1];
      if(!Check()) ans++;
    }
  }
  return printf("%d\n",ans),0;
}
View Code

 

 2017.7.10

 

小y的密码

发现如果直接从小到大搜A数组方案数不会很多 也就是可重复组合 YY一下不会很多的

然后裸搜 算组合数用递归和for搞一下 开一下longlong 搞一搞细节就行了

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long LL;

const LL Maxn=20;

LL N,K,Limit; LL A[Maxn]; LL C[Maxn][Maxn]; LL bit[Maxn]; bool V[Maxn];
LL ans=0; LL len;

LL P[Maxn][Maxn]; LL num[Maxn];

LL B[Maxn];
void Dfs(LL k)
{
  LL s=0;
  if(k==len+1){ans++; return ;}
  else
  {
    for(LL i=(k==1);i<B[len-k+1];i++)
    {
      if(num[i])
      {
        num[i]--; LL tot=P[len-k][len-k];
        for(LL j=0;j<=9;j++) tot/=P[num[j]][num[j]];
        ans+=tot; num[i]++;
      }
    }
    if(num[B[len-k+1]])
    {
      num[B[len-k+1]]--;
      Dfs(k+1);
      num[B[len-k+1]]++;
    }
  }
}

void Count1(LL x,LL k)
{
  if(x==k+1)
  {
    LL s=0; for(LL i=1;i<=k;i++) s+=bit[(A[i]-A[(k+1)/2])+10]; if(s>Limit) return ;
    
    memset(num,0,sizeof(num));
    for(LL i=1;i<=k;i++) num[A[i]]++;
    
    if(k==len) Dfs(1);
    else
    {
      for(LL i=1;i<=9;i++)
      {
        if(num[i])
        {
          num[i]--; LL ss=P[k-1][k-1];
          for(LL j=0;j<=9;j++) ss/=P[num[j]][num[j]];
          ans+=ss; num[i]++;
        }
      }
    }
  }
  else
  {
    LL p;
    if(x==1) p=0;
    else p=A[x-1];
    for(LL i=p;i<=9;i++)
    {
      A[x]=i;
      Count1(x+1,k);
      A[x]=0;
    }
  }
}

int main()
{
  scanf("%d%d%d",&N,&K,&Limit);
  
  for(LL i=-9;i<=9;i++)
  {
    if(K==0){bit[i+10]=1; continue;}
    LL s=i; for(LL j=2;j<=K;j++) s=s*i;
    bit[i+10]=s;
  }
  
  LL x=N; len=0; while(x) B[++len]=x%10,x/=10;
  
  P[0][0]=1; for(LL i=1;i<=len;i++) for(LL j=1;j<=i;j++){P[i][j]=1; for(LL k=i;k>=i-j+1;k--) P[i][j]*=k;}
  
  C[1][0]=C[1][1]=1;
  for(LL i=2;i<=len;i++)
  {
    C[i][0]=1;
    for(LL j=1;j<=i;j++) C[i][j]=C[i-1][j-1]+C[i-1][j];
  }
  
  ans=0;
  for(LL i=1;i<=len;i++)
    Count1(1,i);
  
  return printf("%d\n",ans),0;
}
View Code 

 

[Tjoi2017]可乐

简单快速幂矩阵转移

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
const int Mod=2017; int N,M;
struct node
{
  int A[40][40];
  node(){memset(A,0,sizeof(A));}
}base,base2;
node Multi(node x,node y){node z; for(int k=0;k<=N;k++) for(int i=0;i<=N;i++) for(int j=0;j<=N;j++) z.A[i][j]=(z.A[i][j]+x.A[i][k]*y.A[k][j])%Mod; return z;}
int main()
{
  scanf("%d%d",&N,&M);
  for(int i=0;i<=N;i++) base2.A[i][i]=base2.A[i][0]=1;
  for(int i=1;i<=M;i++){int x,y; scanf("%d%d",&x,&y); base2.A[x][y]=base2.A[y][x]=1;}
  for(int i=0;i<=N;i++) base.A[i][i]=1;
  
  int t; scanf("%d",&t);
  while(t){if(t&1) base=Multi(base,base2); base2=Multi(base2,base2); t>>=1;}
  int ans=0; for(int i=0;i<=N;i++) ans=(ans+base.A[1][i])%Mod;
  return printf("%d\n",ans),0;
}
View Code

 

2017.7.11

 

[Tjoi2017]不勤劳的图书管理员

这道题一定要在清醒的时候才做!!

看错了几千遍题意

补充一下题意吧 就是按照他给的顺序来搞 一开始是所有书的位置是1 2 3 4 5 ......

那么我们发现 假设第一本书应该放在第二个位置 不妨把第一本书标个号2 然后所有的数都标个号

打个比方 数据是:

2 ?

4 ?

5 ?

1 ?

3 ?

 

那么书的一开始位置和标号就是

1 2 3 4 5

2 4 5 1 3

 

我们可以知道 要按这个编号从小到大排才是正确的 交换书也是交换标号

 

我们又发现 其实就是标号的权值逆序对 关键还带修改

我们考虑之前不带修改的逆序对求法 是用线段树 但是现在带修改了 发现影响的只是中间一段 可以用主席树? 又发现很难修改

不如换成分块 费一点时间 题目开10s是够的 那么就是每个块维护两个树状数组

对于逆序对来说 剩下的直接扫 大块的只要统计和我成逆序对的个数和这些能和我成逆序对的数的权值和即可

细节比较多 比较恶心

不知道说的清不清楚 反正做这些题目有点乱就是了

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdlib>

using namespace std;

typedef long long LL;

const LL Maxn=50010;
const LL Mod=1e9+7;

LL N,M; LL rak[Maxn],val[Maxn],pos[Maxn];

LL Block[Maxn]; LL q,cnt; LL siz[Maxn];
LL tr[2][60][Maxn];
LL low_bit(LL x){return x&(-x);}
void Add(LL k,LL b,LL x,LL c)
{
  while(x<=N)
  {
    tr[k][b][x]+=c; x+=low_bit(x);
  }
}
LL Query(LL k,LL b,LL x){LL ans=0; while(x>=1){ans+=tr[k][b][x]; x-=low_bit(x);} return ans;}

int main()
{
  freopen("a.in","r",stdin);
  freopen("a.out","w",stdout);
  
  scanf("%lld%lld",&N,&M);
  for(LL i=1;i<=N;i++) scanf("%lld%lld",&pos[i],&val[i]);
  
  q=(LL)sqrt(N*20); for(LL i=1;i<=N;i++) Block[i]=(i-1)/q+1; cnt=Block[N];
  memset(siz,0,sizeof siz);
  for(LL i=1;i<=N;i++) siz[Block[i]]++;
  
  for(LL i=1;i<=N;i++)
  {
    Add(0,Block[i],pos[i],1);
    Add(1,Block[i],pos[i],val[i]);
  }
  
  LL ans=0;
  
  for(LL i=1;i<=N;i++)
  {
    LL j;
    for(j=Block[1];j<Block[i];j++) ans+=(Query(0,j,N)-Query(0,j,pos[i]))*val[i]+(Query(1,j,N)-Query(1,j,pos[i]));
    j=i; while(Block[j-1]==Block[j]){j--; if(pos[j]>pos[i]) ans+=val[i]+val[j];}
  }
  
  for(LL i=1;i<=M;i++)
  {
    LL x,y; scanf("%lld%lld",&x,&y);
    
    LL l=min(x,y); LL r=max(x,y);
    
    if(Block[l]==Block[r])
    {
      /// -- ++
      if(pos[l]<pos[r]) ans+=val[l]+val[r];
      else if(pos[l]>pos[r]) ans-=val[l]+val[r];
      
      for(LL j=l+1;j<r;j++) if(pos[j]<pos[l]) ans-=val[l]+val[j];
      for(LL j=r-1;j>l;j--) if(pos[j]>pos[r]) ans-=val[r]+val[j];
      swap(pos[l],pos[r]); swap(val[l],val[r]);
      for(LL j=l+1;j<r;j++) if(pos[j]<pos[l]) ans+=val[l]+val[j];
      for(LL j=r-1;j>l;j--) if(pos[j]>pos[r]) ans+=val[r]+val[j];
    }
    
    else
    {
      LL j;
      
      if(pos[l]<pos[r]) ans+=val[l]+val[r];
      else if(pos[l]>pos[r]) ans-=val[l]+val[r];
      
      j=l; while(Block[j+1]==Block[j]){j++; if(pos[j]<pos[l]) ans-=val[l]+val[j];}
      for(j=Block[l]+1;j<Block[r];j++) ans-=Query(0,j,pos[l])*val[l]+Query(1,j,pos[l]);
      j=r; while(Block[j-1]==Block[j]){j--; if(pos[j]<pos[l]) ans-=val[l]+val[j];}
      
      j=l; while(Block[j+1]==Block[j]){j++; if(pos[j]>pos[r]) ans-=val[r]+val[j];}
      for(j=Block[l]+1;j<Block[r];j++) ans-=(Query(0,j,N)-Query(0,j,pos[r]))*val[r]+(Query(1,j,N)-Query(1,j,pos[r]));
      j=r; while(Block[j-1]==Block[j]){j--; if(pos[j]>pos[r]) ans-=val[r]+val[j];}
      
      Add(0,Block[l],pos[l],-1); Add(0,Block[r],pos[r],-1); Add(1,Block[l],pos[l],-val[l]); Add(1,Block[r],pos[r],-val[r]);
      swap(pos[l],pos[r]); swap(val[l],val[r]);
      Add(0,Block[l],pos[l],1); Add(0,Block[r],pos[r],1); Add(1,Block[l],pos[l],val[l]); Add(1,Block[r],pos[r],val[r]);
      
      j=l; while(Block[j+1]==Block[j]){j++; if(pos[j]<pos[l]) ans+=val[l]+val[j];}
      for(j=Block[l]+1;j<Block[r];j++) ans+=Query(0,j,pos[l])*val[l]+Query(1,j,pos[l]);
      j=r; while(Block[j-1]==Block[j]){j--; if(pos[j]<pos[l]) ans+=val[l]+val[j];}
      
      j=l; while(Block[j+1]==Block[j]){j++; if(pos[j]>pos[r]) ans+=val[r]+val[j];}
      for(j=Block[l]+1;j<Block[r];j++) ans+=(Query(0,j,N)-Query(0,j,pos[r]))*val[r]+(Query(1,j,N)-Query(1,j,pos[r]));
      j=r; while(Block[j-1]==Block[j]){j--; if(pos[j]>pos[r]) ans+=val[r]+val[j];}
      
    }
    printf("%lld\n",ans%Mod);
  }
  return 0;
}
View Code

 

 

[Tjoi2017]异或和

好题啊 我们发现这道题直接跑是N^2

很快想到按位跑 然后想想前缀和

一段序列的和就是前缀和的一个后面的减去前缀和的一个前面的

前缀和当前位相同的时候 后面的那个数的 当前位后面的数 要小于前面的那个数的 当前位后面的数

相反则翻转过来 用树状数组维护

#include <bits/stdc++.h>
using namespace std;
const int Maxn=100010;
int N,A[Maxn]; int tr[2][1000010]; int Sum[Maxn]; int bit[30];
int low_bit(int x){return x&(-x);}
void Add(int k,int x,int c){while(x<=1000000+1){tr[k][x]+=c; x+=low_bit(x);}}
int Query(int k,int x){int ans=0; while(x>=1){ans+=tr[k][x]; x-=low_bit(x);} return ans;}
int main()
{
  scanf("%d",&N); for(int i=1;i<=N;i++) scanf("%d",&A[i]);
  Sum[0]=0; for(int i=1;i<=N;i++) Sum[i]=Sum[i-1]+A[i];
  for(int i=0;i<=20;i++) bit[i]=(1<<i);
  int ans=0;
  for(int p=0;p<=19;p++)
  {
    int s=0; memset(tr,0,sizeof(tr));
    for(int i=0;i<=N;i++)
    {
      int bitp=Sum[i]%bit[p+1]/bit[p]; int x=Sum[i]%bit[p];
      s+=Query(bitp^1,x+1)+(Query(bitp,1000000+1)-Query(bitp,x+1));
      Add(bitp,x+1,1);
    }
    if(s&1) ans+=bit[p];
  }
  return printf("%d\n",ans),0;
}
View Code

 

 2017.7.12

[Tjoi2017]城市

枚举删去的边 然后分开 跑出到这个点的最长距离和树内最长链 然后两棵树并起来即可

我发现我有卡时的天赋

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <climits>
 
using namespace std;
const int Maxn=5010;
struct node{int x,y,next,d;}edge[Maxn*2]; int len,first[Maxn];
void ins(int x,int y,int d){len++; edge[len].x=x; edge[len].y=y; edge[len].d=d; edge[len].next=first[x]; first[x]=len;}
int N; int fir[Maxn],sec[Maxn],dis[Maxn],G[Maxn];
 
int f[Maxn]; int Find(int x){return (f[x]==x)?f[x]:f[x]=Find(f[x]);}
 
void Dfs(int x,int fa)
{
  for(int k=first[x];k!=-1;k=edge[k].next)
  {
    int y=edge[k].y; if(y==fa) continue;
    Dfs(y,x); dis[x]=max(dis[x],dis[y]+edge[k].d);
    if(dis[y]+edge[k].d>fir[x]) sec[x]=fir[x],fir[x]=dis[y]+edge[k].d;
    else if(dis[y]+edge[k].d>sec[x]) sec[x]=dis[y]+edge[k].d;
  }
}
 
void Dfs2(int x,int fa,int anc)
{
  for(int k=first[x];k!=-1;k=edge[k].next)
  {
    int y=edge[k].y; if(y==fa) continue;
    if(dis[y]==fir[x]-edge[k].d)
    {
      if(anc==fa) G[y]=sec[x]+edge[k].d;
      else G[y]=max(G[x],sec[x])+edge[k].d;
    }
    else
    {
      if(anc==fa) G[y]=fir[x]+edge[k].d;
      else G[y]=max(G[x],fir[x])+edge[k].d;
    }
    f[Find(y)]=f[Find(x)];
    Dfs2(y,x,anc);
  }
}
 
int main()
{
  scanf("%d",&N); len=0; memset(first,-1,sizeof(first));
  for(int i=1;i<N;i++){int x,y,d; scanf("%d%d%d",&x,&y,&d); ins(x,y,d); ins(y,x,d);}
  int minx=INT_MAX;
  for(int i=1;i<=len;i+=2)
  {
    memset(fir,0,sizeof(fir));
    memset(sec,0,sizeof(sec));
    memset(dis,0,sizeof(dis));
    memset(G,0,sizeof(G));
    for(int j=1;j<=N;j++) f[j]=j;
     
    Dfs(edge[i].x,edge[i].y);
    Dfs(edge[i].y,edge[i].x);
     
    Dfs2(edge[i].x,edge[i].y,edge[i].y);
    Dfs2(edge[i].y,edge[i].x,edge[i].x);
     
    int xx=Find(edge[i].x); int yy=Find(edge[i].y);
     
    int xmin=INT_MAX; int ymin=INT_MAX;
    for(int j=1;j<=N;j++)
    {
      if(Find(j)==xx) xmin=min(xmin,max(dis[j],G[j]));
      if(Find(j)==yy) ymin=min(ymin,max(dis[j],G[j]));
    }
     
    int sum=xmin+ymin+edge[i].d;
     
    for(int j=1;j<=N;j++)
    {
      int mi=min(fir[j],min(sec[j],G[j]));
      sum=max(sum,fir[j]+sec[j]+G[j]-mi);
    }
     
    minx=min(minx,sum);
  }
  return printf("%d\n",minx),0;
}
View Code

 

 

[Tjoi2017]dna

很快想到hash一下 但是不可以换 那么我们直接跳过那三个hash就好了

还有hash的时候不要想太多mod和其他的 自然溢出很优美

hash的方法是最高位在前面 最低位在后面 然后相减

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int Maxn=100010;

const int Mod=1e9+7;

int bit[Maxn]; int T; char A[Maxn],B[Maxn]; int Alen,Blen;

int HashA[Maxn],HashB[Maxn];

int num(char st)
{
  if(st=='A') return 1;
  if(st=='T') return 2;
  if(st=='C') return 3;
  if(st=='G') return 4;
}


LL getnumA(int l,int r){return HashA[r]-HashA[l-1]*bit[r-l+1];}
LL getnumB(int l,int r){return HashB[r]-HashB[l-1]*bit[r-l+1];}

int Find(int x,int y)
{
  int l=0; int r=min(Alen-x+1,Blen-y+1); int mid,ret;
  while(l<=r)
  {
    mid=(l+r)>>1;
    if(getnumA(x,x+mid)!=getnumB(y,y+mid)) r=mid-1,ret=mid;
    else l=mid+1;
  }
  return y+ret;
}

int main()
{
  scanf("%d",&T); bit[0]=1; for(int i=1;i<=100000;i++) bit[i]=bit[i-1]*5;

  while(T--)
  {
    scanf("%s",A+1); scanf("%s",B+1);
    Alen=strlen(A+1); Blen=strlen(B+1);
    
    memset(HashA,0,sizeof(HashA));
    memset(HashB,0,sizeof(HashB));
    for(int i=1;i<=Alen;i++) HashA[i]=HashA[i-1]*5+num(A[i]);
    for(int i=1;i<=Blen;i++) HashB[i]=HashB[i-1]*5+num(B[i]);
    
    int ans=0;
    for(int i=1;i<=Alen-Blen+1;i++)
    {
      int nx; nx=Find(i,1);
      if(nx<=Blen) nx=Find(i+nx,nx+1);
      if(nx<=Blen) nx=Find(i+nx,nx+1);
      if(nx<=Blen) nx=Find(i+nx,nx+1);
      if(nx==Blen+1) ans++;
    }
    printf("%d\n",ans);
  }
  return 0;
}
View Code

 

2017.7.13

今天学了线性基 很好玩

那么今天就做点线性基的题目

 

[BeiJing2011]元素

就是求不为0的 那么先从大到小排 尽量能插就插

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

typedef long long LL;

const int Maxn=1010;

int N; pair<LL,LL>pr[Maxn]; LL ins[Maxn];
bool Cmp(const pair<LL,LL> &x,const pair<LL,LL> &y){return x.second>y.second;}


int main()
{
  
  scanf("%d",&N); for(int i=1;i<=N;i++) scanf("%lld%lld",&pr[i].first,&pr[i].second);
  sort(pr+1,pr+N+1,Cmp);
  
  LL ans=0;
  for(int i=1;i<=N;i++)
  {
    for(int j=62;j>=0;j--)
      if(pr[i].first >> j &1)
      {
        if(ins[j]) pr[i].first^=ins[j];
        else{ins[j]=pr[i].first; break;}
      }
    if(pr[i].first) ans+=pr[i].second;
  }
  return printf("%lld\n",ans),0;
}
View Code

 

[Wc2011] Xor

把环找出来 然后只要随便搞出一条路径 因为是双向边 如果答案是其他路径也会被搞出来

然后用环搞出来的xor值用线性基搞一下 和路径的xor值取贪心取最大

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

typedef long long LL;
const LL Maxn=50010;

struct node{LL x,y,next,d;}edge[Maxn*4]; LL len,first[Maxn];
void ins(LL x,LL y,LL d){len++; edge[len].x=x; edge[len].y=y; edge[len].d=d; edge[len].next=first[x]; first[x]=len;}

LL N,M,d[Maxn],a[Maxn*20],b[Maxn],cnt;
bool vis[Maxn];
void Dfs(LL x,LL fa)
{
  vis[x]=1;
  for(LL k=first[x];k!=-1;k=edge[k].next)
  {
    LL y=edge[k].y;
    if(!vis[y])
    {
      d[y]=d[x]^edge[k].d;
      Dfs(y,x);
    }
    else a[++cnt]=d[y]^d[x]^edge[k].d;
  }
}

int main()
{
  scanf("%lld%lld",&N,&M); len=0; memset(first,-1,sizeof(first));
  for(LL i=1;i<=M;i++){LL x,y,d; scanf("%lld%lld%lld",&x,&y,&d); ins(x,y,d); ins(y,x,d);}
  cnt=0; memset(vis,0,sizeof(vis)); Dfs(1,0);
  
  for(LL i=1;i<=cnt;i++)
  {
    for(LL j=62;j>=0;j--)
      if(a[i]>>j &1)
      {
        if(b[j]) a[i]^=b[j];
        else{b[j]=a[i]; break;}
      }
  }
  
  LL maxx=d[N];
  for(LL i=62;i>=0;i--) if(b[i]) if((maxx ^ b[i]) > maxx) maxx^=b[i];
  
  return printf("%lld\n",maxx),0;
}
View Code

 

 

[cqoi2013]新Nim游戏

第一个人无论如何都可以必胜 而且留给第二个人一定是一些无论怎么组合都不会异或为0的数

那么就简单了 简单点说 就是要第二个人由大到小取的能构成线性基的 剩下的交给第一个人就好了

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL Maxn=110; LL A[Maxn],N,B[Maxn];
bool Cmp(const LL &x,const LL &y){return x>y;}
LL Check(LL i)
{
  LL x=A[i];
  for(LL j=62;j>=0;j--)
    if(x >> j &1)
    {
      if(B[j]) x^=B[j];
      else{B[j]=x; break;}
    }
  return x;
}
int main()
{
  scanf("%lld",&N); LL ans=0; for(LL i=1;i<=N;i++) scanf("%lld",&A[i]),ans+=A[i];
  sort(A+1,A+N+1,Cmp);
  for(LL i=1;i<=N;i++) if(Check(i)) ans-=A[i];
  return printf("%lld\n",ans),0;
}
View Code

 

 

albus就是要第一个出场

首先我们的第k小 通过高斯消元后可以得到第k小为

那么现在给出你一个数 然后让你去用线性基得到它

很简单 高斯消元的线性基满足每个位只有一个数 我们就打算在线性基中凑出这个数 然后把这个数转成十进制即可

因为有空集合 所以一定有0 然后再用上重复个数有 2​^(nB∣) 个的结论就好了

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL Maxn=1000010;
const LL Mod=10086;
LL N,A[Maxn],B[Maxn],Q; LL cnt=0;
void Dfs(LL x)
{
  for(LL j=62;j>=0;j--)
    if(x>>j &1)
    {
      if(B[j]) x^=B[j];
      else
      {
        B[j]=x; cnt++;
        for(LL k=0;k<j;k++) if(B[j]>>k&1) B[j]^=B[k];
        for(LL k=62;k>j;k--) if(B[k]>>j&1) B[k]^=B[j];
        break;
      }
    }
}

LL Quick_Power(LL x,LL k)
{
  LL ans=1;
  while(k)
  {
    if(k&1) ans=(ans*x)%Mod;
    k>>=1; x=(x*x)%Mod;
  }return ans;
}

LL c[Maxn]; LL clen=0;

int main()
{
  //freopen("a.in","r",stdin);
  //freopen("a.out","w",stdout);
  scanf("%lld",&N); for(LL i=1;i<=N;i++) scanf("%lld",&A[i]),Dfs(A[i]);
  scanf("%lld",&Q); 
  for(LL i=0;i<=62;i++) if(B[i]) c[clen++]=i;
  LL ans=0; LL num=Quick_Power(2,N-cnt);
  for(LL i=clen-1;i>=0;i--) if(Q>>c[i]&1) ans=(ans+(1<<i))%Mod;
  return printf("%lld\n",(ans*num%Mod+1)%Mod),0;
}
View Code

 

2017.7.14

 

[Scoi2016]幸运数字

倍增就好了 合并的时候把一个数组扔到另外一个数组里面跑

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int Maxn=20010;
struct EDGE{int x,y,next;}edge[Maxn*20]; 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;}
int N,Q; LL Val[Maxn]; int fa[Maxn][17]; LL bas[Maxn][17][63]; int dep[Maxn];

int L[Maxn][17];

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

LL A[100],B[100]; int Alen;

void Merge()
{
  for(int i=1;i<=Alen;++i)
    for(int j=59;j>=0;--j)
      if(A[i]>>j &1)
      {
        if(B[j]) A[i]^=B[j];
        else{B[j]=A[i]; break;}
      }
}

int LCA(int x,int y)
{
  if(dep[x]<dep[y]) swap(x,y);
  int deep=dep[x]-dep[y];
  for(int i=16;i>=0;--i)
  {
    if(deep>=(1<<i))
    {
      deep-=(1<<i);
      Alen=0; for(int p=1;p<=L[x][i];++p) A[++Alen]=bas[x][i][p];
      Merge();
      x=fa[x][i];
    }
  }
  
  if(x==y) return x;
  for(int i=16;i>=0;--i)
    if(fa[x][i]!=fa[y][i])
    {
      Alen=0; for(int p=1;p<=L[x][i];++p) A[++Alen]=bas[x][i][p];
      Merge();
      x=fa[x][i];
      
      
      Alen=0; for(int p=1;p<=L[y][i];++p) A[++Alen]=bas[y][i][p];
      Merge();
      y=fa[y][i];
    }
    
  Alen=0; for(int p=1;p<=L[x][0];++p) A[++Alen]=bas[x][0][p];
  Merge();  
  x=fa[x][0];
      
  Alen=0; for(int p=1;p<=L[y][0];++p) A[++Alen]=bas[y][0][p];
  Merge();  
  y=fa[y][0];
  
  return x;
}

int main()
{
  freopen("a.in","r",stdin);
  freopen("a.out","w",stdout);
  scanf("%d%d",&N,&Q); len=0; memset(first,-1,sizeof(first));
  for(int i=1;i<=N;i++) scanf("%lld",&Val[i]);
  for(int i=1;i<N;i++){int x,y; scanf("%d%d",&x,&y); ins(x,y); ins(y,x);}
  dep[1]=1; fa[1][0]=0; bas[1][0][++L[1][0]]=Val[1]; Dfs(1);
  
  for(int j=1;j<=16;j++)
    for(int i=1;i<=N;i++)
    {
      fa[i][j]=fa[fa[i][j-1]][j-1];
      
      Alen=0; memset(B,0,sizeof(B));
      for(int p=1;p<=L[i][j-1];p++) A[++Alen]=bas[i][j-1][p];
      for(int p=1;p<=L[fa[i][j-1]][j-1];p++) A[++Alen]=bas[fa[i][j-1]][j-1][p];
      Merge();
      for(int p=0;p<=59;p++) if(B[p]) bas[i][j][++L[i][j]]=B[p];
      
    }
    
  for(int i=1;i<=Q;i++)
  {
    int x,y; scanf("%d%d",&x,&y);
    
    memset(B,0,sizeof(B));
    int anc=LCA(x,y);
    
    Alen=0; for(int p=1;p<=L[anc][0];p++) A[++Alen]=bas[anc][0][p];
    Merge();
    
    LL ans=0; for(int i=59;i>=0;i--) if((ans^B[i])>ans) ans^=B[i];
    printf("%lld\n",ans);
    
  }
  
  //cout<<(double)(clock())<<endl;
  return 0;
}
View Code

 

[JLOI2015]装备购买

 像线性基一样 维护一个上三角 用高斯消元

#include <bits/stdc++.h>
using namespace std;
const int Maxn=510;
const long double eps=1e-6;
int N,M; struct node{long double A[Maxn]; int c;}z[Maxn];
bool Cmp(const node &x,const node &y){return x.c<y.c;} int vis[Maxn];
int main()
{
  scanf("%d%d",&N,&M);
  for(int i=1;i<=N;i++) for(int j=1;j<=M;j++) cin>>z[i].A[j];
  for(int i=1;i<=N;i++) scanf("%d",&z[i].c);
  
  sort(z+1,z+N+1,Cmp); memset(vis,0,sizeof(vis)); int ans=0,ans1=0;
  for(int i=1;i<=N;i++)
  {
    for(int j=1;j<=M;j++)
    {
      if(fabs(z[i].A[j])>eps)
      {
        if(!vis[j])
        {
          ans++; ans1+=z[i].c;
          vis[j]=i;
          break;
        }
        else
        {
          long double kk=z[i].A[j]/z[vis[j]].A[j];
          for(int k=1;k<=M;k++) z[i].A[k]-=z[vis[j]].A[k]*kk;
        }
      }
    }
  }
  return printf("%d %d\n",ans,ans1),0;
}
View Code

  

 

shallot

跑出来的时间很玄学啊这道题 首先每个值只影响一个区间 然后我们可以对时间开线段树 然后在节点上放上这个值

很容易知道每个叶子节点到根的所有点的所有值的线性基 再搞一下 就是这个点的答案

于是分治下去 一开始担心空间一个个跑 发现这样时间退化的很厉害

只能到每个节点分开跑 这样保证是O(NlogN) 总的时间复杂度是O(N logN logAi) 发现把结构体传下去这样的做法不卡空间

本机测16s在评测机上测很快

 

#include <bits/stdc++.h>
using namespace std;
const int Maxn=500010;
int N,A[Maxn]; map<int,int>mp; int cnt; int Hash[Maxn];
vector<pair<int,int> >V[Maxn];

struct node
{
  int b[32];
  node(){memset(b,0,sizeof(b));}
  void insert(int x)
  {
    for(int j=30;j>=0;j--)
      if(x>>j &1)
      {
        if(b[j]) x^=b[j];
        else{b[j]=x; break;}
      }
  }
};
vector<int>C[Maxn*4]; int lc[Maxn*4]; int rc[Maxn*4]; int rt,tot;
void Link(int &u,int L,int R,int l,int r,int c)
{
  if(!u) u=++tot;
  if(L==l&&R==r){C[u].push_back(c); return ;}
  int mid=(L+R)>>1;
  if(r<=mid) Link(lc[u],L,mid,l,r,c);
  else if(l>mid) Link(rc[u],mid+1,R,l,r,c);
  else
  {
    Link(lc[u],L,mid,l,mid,c);
    Link(rc[u],mid+1,R,mid+1,r,c);
  }
}

int ans[Maxn],nx;

void Query(int u,int L,int R,node now)
{
  for(int i=0;i<C[u].size();i++)
    now.insert(C[u][i]);
  if(!lc[u] && !rc[u])
  {
    int maxx=0;
    for(int i=30;i>=0;i--) maxx=max(maxx,maxx^now.b[i]);
    
    for(int i=L;i<=R;i++) printf("%d\n",maxx);
    return ;
  }
  int mid=(L+R)>>1;
  Query(lc[u],L,mid,now);
  Query(rc[u],mid+1,R,now);
}

int main()
{
  scanf("%d",&N); for(int i=1;i<=N;i++) scanf("%d",&A[i]);
  for(int i=1;i<=N;i++) if(A[i]>0) if(mp[A[i]]==0) mp[A[i]]=++cnt,Hash[cnt]=A[i];
  for(int i=1;i<=N;i++)
  {
    if(A[i]>0) V[mp[A[i]]].push_back(make_pair(i,A[i]));
    else V[mp[-A[i]]].push_back(make_pair(i,A[i]));
  }
  
  for(int i=1;i<=cnt;i++)
  {
    int last; int c=0;
    for(int j=0;j<V[i].size();j++)
    {
      pair<int,int>pr=V[i][j];
      if(V[i][j].second>0)
      {
        if(c==0) last=V[i][j].first;
        c++;
      }
      if(V[i][j].second<0)
      {
        if(c==1) Link(rt,1,N,last,V[i][j].first-1,Hash[i]);
        c--;
      }
    }
    if(c) Link(rt,1,N,last,N,Hash[i]);
  }
  
  node now;
  Query(rt,1,N,now);
  return 0;
}
View Code

 

 

 2017.7.15

 

[Scoi2016]萌萌哒

首先我们很快想到用并查集搞一下

但是MN并查集有点吃屎

所以我们想到倍增优化

标记满足往下传

连到最前面就好了

 

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long LL;

const int Maxn=100010;
const int Mod=1e9+7;

struct node{int l1,l2,r1,r2;}Q[Maxn];

int N,M; int num[Maxn][22],tot=0; pair<int,int>pr[Maxn*22];
int fa[Maxn*22]; int Find(int x){return (fa[x]==x)?x:fa[x]=Find(fa[x]);}

LL Quick_Power(LL x,LL k)
{
  LL ans=1;
  while(k)
  {
    if(k&1) ans=(ans*x)%Mod;
    x=(x*x)%Mod; k>>=1;
  }return ans;
}

int main()
{
  
  scanf("%d%d",&N,&M);
  
  for(int j=0;j<=20;j++)
    for(int i=1;i<=N;i++)
    {
      num[i][j]=++tot;
      pr[tot].first=i; pr[tot].second=j;
    }

  for(int i=1;i<=tot;i++) fa[i]=i;

  for(int i=1;i<=M;i++)
  {
    scanf("%d%d%d%d",&Q[i].l1,&Q[i].r1,&Q[i].l2,&Q[i].r2);
    int L=Q[i].r1-Q[i].l1+1;
    for(int j=20;j>=0;j--) if((1<<j)<=L)
    {
      int xx=Find(num[Q[i].l1][j]); int yy=Find(num[Q[i].l2][j]);
      
      if(xx>yy) swap(xx,yy);
      
      fa[yy]=xx; L-=(1<<j); Q[i].l1+=(1<<j); Q[i].l2+=(1<<j);
    }
  }
  
  for(int j=20;j>=1;j--)
    for(int i=1;i<=N;i++)
    {
      int y=Find(num[i][j]); int ii=pr[y].first; int jj=pr[y].second;
      
      int l3=Find(num[ii][jj-1]);
      int l4=Find(num[i][j-1]);
      if(l3>l4) swap(l3,l4);
      fa[l4]=l3;
      
      if(ii+(1<<(jj-1)) <=N)
      {
        int r3=Find(num[ii+(1<<(jj-1))][jj-1]);
        int r4=Find(num[i+(1<<(j-1))][j-1]);
        if(r3>r4) swap(r3,r4);
        fa[r4]=r3;
      }
    }
  
  int cnt=0;
  for(int i=1;i<=N;i++) if(fa[num[i][0]]==num[i][0]) cnt++;
  
  return printf("%lld\n",9LL*Quick_Power(10,cnt-1)%Mod),0;
}
View Code

 

 

罗马游戏

 左偏树可并堆模版题

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int Maxn=1000010;
struct LTT
{
  int fa[Maxn],lc[Maxn],rc[Maxn],val[Maxn],D[Maxn]; bool die[Maxn];
  int Find(int x){return (fa[x]==x) ? x:fa[x]=Find(fa[x]);}
  int Merge(int x,int y)
  {
    if(!x) return y;
    if(!y) return x;
    if(val[x]>val[y]) swap(x,y);
    rc[x]=Merge(rc[x],y);
    if(D[lc[x]]<D[rc[x]]) swap(D[lc[x]],D[rc[x]]);
    return x;
  }
}zps; int N,M; char ch[10];
int main()
{
  scanf("%d",&N); for(int i=1;i<=N;i++) scanf("%d",&zps.val[i]);
  
  zps.D[0]=-1; for(int i=1;i<=N;i++) zps.fa[i]=i;
  
  scanf("%d",&M);
  for(int i=1;i<=M;i++)
  {
    scanf("%s",ch+1); int x,y;
    if(ch[1]=='M')
    {
      scanf("%d%d",&x,&y);
      if(zps.die[x] || zps.die[y]) continue;
      int p=zps.Find(x); int q=zps.Find(y);
      if(p!=q) zps.fa[p]=zps.fa[q]=zps.Merge(p,q);
    }
    else
    {
      scanf("%d",&x);
      if(zps.die[x]){printf("0\n"); continue;}
      int p=zps.Find(x); zps.die[p]=1; printf("%d\n",zps.val[p]);
      zps.fa[p]=zps.Merge(zps.lc[p],zps.rc[p]);
      zps.fa[zps.fa[p]]=zps.fa[p];
    }
  }
  return 0;
}
View Code 

 

[Baltic2004]sequence

好像是论文题 见国家集训队2005 黄源河论文

 

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdlib>

using namespace std;

const int Maxn=1e6+10;

struct LTree
{
  int fa[Maxn],L[Maxn],R[Maxn],D[Maxn],cnt,val[Maxn],siz[Maxn];
  LTree(){cnt=0;}
  int Find(int x){return (x==fa[x])?x:fa[x]=Find(fa[x]);}
  int New_heap(int x)
  {
    cnt++; val[cnt]=x; siz[cnt]=1;
    fa[cnt]=cnt; return cnt;
  }
  int top(int x){return val[x];}
  int Merge(int x,int y)
  {
    if(!x) return y;
    if(!y) return x;
    if(val[x]<val[y]) swap(x,y);
    R[x]=Merge(R[x],y); siz[x]=siz[R[x]]+siz[L[x]]+1;
    if(D[R[x]]>D[L[x]]) swap(R[x],L[x]);
    D[x]=D[R[x]]+1;
    return x;
  }
  void pop(int &x){x=Merge(L[x],R[x]);}
}heap;

int N; int now=0,rt[Maxn]; int L[Maxn],R[Maxn],A[Maxn];

int main()
{
  scanf("%d",&N); heap.D[0]=-1;
  
  for(int i=1;i<=N;i++)
  {
    scanf("%d",&A[i]); A[i]-=i; now++; rt[now]=heap.New_heap(A[i]); L[now]=R[now]=i;
    while(now>1 && heap.top(rt[now])<=heap.top(rt[now-1]))
    {
      rt[now-1]=heap.Merge(rt[now],rt[now-1]);
      R[now-1]=R[now];
      
      while(heap.siz[rt[now-1]]>(R[now-1]-L[now-1]+1)/2+1) heap.pop(rt[now-1]);
      
      now--;
    }
  }
  
  double ans=0;
  for(int i=1;i<=now;i++)
  {
    double mid;
    if((R[i]-L[i]+1)&1){mid=heap.top(rt[i]);}
    else{mid=heap.top(rt[i]); heap.pop(rt[i]); mid+=heap.top(rt[i]); mid/=2.0;}
    for(int j=L[i];j<=R[i];j++) ans+=fabs(A[j]-mid);
  }
  return printf("%.0lf\n",ans),0;
}

/*
7
9
4
8
20
14
15
18
*/
View Code

 

 2017.7.16

 

[Apio2012]dispatching

 从低下向上合并 看懂题目是最难的

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>

using namespace std;

typedef long long LL;

const LL Maxn=1e5+10;

struct LTT
{
  LL fa[Maxn],L[Maxn],R[Maxn],D[Maxn],val[Maxn],tot[Maxn],siz[Maxn];
  LL Find(LL x){return (fa[x]==x)?x:fa[x]=Find(fa[x]);}
  LL Merge(LL x,LL y)
  {
    if(!x) return y; if(!y) return x;
    if(val[x]<val[y]) swap(x,y);
    R[x]=Merge(R[x],y); tot[x]=val[x]+tot[L[x]]+tot[R[x]]; siz[x]=siz[L[x]]+siz[R[x]]+1;
    if(D[R[x]]>D[L[x]]) swap(L[x],R[x]);
    D[x]=D[R[x]]+1;
    return x;
  }
  void pop(LL &x)
  {
    fa[x]=Merge(L[x],R[x]);
    fa[fa[x]]=fa[x];
    x=fa[x];
  }
}zps;

LL N,M; LL B[Maxn],C[Maxn],L[Maxn],ru[Maxn],rt[Maxn];

queue<LL>Q;

int main()
{
  
  scanf("%lld%lld",&N,&M); memset(ru,0,sizeof(ru));
  for(LL i=1;i<=N;i++) scanf("%lld%lld%lld",&B[i],&C[i],&L[i]),ru[B[i]]++;
  
  LL maxx=0; zps.D[0]=-1;
  for(LL i=1;i<=N;i++)
  {
    zps.fa[i]=i,zps.val[i]=C[i],rt[i]=i,zps.tot[i]=C[i],zps.siz[i]=1;
    if(C[i]<=M) maxx=max(maxx,L[i]);
  }
  
  for(LL i=1;i<=N;i++) if(ru[i]==0) Q.push(i); 
  while(!Q.empty())
  {
    LL x=Q.front(); LL f=B[x]; if(f==0) break;
    LL p=zps.Find(x); LL q=zps.Find(f);
    zps.fa[p]=zps.fa[q]=rt[f]=zps.Merge(p,q);
    
    while(zps.tot[rt[f]]>M) zps.pop(rt[f]);
    
    ru[f]--;
    if(ru[f]==0)
    {
      maxx=max(maxx,L[f]*zps.siz[rt[f]]);
      Q.push(f);
    }
    Q.pop();
  }

  return printf("%lld\n",maxx),0;
}
View Code

 

 2017.7.22

很久没更新了 之前学了一下K短路 然后今天做了一下后缀自动机

首先

 

[Zjoi2015]诸神眷顾的幻想乡

 之前学这个东西好像还没开窍?

就是字典树建一下 对于每个叶子节点跑一次DFS建出来

那么他们的所有路径就可以表示出来了(想一想)

然后答案就是$\Sigma step[i]-step[pre[i]]$

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

typedef long long LL;

const int Maxn=4000010;

int C[Maxn]; int N,K;

struct SAM
{
  int fail[Maxn],step[Maxn],rt[Maxn],tot,last,G[Maxn][20],root;
  void init()
  {
    root=tot=1;
    memset(step,0,sizeof(step));
    memset(fail,0,sizeof(fail));
    memset(G,0,sizeof(G));
    memset(rt,0,sizeof(rt));
  }
  void insert(int last,int i)
  {
    int p,np,q,nq; int nx=C[i];
    p=last; np=++tot; rt[i]=np; step[np]=step[p]+1;
    while( p && !G[p][nx]) G[p][nx]=np,p=fail[p];
    if(!p) fail[np]=root;
    else
    {
      q=G[p][nx];
      if(step[q]==step[p]+1) fail[np]=q;
      else
      {
        nq=++tot; for(int i=0;i<=K;i++) G[nq][i]=G[q][i];
        fail[nq]=fail[q]; step[nq]=step[p]+1;
        fail[q]=fail[np]=nq;
        while(p && G[p][nx]==q) G[p][nx]=nq,p=fail[p];
      }
    }
    last=np;
  }
}Sam;

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;}

int D[Maxn];

void Dfs(int x,int fa)
{
  Sam.insert(Sam.rt[fa],x);
  for(int k=first[x];k!=-1;k=edge[k].next)
  {
    int y=edge[k].y;
    if(y==fa) continue;
    Dfs(y,x);
  }
}

int main()
{
  freopen("a.in","r",stdin);
  freopen("a.out","w",stdout);
  scanf("%d%d",&N,&K); len=0; memset(first,-1,sizeof(first));
  for(int i=1;i<=N;i++) scanf("%d",&C[i]);
  for(int i=1;i<N;i++)
  {
    int x,y; scanf("%d%d",&x,&y); ins(x,y); ins(y,x);
    D[x]++; D[y]++;
  }
  
  Sam.init(); Sam.rt[0]=Sam.root;
  for(int i=1;i<=N;i++) if(D[i]==1) Dfs(i,0);
  LL ans=0; for(int i=1;i<=Sam.tot;i++) ans+=(LL)Sam.step[i]-Sam.step[Sam.fail[i]];
  return printf("%lld\n",ans),0;
}
/*
7 3
0 2 1 2 1 0 0
1 2
3 4
3 5
4 6
5 7
2 5
*/
View Code 

 

[Spoj]8093 Sevenk Love Oimaster

 多串建后缀自动机 然后对于新建的点跳fail来更新之前的点 表示多了一个串 没有被更新过的就更新一下

虽然我感觉复杂度很玄学 这样做好像比较方便

网上有DFS+树状数组的写法?

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <map>

using namespace std;

const int Maxn=100010;

struct SAM
{
  int last,tot,root,step[Maxn*2],fail[Maxn*2],vis[Maxn*2],cnt[Maxn*2]; map<int,int>G[Maxn*2];
  void init()
  {
    last=tot=root=1;
    memset(step,0,sizeof(step));
    memset(fail,0,sizeof(fail));
  }
  void insert(int nx)
  {
    int p,np,q,nq;
    p=last; np=++tot; step[np]=step[p]+1;
    while( p && !G[p][nx]) G[p][nx]=np,p=fail[p];
    if(!p) fail[np]=root;
    else
    {
      q=G[p][nx]; if(step[q]==step[p]+1) fail[np]=q;
      else
      {
        nq=++tot; G[nq]=G[q]; cnt[nq]=cnt[q];
        fail[nq]=fail[q]; fail[q]=fail[np]=nq;
        step[nq]=step[p]+1;
        while( p && G[p][nx]==q) G[p][nx]=nq,p=fail[p];
      }
    }
    last=np;
  }
}Sam;

int N,M; char A[500010],B[500010];

int Solve()
{
  scanf("%s",B+1); int Blen=strlen(B+1);
  int u=Sam.root;
  for(int i=1;i<=Blen;i++)
  {
    u=Sam.G[u][B[i]];
    if(!u) return 0;
  }
  return Sam.cnt[u];
}

int main()
{
  
  scanf("%d%d",&N,&M); Sam.init();
  for(int i=1;i<=N;i++)
  {
    scanf("%s",A+1); int Alen=strlen(A+1); Sam.last=Sam.root;
    int L=Sam.tot+1; int R;
    for(int j=1;j<=Alen;j++) Sam.insert(A[j]);
    R=Sam.tot;
    for(int j=L;j<=R;j++)
    {
      int x=j;
      while( Sam.vis[x]!=i)
      {
        Sam.cnt[x]++; Sam.vis[x]=i;
        x=Sam.fail[x];
      }
    }
  }
  
  while(M--) printf("%d\n",Solve());
  return 0;
}
View Code

 

 2017.7.29

之前学了好多东西 我慢慢填

 

[Ahoi2009]Mincut 最小割

这道题很经典的 我慢慢写

首先对于第一问 是可能割和必须割

充要条件:一定是满流的 证明  如果不是满流的 就不是必须割 可能割也不是 证明前面或后面有一个更小的限制住?

还有满流不一定是可能流 这显然 有一些只是顺带流满而已 举个例子 三条边流量相同的二叉路?

 

然后残余网络跑一次强连通?

结论1:左右端点属于同一个联通分量的一定不是可能割 否则就是

证明 简单嘛 假如属于同一个联通分量 就有可替代边 否则没有可替代边 这样说可以行得通?

结论2:左端点和起点属于一个联通分量 右端点和终点属于一个联通分量 就是必须割 否则不是

首先必须割存在于可能割中 这一点都不矛盾 然后假设中间有其他联通分量 而不是两端一端起点 一端终点

那么就不止一条限制边了 就好像画个图 例如起点到终点有一条很长的路 有很多边 然后权值一样

清楚的二十分钟写完

 

#include <bits/stdc++.h>
using namespace std;
const int Maxn=500010;
struct node{int x,y,next,c,other;}edge[Maxn]; int len,first[4010];
void ins(int x,int y,int c)
{
  len++; int k1=len; edge[len].x=x; edge[len].y=y; edge[len].c=c; edge[len].next=first[x]; first[x]=len;
  len++; int k2=len; edge[len].x=y; edge[len].y=x; edge[len].c=0; edge[len].next=first[y]; first[y]=len;
  edge[k1].other=k2; edge[k2].other=k1;
}

int dep[4010]; queue<int>Q; int ST,ED,N,M;
bool Bfs()
{
  memset(dep,0,sizeof(dep)); dep[ST]=1;
  while(!Q.empty()) Q.pop(); Q.push(ST);
  while(!Q.empty())
  {
    int x=Q.front(); Q.pop();
    for(int k=first[x];k!=-1;k=edge[k].next)
    {
      int y=edge[k].y;
      if(dep[y]==0 && edge[k].c)
      {
        dep[y]=dep[x]+1;
        Q.push(y);
      }
    }
  }
  return dep[ED]>0;
}
int Dfs(int x,int flow)
{
  if(x==ED) return flow;
  int delta=0;
  for(int k=first[x];k!=-1;k=edge[k].next)
  {
    int y=edge[k].y;
    if(dep[y]==dep[x]+1 && edge[k].c && delta<flow)
    {
      int minf=Dfs(y,min(edge[k].c,flow-delta));
      delta+=minf; edge[k].c-=minf; edge[edge[k].other].c+=minf;
    }
  }
  if(delta==0) dep[x]=0;
  return delta;
}

int low[Maxn],dfn[Maxn],scc[Maxn],id,cnt; bool insta[Maxn]; stack<int>S;
void Dfs(int x)
{
  low[x]=dfn[x]=++id; insta[x]=1; S.push(x);
  for(int k=first[x];k!=-1;k=edge[k].next)
  {
    if(edge[k].c==0) continue;
    int y=edge[k].y;
    if(dfn[y]==-1){Dfs(y); low[x]=min(low[x],low[y]);}
    else if(insta[y]) low[x]=min(low[x],dfn[y]);
  }
  if(dfn[x]==low[x])
  {
    int i; cnt++;
    do
    {
      i=S.top(); S.pop();
      scc[i]=cnt; insta[i]=0;
    }while(i!=x);
  }
}
int main()
{
  scanf("%d%d%d%d",&N,&M,&ST,&ED); len=0; memset(first,-1,sizeof(first));
  for(int i=1;i<=M;i++)
  {
    int x,y,c; scanf("%d%d%d",&x,&y,&c); ins(x,y,c); 
  }

  while(Bfs()) if(Dfs(ST,INT_MAX));
  
  memset(low,-1,sizeof(low));
  memset(dfn,-1,sizeof(dfn));
  memset(insta,0,sizeof(insta));
  id=cnt=0;
  for(int i=1;i<=N;i++) if(dfn[i]==-1) Dfs(i);

  for(int i=1;i<=len;i+=2)
  {
    if(edge[i].c) printf("0 0\n");
    else
    {
      int x=edge[i].x; int y=edge[i].y;
      int pst=scc[ST]; int ped=scc[ED];
      int px=scc[x]; int py=scc[y];

      if(px!=py) printf("1 ");
      else printf("0 ");

      if(px==pst && py==ped) printf("1\n");
      else printf("0\n");
    }
  }
  return 0;
}
View Code

  

 

最小生成树

首先想想这条边为什么不在最小生成树上 因为从起点到终点有更小的路径咯

那么就把那些小的路径建出来 最小割一下

大的一样嘛 注意是无向图

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>

using namespace std;

const int Maxn=400010;

struct node{int x,y,next,c,other;}edge[Maxn],e[Maxn]; int len,first[Maxn];
void ins(int x,int y,int c)
{
  len++; int k1=len; edge[len].x=x; edge[len].y=y; edge[len].c=c; edge[len].next=first[x]; first[x]=len;
  len++; int k2=len; edge[len].x=y; edge[len].y=x; edge[len].c=c; edge[len].next=first[y]; first[y]=len;
  edge[k1].other=k2; edge[k2].other=k1;
}
int dep[Maxn]; queue<int>Q; int N,M,ST,ED,L;
bool Bfs()
{
  while(!Q.empty()) Q.pop(); Q.push(ST);
  memset(dep,0,sizeof(dep)); dep[ST]=1;
  while(!Q.empty())
  {
    int x=Q.front(); Q.pop();
    for(int k=first[x];k!=-1;k=edge[k].next)
    {
      int y=edge[k].y;
      if(dep[y]==0 && edge[k].c){dep[y]=dep[x]+1; Q.push(y);}
    }
  }
  return dep[ED]>0;
}
int Dfs(int x,int flow)
{
  if(x==ED) return flow;
  int delta=0;
  for(int k=first[x];k!=-1;k=edge[k].next)
  {
    int y=edge[k].y;
    if(dep[y]==dep[x]+1 && edge[k].c && flow>delta)
    {
      int minf=Dfs(y,min(edge[k].c,flow-delta));
      delta+=minf; edge[k].c-=minf; edge[edge[k].other].c+=minf;
    }
  }
  if(delta==0) dep[x]=0;
  return delta;
}

int main()
{

  scanf("%d%d",&N,&M);
  for(int i=1;i<=M;i++) scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].c);
  scanf("%d%d%d",&ST,&ED,&L);

  int ans=0,delta;

  len=0; memset(first,-1,sizeof(first));
  for(int i=1;i<=M;i++) if(e[i].c<L) ins(e[i].x,e[i].y,1);
  while(Bfs()) ans+=Dfs(ST,0x3f3f3f3f);

  len=0; memset(first,-1,sizeof(first));
  for(int i=1;i<=M;i++) if(e[i].c>L) ins(e[i].x,e[i].y,1);
  while(Bfs()) ans+=Dfs(ST,0x3f3f3f3f);

  return printf("%d\n",ans),0;
}
View Code 

 

[Apio2008]免费道路

水泥跑一边鹅卵石补全生成树重置并查集再补齐K条鹅卵石再跑水泥路

套路细心题

#include<bits/stdc++.h>
using namespace std;
const int Maxn=100010;
struct node{int x,y; node(){} node(int _x,int _y){x=_x; y=_y;} }e1[Maxn],e2[Maxn],e[Maxn]; int len1,len2,len;
int N,M,K; bool bo2[Maxn]; int fa[Maxn]; int Find(int x){return (fa[x]==x)?fa[x]:fa[x]=Find(fa[x]);}
int main()
{
  scanf("%d%d%d",&N,&M,&K); len1=len2=len=0;
  for(int i=1;i<=M;i++)
  {
    int x,y,op; scanf("%d%d%d",&x,&y,&op);
    if(op==0) e2[++len2]=node(x,y);
    if(op==1) e1[++len1]=node(x,y);
  }
  for(int i=1;i<=N;i++) fa[i]=i;
  for(int i=1;i<=len1;i++)
  {
    int xx=Find(e1[i].x); int yy=Find(e1[i].y);
    if(xx!=yy) fa[xx]=yy; 
  }
  memset(bo2,0,sizeof(bo2));
  for(int i=1;i<=len2;i++)
  {
    int xx=Find(e2[i].x); int yy=Find(e2[i].y);
    if(xx!=yy) fa[xx]=yy,e[++len]=e2[i],bo2[i]=1;
  }
  if(len>K){printf("no solution\n"); return 0;}
  for(int i=2;i<=N;i++) if(Find(i-1)!=Find(i)){printf("no solution\n"); return 0;}
  for(int i=1;i<=N;i++) fa[i]=i;
  for(int i=1;i<=len;i++){int xx=Find(e[i].x); int yy=Find(e[i].y); fa[xx]=yy;}
  for(int i=1;i<=len2;i++)
  {
    if(len==K) break;
    int xx=Find(e2[i].x); int yy=Find(e2[i].y);
    if(!bo2[i] && xx!=yy) e[++len]=e2[i],fa[xx]=yy;
  }
  if(len<K){printf("no solution\n"); return 0;}
  else for(int i=1;i<=len;i++) printf("%d %d 0\n",e[i].x,e[i].y);
  for(int i=1;i<=len1;i++)
  {
    int xx=Find(e1[i].x); int yy=Find(e1[i].y);
    if(xx!=yy) printf("%d %d 1\n",e1[i].x,e1[i].y),fa[xx]=yy;
  }
  return 0;
}
View Code

 

 2017.7.30

今天浪了一天很开心啊 见到了想见的人 然后发现暑假就要结束了 时间好快啊

不然呢 你还想怎样?期待下一个暑假(虽然也是只有一个月而已 但是下一个暑假就是高三加油了)

晚上颓废的写了一道题 慢吞吞的 而且有bug不能迅速发现 不满意

 

[Noi2017]整数

 一开始看错题了以为很麻烦 其实不呢

它有个条件 就是每时每刻必须为正数 不然我不会做

其实每一次无论加还是减影响的 也就只有比这个位大的位 小的位没关系的呢

这样就很好写了 然后记录后面1111111111111111111111111...... 000000000000000000000000.... 这样类型的出现情况就好了

那就是线段树+压位了 值得注意的就是 你把一段修改为上面的东西的时候 你要把另一个也给修改了

也就是说 你把一段改成1111111111111..... 记得把以前打0的标记删掉 不然你会死得很惨

想清楚再打好了 细节题 代码能力题(发现自己最弱这种题 要多打)

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <climits>

using namespace std;

typedef long long LL;
const int Maxn=1000010;

int lc[Maxn<<2],rc[Maxn<<2],rt,tot; LL C[Maxn<<2]; bool bo0[Maxn<<2],bo1[Maxn<<2];

void update(int u)
{
  if(bo0[lc[u]] && bo0[rc[u]]) bo0[u]=1;
  else bo0[u]=0;
  if(bo1[lc[u]] && bo1[rc[u]]) bo1[u]=1;
  else bo1[u]=0;
}
void Push_down(int u)
{
  if(bo0[u])
  {
    bo0[lc[u]]=bo0[rc[u]]=1; C[lc[u]]=C[rc[u]]=0;
    bo1[lc[u]]=bo1[rc[u]]=0;
  }
  if(bo1[u])
  {
    bo1[lc[u]]=bo1[rc[u]]=1; C[lc[u]]=C[rc[u]]=INT_MAX;
    bo0[lc[u]]=bo0[rc[u]]=0;
  }
}

void Link(int &u,int L,int R,int k)
{
  if(!u) u=++tot; bo0[u]=1,C[u]=0;
  if(L==R) return ;
  int mid=(L+R)>>1;
  if(k<=mid) Link(lc[u],L,mid,k);
  else Link(rc[u],mid+1,R,k);
}

inline int Add(int u,int L,int R,int k,int c)
{
  if(L==R)
  {
    C[u]+=c; int p=0;
    if(C[u]>INT_MAX) C[u]-=(1LL<<31),p++;
    if(C[u]<0) C[u]+=(1LL<<31),p++;

    if(C[u]==0) bo0[u]=1,bo1[u]=0;
    else if(C[u]==INT_MAX) bo1[u]=1,bo0[u]=0;
    else bo0[u]=bo1[u]=0;
    return p;
  }
  Push_down(u);
  int mid=(L+R)>>1; int p;
  if(k<=mid) p=Add(lc[u],L,mid,k,c);
  else p=Add(rc[u],mid+1,R,k,c);
  return update(u),p;
}

inline int Dfs0(int u,int L,int R)
{
  if(bo1[u]) return INT_MAX;
  if(bo0[u]) return L;
  if(L==R) return L;
  int mid=(L+R)>>1; Push_down(u);
  int ls=Dfs0(lc[u],L,mid);
  if(ls!=INT_MAX) return ls;
  else return Dfs0(rc[u],mid+1,R);
}

inline int Find0(int u,int L,int R,int l,int r)
{
  if(L==l && R==r){return Dfs0(u,L,R);}
  int mid=(L+R)>>1; Push_down(u);
  if(r<=mid) return Find0(lc[u],L,mid,l,r);
  else if(l>mid) return Find0(rc[u],mid+1,R,l,r);
  else
  {
    int ls=Find0(lc[u],L,mid,l,mid);
    if(ls==INT_MAX) return Find0(rc[u],mid+1,R,mid+1,r);
    else return ls;
  }
}

inline int Dfs1(int u,int L,int R)
{
  if(bo0[u]) return INT_MAX;
  if(bo1[u]) return L;
  if(L==R) return L;
  int mid=(L+R)>>1; Push_down(u);
  int ls=Dfs1(lc[u],L,mid);
  if(ls!=INT_MAX) return ls;
  else return Dfs1(rc[u],mid+1,R);
}

inline int Find1(int u,int L,int R,int l,int r)
{
  if(L==l && R==r){return Dfs1(u,L,R);}
  int mid=(L+R)>>1; Push_down(u);
  if(r<=mid) return Find1(lc[u],L,mid,l,r);
  else if(l>mid) return Find1(rc[u],mid+1,R,l,r);
  else
  {
    int ls=Find1(lc[u],L,mid,l,mid);
    if(ls==INT_MAX) return Find1(rc[u],mid+1,R,mid+1,r);
    else return ls;
  }
}

void Change0(int u,int L,int R,int l,int r)
{
  if(l>r) return; 
  if(L==l&& R==r){C[u]=0; bo0[u]=1; bo1[u]=0; return ;}
  int mid=(L+R)>>1; Push_down(u);
  if(r<=mid) Change0(lc[u],L,mid,l,r);
  else if(l>mid) Change0(rc[u],mid+1,R,l,r);
  else
  {
    Change0(lc[u],L,mid,l,mid);
    Change0(rc[u],mid+1,R,mid+1,r);
  }
  update(u);
}

void Change1(int u,int L,int R,int l,int r)
{
  if(l>r) return ;
  if(L==l && R==r){C[u]=INT_MAX; bo1[u]=1; bo0[u]=0; return ;}
  int mid=(L+R)>>1; Push_down(u);
  if(r<=mid) Change1(lc[u],L,mid,l,r);
  else if(l>mid) Change1(rc[u],mid+1,R,l,r);
  else
  {
    Change1(lc[u],L,mid,l,mid);
    Change1(rc[u],mid+1,R,mid+1,r);
  }
  update(u);
}

inline int Query(int u,int L,int R,int k)
{
  if(L==R) return C[u];
  int mid=(L+R)>>1; Push_down(u);
  if(k<=mid) return Query(lc[u],L,mid,k);
  else return Query(rc[u],mid+1,R,k);
}

inline int Read()
{
  int p=0,f=1; char ch=getchar();
  while(ch<'0' || ch > '9'){if(ch=='-'){f=-1;}ch=getchar();}
  while(ch>='0' && ch<='9'){p=p*10+(ch-'0'); ch=getchar();}
  return p*f;
}

int N,t1,t2,t3;

int main()
{

  rt=tot=0;
  for(int i=1;i<=1000000;i++) Link(rt,1,1000000,i);

  N=Read(); Read(); Read(); Read();
  while(N--)
  {
    int opt; LL a; int b; opt=Read();
    if(opt==1)
    {
      a=Read(); b=Read();
      if(a==0) continue;
      if(a>0)
      {
        int p=b/31+1; int bit=b%31; a<<=bit;
        int q=Add(rt,1,1000000,p,a%(1LL<<31));
        a>>=31; a+=q;
        if(a)
        {
          p++; bit=0;
          q=Add(rt,1,1000000,p,a);
        }

        if(q)
        {
          int nx=Find0(rt,1,1000000,p+1,1000000);
          Change0(rt,1,1000000,p+1,nx-1);
          Add(rt,1,1000000,nx,1);
        }
      }
      else
      {
        a=-a;
        int p=b/31+1; int bit=b%31; a<<=bit;
        int q=Add(rt,1,1000000,p,-(a%(1LL<<31)));
        a>>=31; a+=q;
        if(a)
        {
          p++;  bit=0;
          q=Add(rt,1,1000000,p,-a);
        }

        if(q)
        {
          int nx=Find1(rt,1,1000000,p+1,1000000);
          Change1(rt,1,1000000,p+1,nx-1);
          Add(rt,1,1000000,nx,-1);
        }
      }
    }
    else
    {
      b=Read(); int p=b/31+1; int bit=b%31;
      int ans=Query(rt,1,1000000,p);
      printf("%d\n",((ans%(1<<(bit+1)))>>bit)?1:0);
    }
  }

  return 0;
}

/*
10 3 1 2
1 100 0
1 2333 0
1 -233 0
2 5
2 7
2 15
1 5 15
2 15
1 -1 12
2 15
*/
View Code

 

 

2017.8.8

之前在雅礼集训有点恶心 天天测试

今天难得放假 写了一道题

也很恶心

 

[Poi2004]Gra

这道题膜了别人好久啊

首先第一个 如果是一堆石子的位置 随便挑出一堆中的一个往右移 你发现每次向右移 在这堆石子中 这个石子左边的石子 右边的白格数目不变 右边的石子 右边的白格数目$-1$

然后发现聪明的人都不会把石子放到$M-1$这个位置上面  不然别人就可以到达$M$了

考虑所有石子都在$M-N-1$到$M-2$这个范围内 是结束状态 谁完成这个状态就必定胜利

这样的话 就想到了阶梯博弈 每次就是把一堆中选一个 包括自己和右边的石子都向下移动一个阶梯

这样的话就解决问题 注意初始$M-1$位置有石子的 先手也必定可以通过移这个石子胜利

阶梯博弈转化为$NIM$游戏 现在是要求$NIM$游戏先手第一次可以挪的棋子 并且以后必胜

当$xor$和不为$0$时才有解 不然$printf 0$

考虑:

当阶梯为奇数时: 这时的石子有$B[i]$需要搞掉使得$xor$和为$0$ 也就是要满足

$B[i]- SG xor B[i] \geq 0$

也就是要够取掉使得$xor$和为$0$

当阶梯为偶数时:这时的石子有$B[i]$需要使得下一个阶梯的石子添多 使得$xor$和为$0$

$-B[i] \leq B[i+1] - SG xor B[i+1] \leq 0$ $B[i-1]$为下一个阶梯的石子数

然后就完成了 考虑阶梯的时候 如果之间只有一个白格 那么$B[i]$就隔一个位置 因为下了之后就和之前的石子合并啊

要不中间有很多个的话 $B[i]$就隔两个或者三个格子 因为可以下了之后再下一次啊 要分奇数和偶数

#include <bits/stdc++.h>
using namespace std;
const int Maxn=1e6+10;
int A[Maxn]; int p[Maxn]; map<int,int>cnt; int B[Maxn];
int main()
{
  int N,M; scanf("%d%d",&M,&N); cnt.clear();
  for(int i=1;i<=N;i++) scanf("%d",&A[i]);
  
  if(A[N]==M-1)
  {
    int ans=1; for(int j=N-1;j>=1;j--) if(A[j+1]-A[j]==1) ans++; else break;
    printf("%d\n",ans); return 0;
  }

  int idx=0; A[N+1]=M-1;
  for(int i=N;i>=1;i--)
  {
    if(A[i]+1==A[i+1]) B[idx]++;
    else if(A[i]+2 ==A[i+1]) B[++idx]++;
    else if((A[i+1]-A[i]-1) &1){idx+=3; B[idx]++;}
    else {idx+=2; B[idx]++;}
  }

  
  int xo=0; int ans=0;
  for(int i=1;i<=idx;i+=2) xo^=B[i];
  if(xo)
  {
    for(int i=1;i<=idx;i+=2) if((B[i] ^ xo) < B[i]) ans++;
    for(int i=2;i<=idx;i+=2) if( ((B[i-1]^xo) - (B[i-1]) <= B[i]) && ((B[i-1]^xo) - (B[i-1]) >0) ) ans++;
    printf("%d\n",ans);
  }
  else printf("0\n");
  return 0;
}
View Code

 

2017.8.17

 

[HNOI2007]紧急疏散evacuate

经典的匹配问题 这道题我写的是匈牙利算法

首先每个门每一秒钟只能通过一个人 我们考虑先二分答案 就是二分时间

然后把门给拆点 让门和人匹配

用BFS找出门与人的距离(这里把门扔进去比较方便)

注意 如果一个门被另外一个门挡着的情况 就把另外一个门给看成一个障碍 因为每次经过另外一个门都要等一秒钟 不如就从这个门出去

然后空间的话自己算算

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>

using namespace std;

const int Maxn=25;

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

char mp[Maxn][Maxn]; int N,M;

int dis[Maxn*Maxn][Maxn*Maxn]; int id[Maxn][Maxn];
int dx[5]={-1,0,1,0};
int dy[5]={0,1,0,-1};

vector<int>V,G[Maxn*Maxn];

queue<int>Q; bool bo[Maxn*Maxn]; int Dis[Maxn*Maxn][Maxn*Maxn];

void SPFA(int ST)
{
  while(!Q.empty()) Q.pop(); Q.push(ST);
  memset(bo,0,sizeof(bo)); bo[ST]=1;
  memset(Dis[ST],63,sizeof(Dis[ST])); Dis[ST][ST]=0;
  while(!Q.empty())
  {
    int x=Q.front();
    for(int k=first[x];k!=-1;k=edge[k].next)
    {
      int y=edge[k].y;
      if(Dis[ST][y]>Dis[ST][x]+edge[k].d)
      {
        Dis[ST][y]=Dis[ST][x]+edge[k].d;
        if(!bo[y]){bo[y]=1; Q.push(y);}
      }
    }
    Q.pop(); bo[x]=false;
  }
}

int idx[Maxn*Maxn][Maxn*Maxn*Maxn]; int p=0; int tot=0;

int match[Maxn*Maxn*Maxn*4]; bool chw[Maxn*Maxn*Maxn*4];

bool Dfs(int x)
{
  for(int i=0;i<G[x].size();i++)
  {
    int y=G[x][i];
    if(!chw[y])
    {
      chw[y]=1;
      if((match[y]==0) || Dfs(match[y])){match[y]=x; return true;}
    }
  }
  return false;
}

bool Check(int t)
{
  p=0; for(int i=0;i<V.size();i++) for(int T=1;T<=t;T++) idx[i][T]=++p;
  
  for(int x=1;x<=N;x++) for(int y=1;y<=M;y++) G[id[x][y]].clear();
  
  for(int i=0;i<V.size();i++)
  {
    for(int x=1;x<=N;x++) for(int y=1;y<=M;y++) if(mp[x][y]=='.')
    {
      for(int T=Dis[V[i]][id[x][y]];T<=t;T++)
        G[id[x][y]].push_back(idx[i][T]);
    }
  }

  /*
  for(int x=1;x<=N;x++) for(int y=1;y<=M;y++) if(mp[x][y]=='.')
  {
    for(int j=0;j<G[id[x][y]].size();j++) printf("%d ",G[id[x][y]][j]);
    printf("\n");
  }*/
  
  //printf("\n");
  
  int s=0; for(int i=1;i<=p;i++) match[i]=0;
  for(int x=1;x<=N;x++) for(int y=1;y<=M;y++) if(mp[x][y]=='.')
  {
    for(int j=1;j<=p;j++) chw[j]=0;
    if(Dfs(id[x][y])) s++;
    //else printf("%d %d\n",x,y);
  }
  
  //printf("%d\n",s);
  return (s==tot); 
}

int main()
{
  
  scanf("%d%d",&N,&M); len=0; memset(first,-1,sizeof(first));
  
  for(int i=1;i<=N;i++) scanf("%s",mp[i]+1);
  for(int i=1;i<=N;i++) for(int j=1;j<=M;j++) id[i][j]=(i-1)*M+j;
  
  tot=0;
  for(int i=1;i<=N;i++) for(int j=1;j<=M;j++) if(mp[i][j]!='X')
  {
    tot++;
    for(int k=0;k<4;k++)
    {
      int x=i+dx[k]; int y=j+dy[k];
      if(x>0 && y>0 && x<=N && y<=M && mp[x][y]=='.')
        ins(id[i][j],id[x][y],1);
    }
    if(mp[i][j]=='D') V.push_back(id[i][j]),tot--;
  }
  
  for(int i=0;i<V.size();i++) SPFA(V[i]);
  
  int L=1; int R=400; int ret=-1;
  while(L<=R)
  {
    int mid=(L+R)>>1;
    if(Check(mid)){ret=mid; R=mid-1;}
    else L=mid+1;
  }// Check(2);
  
  if(ret==-1) puts("impossible");
  else printf("%d\n",ret);
  
  return 0;
}
View Code

 

 

Number

首先 两个奇数或者两个偶数肯定不满足两个条件中的一个 所以可以同时选 下面给出证明

 

两个偶数不满足 $GCD(x,y)=1$ 显然他们的$GCD$至少是2

两个奇数不满足 $A^2+B^2=C^2$

不妨设两个数分别为$2n+1$$2m+1$

$(2n+1)(2n+1) + (2m+1)(2m+1)$

$=4(n^2 + n + m^2 +m)+2 = c^2$

$\therefore 2|c^2$

因为c是质数

$\therefore 2|c$

所以我们设$c=2k+1$

$4(n^2 + n + m^2 +m)+2= 4k^2$

$(n^2+ n +m^2 +m) + \frac{1}{2} = k^2$

显然不存在正整数k满足 证毕

 

因为我们是要求一个最大权值的一个集合 使得里面的点互相可以选

这样我们知道上面的结论之后 不妨想想

如果做费用流 很难保证只有一个集合

所以先把所有数都放在一个集合里面然后踢贡献最小的?

这就是最小割问题 把奇数放一边 偶数放一边 不可以同时选的连一条inf 的边就好了

#include <bits/stdc++.h>
using namespace std;
const int Maxn=3010;
struct node{int x,y,next,c,other;}edge[Maxn*Maxn/2]; int len,first[Maxn*2];
void ins(int x,int y,int c)
{
  len++; int k1=len; edge[len].x=x; edge[len].y=y; edge[len].c=c; edge[len].next=first[x]; first[x]=len;
  len++; int k2=len; edge[len].x=y; edge[len].y=x; edge[len].c=0; edge[len].next=first[y]; first[y]=len;
  edge[k1].other=k2; edge[k2].other=k1;
}
int dep[Maxn*3]; queue<int>Q; int ST,ED;
bool BFS()
{
  memset(dep,0,sizeof(dep)); dep[ST]=1;
  while(!Q.empty()) Q.pop(); Q.push(ST);
  while(!Q.empty())
  {
    int x=Q.front(); Q.pop();
    for(int k=first[x];k!=-1;k=edge[k].next)
    {
      int y=edge[k].y;
      if(dep[y]==0 && edge[k].c)
      {
        dep[y]=dep[x]+1; Q.push(y);
      }
    }
  }
  return dep[ED]>0;
}
int DFS(int x,int flow)
{
  if(x==ED) return flow;
  int delta=0;
  for(int k=first[x];k!=-1;k=edge[k].next)
  {
    int y=edge[k].y;
    if(edge[k].c && dep[y]==dep[x]+1 && flow>delta)
    {
      int minf=DFS(y,min(flow-delta,edge[k].c));
      edge[k].c-=minf; edge[edge[k].other].c+=minf;
      delta+=minf;
    }
  }
  if(delta==0) dep[x]=0;
  return delta;
}
 
int gcd(int x,int y){if(y==0) return x; else return gcd(y,x%y);}
 
int A[Maxn],B[Maxn],Alen,Blen; int N,a[Maxn];
int main()
{
   
  len=0; memset(first,-1,sizeof(first));
   
  scanf("%d",&N); int tot=0; for(int i=1;i<=N;i++) scanf("%d",&a[i]),tot+=a[i];
  Alen=Blen=0; for(int i=1;i<=N;i++){if(a[i]%2==0) A[++Alen]=a[i]; else B[++Blen]=a[i];}
  ST=N+1; ED=N+2;
   
  for(int i=1;i<=Alen;i++) ins(ST,i,A[i]);
  for(int i=1;i<=Blen;i++) ins(Alen+i,ED,B[i]);
  for(int i=1;i<=Alen;i++) for(int j=1;j<=Blen;j++)
    if((gcd(A[i],B[j])==1) && (((int)sqrt(A[i]*A[i]+B[j]*B[j])) * ((int)sqrt(A[i]*A[i]+B[j]*B[j])) == (A[i]*A[i]+B[j]*B[j])) )
      ins(i,Alen+j,0x3f3f3f3f);
   
  int ans=0,delta;
  while(BFS()){if(delta=DFS(ST,0x3f3f3f3f)) ans+=delta;}
  return printf("%d\n",tot-ans),0;
}
View Code

  

 

[HAOI2008]木棍分割

第一问二分就好了 就是可以分成M+1块

第二问dp维护个前缀和

 

#include <bits/stdc++.h>
using namespace std;
const int Maxn=100010;
const int Mod=10007;
int N,M,D[Maxn];

bool Check(int k)
{
  int ans=1; int s=0;
  for(int i=1;i<=N;i++)
  {
    if(s+D[i]<=k) s+=D[i];
    else s=D[i],ans++;
  }
  if(ans<=M+1) return 1;
  else return 0;
}

int F[Maxn]; int G[Maxn]; int Sum[Maxn];

int main()
{
  scanf("%d%d",&N,&M);
  
  int L=1; int R=50000000; int ret;
  for(int i=1;i<=N;i++) scanf("%d",&D[i]),L=max(D[i],L);
  
  while(L<=R)
  {
    int mid=(L+R)>>1;
    if(Check(mid)) ret=mid,R=mid-1;
    else L=mid+1;
  }
  
  printf("%d ",ret);
  
  Sum[0]=0; for(int i=1;i<=N;i++) Sum[i]=Sum[i-1]+D[i];
  memset(G,0,sizeof(G)); G[0]=1;
  
  int ans=0;
  for(int i=0;i<=M;i++)
  {
    int last=0; int s=G[0];
    for(int j=1;j<=N;j++)
    {
      while(Sum[j]-Sum[last] > ret) s=(s-G[last]+Mod)%Mod,last++;
      F[j]=s; s=(s+G[j])%Mod;
    }

    ans=(ans+F[N])%Mod;
    for(int j=0;j<=N;j++) G[j]=F[j],F[j]=0;
  }
  
  return printf("%d\n",ans),0;
}
View Code

 

 2017.8.18

 

[Noi2014]动物园

很好的题 加深了我对字符串的印象

一开始看错题目 要求前i个字符串里面后缀和前缀相同的个数

一想到后缀和前缀 就应该想到$kmp$

想想 每一次相同串个数肯定是$cnt[i]=cnt[fail[i]]+1$ 画画图就知道了

然后就简单了 我们发现题目还有一个条件 就是要不重复 就是不能超过一半 然后再用另外一个来跳就好了

注意$cnt$是可重复的子串 前缀和后缀相同的个数 每次跳到一半之前的$cnt$即可

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdlib>
#include <cmath>

using namespace std;
typedef long long LL;

const int Maxn=1000010;
const int Mod=1e9+7;

char A[Maxn]; int Tcase;
int p[Maxn]; int cnt[Maxn];

int main()
{
  
  scanf("%d",&Tcase);
  while(Tcase--)
  {
    scanf("%s",A+1); int len=strlen(A+1); LL ans=1;
    memset(p,0,sizeof(p)); int j=0; p[1]=0; cnt[0]=0; cnt[1]=1; int k=0;
    for(int i=2;i<=len;i++)
    {
      while(j>0 && A[j+1]!=A[i] ) j=p[j];
      if(A[j+1]==A[i]) j++;
      p[i]=j; cnt[i]=cnt[j]+1;
      
      while(k>0 && ((A[k+1]!=A[i]) || ((k+1)*2>i)) ) k=p[k];
      if(A[k+1]==A[i]) k++;
      ans=ans*(cnt[k]+1)%Mod;
    }
    /*
    for(int i=1;i<=len;i++) printf("%d ",cnt[i]);
    printf("\n");*/
    printf("%lld\n",ans);
  }
  return 0;
}

/*
3
aaaaa
ab
abcababc
*/
View Code

下午打了下百度之星用OZY的号 乱搞了两题出来?

 

2017.8.20

[BeiJing2011]禁忌

经典的AC自动机+DP+矩阵乘法 很简单的$F[i][j]$表示第j个点走了几步 然后走到底下的点就返回根

然而因为我垃圾所以打了很久?

#include <cstdio>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <vector>
 
using namespace std;
const int Maxn=1010;
struct node{int ch[27],fail; bool bo;}tr[Maxn]; int rot,tot;
 
int N,len,alphabet; char str[Maxn];
 
void Build()
{
  int l=strlen(str+1); int u=rot;
  for(int i=1;i<=l;i++)
  {
    int nx=str[i]-'a'+1;
    if(tr[u].ch[nx]==0) tr[u].ch[nx]=++tot;
    u=tr[u].ch[nx];
  }
  tr[u].bo=1;
}
 
queue<int>Q;
void AC_Tire()
{
  while(!Q.empty()) Q.pop(); Q.push(rot);
  while(!Q.empty())
  {
    int u=Q.front(); Q.pop();
    for(int i=1;i<=alphabet;i++)
    {
      if(tr[u].ch[i])
      {
        if(u==rot) tr[tr[u].ch[i]].fail=rot;
        else tr[tr[u].ch[i]].fail=tr[tr[u].fail].ch[i];
         
        tr[tr[u].ch[i]].bo|=tr[tr[tr[u].ch[i]].fail].bo;
         
        Q.push(tr[u].ch[i]);
      }
      else
      {
        if(u==rot) tr[u].ch[i]=rot;
        else tr[u].ch[i]=tr[tr[u].fail].ch[i];
      }
    }
  }
}
 
struct Matrix
{
  long double A[80][80];
  Matrix(){memset(A,0,sizeof(A)); }
  friend Matrix operator *(Matrix x,Matrix y)
  {
    Matrix z;
    for(int i=0;i<=tot;i++) for(int j=0;j<=tot;j++) for(int k=0;k<=tot;k++)
      z.A[i][j]+=x.A[i][k]*y.A[k][j];
    return z;
  }
  friend Matrix operator ^(Matrix x,int k)
  {
    Matrix z; for(int i=0;i<=tot;i++) z.A[i][i]=1.0;
    while(k)
    {
      if(k&1) z=z*x;
      x=x*x; k>>=1;
    }
    return z;
  }
}C,Ans;
 
void DP()
{
  C.A[0][rot]=1.0; Ans.A[0][0]=1.0; long double tmp=(1.0/alphabet);
  for(int i=1;i<=tot;i++)
  {
    for(int k=1;k<=alphabet;k++)
    {
      int nx=tr[i].ch[k];
      if(tr[nx].bo) Ans.A[i][0]+=tmp,Ans.A[i][rot]+=tmp;
      else Ans.A[i][nx]+=tmp;
    }
  }
  Ans=C*(Ans^len);
}
 
/*
long double F[Maxn][Maxn],ans;
void DP()
{
  for(int i=0;i<=tot;i++) for(int j=0;j<=tot;j++) F[i][j]=0.0; F[0][rot]=1.0;
  for(int i=1;i<=len;i++)
  {
    for(int j=1;j<=tot;j++)
    {
      if(!tr[j].bo)
      {
        for(int k=1;k<=alphabet;k++)
          if(F[i-1][j]) F[i][tr[j].ch[k]]+=F[i-1][j]*(1.0/alphabet);
      }
      else
      {
        for(int k=1;k<=alphabet;k++)
          if(F[i-1][j]) F[i][tr[rot].ch[k]]+=F[i-1][j]*(1.0/alphabet);
      }
    }
    for(int j=1;j<=tot;j++) if(tr[j].bo) ans+=F[i][j];
  }
}*/
 
int main()
{
  scanf("%d%d%d",&N,&len,&alphabet);
   
  rot=tot=1;
  for(int i=1;i<=N;i++)
  {
    scanf("%s",str+1);
    Build();
  }
   
  AC_Tire();
  DP();
  return printf("%.9f\n",(double) Ans.A[0][0]),0;
}
View Code

 

 

Spoj 839 Optimal Marks

笑闻哈老师秒题??

首先按位搞 这很明显吧 那么整个图就变成了有很多0/1 的点 还有一下你需要编号的点 使得0/1连边最少

然后就不会做了

其实是很简单的最小割 已经确定的每个点和起点联通就是0 和终点联通就是1 两个流量都是无限大 连边的点互相连边

那些要进行选择的点 就不用管它 它肯定是割掉属于一个集合的边 看他自己割哪个集合 割掉就是代价 就算是两个要选择的点相连也是这样的

然后要求点权和最小 你可以跑一次最小费用最小流 也可以用搞Mod的做法把流量同时表示两个东西

 

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
 
using namespace std;
typedef long long LL;
 
const int Maxn=1010;
const int Maxm=2010;
const int Mod=10000;
const int inf=1000000007;
 
struct EDGE{int x,y,c,next,other;}edge[Maxm*8]; int len,first[Maxn];
void ins(int x,int y,int c)
{
  len++; int k1=len; edge[len].x=x; edge[len].y=y; edge[len].c=c; edge[len].next=first[x]; first[x]=len;
  len++; int k2=len; edge[len].x=y; edge[len].y=x; edge[len].c=0; edge[len].next=first[y]; first[y]=len;
  edge[k1].other=k2; edge[k2].other=k1;
}
int dep[Maxn]; queue<int>Q; int ST,ED;
bool BFS()
{
  while(!Q.empty()) Q.pop(); Q.push(ST);
  memset(dep,0,sizeof(dep)); dep[ST]=1;
  while(!Q.empty())
  {
    int x=Q.front();
    for(int k=first[x];k!=-1;k=edge[k].next)
    {
      int y=edge[k].y;
      if(dep[y]==0 && edge[k].c)
      {
        dep[y]=dep[x]+1;
        Q.push(y);
      }
    }
    Q.pop();
  }
  return dep[ED]>0;
}
int DFS(int x,int flow)
{
  if(x==ED) return flow;
  int delta=0;
  for(int k=first[x];k!=-1;k=edge[k].next)
  {
    int y=edge[k].y;
    if(dep[y]==dep[x]+1 && edge[k].c && flow>delta)
    {
      int minf=DFS(y,min(flow-delta,edge[k].c));
      delta+=minf; edge[k].c-=minf; edge[edge[k].other].c+=minf;
    }
  }
  if(delta==0) dep[x]=0;
  return delta;
}
 
 
int N,M,A[Maxn]; pair<int,int>pr[Maxm]; LL ans=0;
 
void Build(int k)
{
  len=0; memset(first,-1,sizeof(first)); ST=N+1; ED=N+2;
  for(int i=1;i<=N;i++)
  {
    if(A[i]>=0)
    {
      if(A[i]&(1<<k)) ins(i,ED,inf);
      else ins(ST,i,inf);
    }
    ins(ST,i,1);
  }
  for(int i=1;i<=M;i++) ins(pr[i].first,pr[i].second,Mod),ins(pr[i].second,pr[i].first,Mod);
}
 
 
void Dinic()
{
  ans=0; int delta=0;
  while(BFS()){if(delta=DFS(ST,inf)) ans+=delta;}
}
 
int main()
{
   
  scanf("%d%d",&N,&M);
  for(int i=1;i<=N;i++) scanf("%d",&A[i]);
  for(int i=1;i<=M;i++) scanf("%d%d",&pr[i].first,&pr[i].second);
   
  LL ans1=0; LL ans2=0;
  for(int k=0;k<=30;k++)
  {
    Build(k);
    Dinic();
    ans1+=(ans/Mod)*(1<<k);
    ans2+=(ans%Mod)*(1<<k);
  }
  return printf("%lld\n%lld\n",ans1,ans2),0;
}
View Code

 

2017.8.20

[Hnoi2010]Bus 公交线路

就是很快想到状态压缩 然后1024开不下就只存有用的状态

1.首先不用管先后问题 这个很重要 所有的车只可能在P个格子里面 你可以这样想让快的车等一下? 如果不在P个格子里面 或者重合的肯定不是合法状态

所以只要最快的车等一下 一定都在P个格子中

2.当第一个格子是1时 这个状态才正确 这是为了去重 因为每一个格子都要走过嘛 所以从这个格子走到下个格子的状态 两个状态必须第一个格子为1

考虑第一个格子不为1的情况 都是由之前的格子为1情况转移过来 同时也会在以后被表示出来

3.还有一个问题 不用进行层内转移 因为你每一次层外转移 都是把最前面的1挪到后面去 所以每一次层外转移造就了每辆车都会被挪到另外一个位置去

打个比方:

111000 -> 110010

其实看上去就是第三辆车挪到第五个位置 其实这一状态会等到第三次转移时的第一个位为1的状态转移 

然后就没有了

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int Maxn=128;
const int Mod=30031;
 
int Hash[1025]; int p[Maxn],plen; int N,K,P; int bit[11];
struct Matrix
{
  int A[Maxn][Maxn];
  Matrix(){memset(A,0,sizeof(A));}
  friend Matrix operator *(Matrix x,Matrix y)
  {
    Matrix z;
    for(int i=1;i<=plen;i++) for(int j=1;j<=plen;j++) for(int k=1;k<=plen;k++)
      (z.A[i][j]+=x.A[i][k]*y.A[k][j])%=Mod;
    return z;
  }
  friend Matrix operator ^(Matrix x,int k)
  {
    Matrix z; for(int i=1;i<=plen;i++) z.A[i][i]=1;
    while(k)
    {
      if(k&1) z=z*x;
      k>>=1; x=x*x;
    }
    return z;
  }
}C,Ans;
 
 
int main()
{
  scanf("%d%d%d",&N,&K,&P);
   
  for(int i=1;i<=P+1;i++) bit[i]=(1<<(i-1));
   
  plen=0;
  for(int i=0;i<bit[P+1];i++)
  {
    if(!(i&bit[1])) continue;
    int s=0; for(int j=1;j<=P;j++) if(i&bit[j]) s++;
    if(s==K) p[++plen]=i,Hash[i]=plen;
  }
   
  C.A[1][Hash[bit[K+1]-1]]=1;
  for(int i=1;i<=plen;i++)
  {
    int nsta=p[i]>>1;
    for(int j=1;j<=P;j++) if((nsta&bit[j])==0)
    {
      int nnsta=nsta|bit[j];
      if(Hash[nnsta]) Ans.A[i][Hash[nnsta]]++;
    }
  }
 
  return printf("%d\n",(C*(Ans^(N-K))).A[1][Hash[bit[K+1]-1]]),0;
}
View Code

 

 

2017.8.21

[Sdoi2014]旅行

对于每个宗教开线段树 然后树剖裸题

 

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int Maxn=100010;
struct EDGE{int x,y,next;}edge[Maxn*2]; 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;}
int dep[Maxn],dfn[Maxn],fa[Maxn],siz[Maxn],son[Maxn],top[Maxn],id;
 
int tot,rt[Maxn],lc[Maxn*80],rc[Maxn*80],C1[Maxn*80],C2[Maxn*80];
void Link(int &u,int L,int R,int k,int c)
{
  if(!u) u=++tot;
  if(L==R){C1[u]=c; C2[u]=C1[u]; return ;}
  int mid=(L+R)>>1;
  if(k<=mid) Link(lc[u],L,mid,k,c);
  else Link(rc[u],mid+1,R,k,c);
  C1[u]=C1[lc[u]]+C1[rc[u]];
  C2[u]=max(C2[lc[u]],C2[rc[u]]);
}
 
int N,M; pair<int,int>pr[Maxn];
 
void Dfs1(int x,int f)
{
  siz[x]=1; son[x]=0;
  for(int k=first[x];k!=-1;k=edge[k].next)
  {
    int y=edge[k].y;
    if(y==f) continue;
    dep[y]=dep[x]+1;
    fa[y]=x;
    Dfs1(y,x);
    siz[x]+=siz[y];
    if(siz[y] > siz[son[x]]) son[x]=y;
  }
}
 
void Dfs2(int x,int tp)
{
  dfn[x]=++id; top[x]=tp;
  if(son[x]) Dfs2(son[x],tp);
  for(int k=first[x];k!=-1;k=edge[k].next)
  {
    int y=edge[k].y;
    if(y==fa[x] || y==son[x]) continue;
    Dfs2(y,y);
  }
}
 
int Q1(int u,int L,int R,int l,int r)
{
  if(L==l && R==r) return C1[u];
  int mid=(L+R)>>1;
  if(r<=mid) return Q1(lc[u],L,mid,l,r);
  else if(l>mid) return Q1(rc[u],mid+1,R,l,r);
  else return Q1(lc[u],L,mid,l,mid)+Q1(rc[u],mid+1,R,mid+1,r);
}
int Q2(int u,int L,int R,int l,int r)
{
  if(L==l && R==r) return C2[u];
  int mid=(L+R)>>1;
  if(r<=mid) return Q2(lc[u],L,mid,l,r);
  else if(l>mid) return Q2(rc[u],mid+1,R,l,r);
  else return max(Q2(lc[u],L,mid,l,mid),Q2(rc[u],mid+1,R,mid+1,r));
}
 
int Query1(int x,int y,int c)
{
  int tx=top[x]; int ty=top[y]; int ans=0;
  while(tx!=ty)
  {
    if(dep[tx]<dep[ty]) swap(tx,ty),swap(x,y);
    ans+=Q1(rt[c],1,id,dfn[tx],dfn[x]);
    x=fa[tx]; tx=top[x];
  }
  if(dep[x]<dep[y]) swap(x,y);
  return ans+Q1(rt[c],1,id,dfn[y],dfn[x]);
}
 
int Query2(int x,int y,int c)
{
  int tx=top[x]; int ty=top[y]; int ans=0;
  while(tx!=ty)
  {
    if(dep[tx]<dep[ty]) swap(tx,ty),swap(x,y);
    ans=max(ans,Q2(rt[c],1,id,dfn[tx],dfn[x]));
    x=fa[tx]; tx=top[x];
  }
  if(dep[x]<dep[y]) swap(x,y);
  return max(ans,Q2(rt[c],1,id,dfn[y],dfn[x]));
}
 
inline int Read()
{
  char ch=getchar(); int p=0;
  while(ch<'0' || ch>'9') ch=getchar();
  while(ch>='0' && ch<='9') p=p*10+(ch-'0'),ch=getchar();
  return p; 
}
 
int main()
{
  N=Read(); M=Read(); len=0; memset(first,-1,sizeof(first));
  for(int i=1;i<=N;i++) pr[i].first=Read(),pr[i].second=Read();
  for(int i=1;i<N;i++){int x,y; x=Read(); y=Read(); ins(x,y); ins(y,x);}
   
  memset(dep,0,sizeof(dep)); dep[1]=1; Dfs1(1,0);
  id=0; Dfs2(1,1);
   
  memset(C1,0,sizeof(C1)); memset(C2,0,sizeof(C2));
  for(int i=1;i<=N;i++) Link(rt[pr[i].second],1,id,dfn[i],pr[i].first);
   
  for(int i=1;i<=M;i++)
  {
    char ch[10]; scanf("%s",ch+1); int x,y; x=Read(); y=Read();
    if(ch[1]=='Q' && ch[2]=='S')
    {
      printf("%d\n",Query1(x,y,pr[x].second));
    }
    else if(ch[1]=='C' && ch[2]=='C')
    {
      Link(rt[pr[x].second],1,id,dfn[x],0);
      pr[x].second=y;
      Link(rt[pr[x].second],1,id,dfn[x],pr[x].first);
    }
    else if(ch[1]=='Q' && ch[2]=='M')
    {
      printf("%d\n",Query2(x,y,pr[x].second));
    }
    else if(ch[1]=='C' && ch[2]=='W')
    {
      pr[x].first=y;
      Link(rt[pr[x].second],1,id,dfn[x],pr[x].first);
    }
  }
  return 0;
}
View Code

 

Hash Killer IV

很搞笑的一道题 它的Hash会溢出

就反着做嘛 做的时候 有一些位确定的 先把这些位给搞了 后面补够三十二个位就AC了

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
 
LL bit[65]; LL b[65];
 
LL Jia(LL t,LL p)
{
  LL l=0; for(LL i=31;i>=0;i--) if(t>=bit[i]){l=i; break;}
  for(LL i=0;i<p;i++) b[i]=(t>>i)&1; LL last=0;
  for(LL i=p;i<=31;i++)
  {
    int now=(t>>i)&1;
    b[i]=(now-(b[i-p]+last)+2)&1;
    if(now<(b[i-p]+last)) last=1;
    else last=0;
  }
   
  t=0; for(LL i=31;i>=0;i--) t+=b[i]*bit[i];
  return t;
}
 
LL Mul(LL t,LL p)
{
  if(t>=bit[p])
  {
    LL l=0; for(LL i=31;i>=0;i--) if(t>=bit[i]){l=i; break;}
    for(LL i=l;i>l-p;i--) b[i]=(t>>i)&1;
    for(LL i=l-p;i>=0;i--)
    {
      LL now=(t>>i)&1;
      b[i]=now^b[i+p];
    }
    t=0; for(LL i=0;i<=l;i++) t=t|(b[i]<<i);
  }
  return t;
}
 
int main()
{
  LL Q; scanf("%lld",&Q); for(LL i=0;i<=32;i++) bit[i]=(1LL<<i);
  while(Q--)
  {
    LL t; scanf("%lld",&t);
    t=Jia(t,16);
    t=Mul(t,11);
    t=Jia(t,3);
    t=Mul(t,6);
    t=Jia(t,10);
    printf("%lld\n",t);
  }
  return 0;
}
View Code

 

2017.8.22

 

Karp-de-Chant Number

 首先对于每一个串 我们考虑是括号序列 自然1 -1标号统计前缀和 对于有用的 只是全部的前缀和 和某一时刻前缀和最小的 不妨设两个值为x和y

 

对于x>0 y>0 只要前面合法随便放即可 对吧

对于x>0 y<0 那这些也能放就尽量放 很显然吧 对后面的贡献也是会有的 那么我们肯定挑选出y比较大的先放 这样的话给y比较小的提供机会?

对于x<0 y>0 这种情况不存在 因为y是所有时刻最小的 一定有x>y

对于x<0 y<0 这种情况比较复杂 我们考虑到背包 ?  但是直接背包好像是会有错的

他们之间存在一个先后问题

比如说 就是你先放的x1 有可能卡住后面的y2 但是你放的x2 卡不住y1

所以我们需要排个序

对于两个序列

x1 y1

x2 y2

对于之前剩下的L  和两个放完之后的状态都是一样的 那么就有

L+x1+y2  L+x2+y1

就是先放第一个还是先放第二个

如果结果是L+x1+y2 > L+x2+y1

那么就是先放第一个比较优 其实这个式子可以变成 x1-y1>x2-y2

然后确定了顺序就可以背包?

注意一下这里只是背包 不是直接取 很容易举出直接取是有反例的 因为有一些序列不一定排序在前就优 只是说排序在前的先放 排序后面的后放会避免前面的不必要的被卡住的情况

 

#include <bits/stdc++.h>
using namespace std;
const int Maxn=310;
struct node
{
  int x,y,L;
  node(){};
  node(int _x,int _y,int _L){x=_x; y=_y; L=_L;}
};
node pr[Maxn],A[Maxn],B[Maxn]; int N,Alen,Blen;
char st[Maxn];
bool Cmp1(const node &x,const node &y){return x.x>y.x;}
bool Cmp2(const node &x,const node &y){return x.y-x.x>y.y-y.x;}
 
int F[Maxn*Maxn/2]; int G[Maxn*Maxn/2];
 
int main()
{
  scanf("%d",&N);
  for(int i=1;i<=N;i++)
  {
    scanf("%s",st+1); int len=strlen(st+1);
    int x,y; x=INT_MAX; int now=0;
    for(int j=1;j<=len;j++)
    {
      if(st[j]=='(') now++; else now--;
      if(now<x) x=now;
    }
    y=now; pr[i]=node(x,y,len);
  }
  Alen=Blen=0; for(int i=1;i<=N;i++)
  {
    if(pr[i].y>=0) A[++Alen]=pr[i];
    else B[++Blen]=pr[i];
  }
  sort(A+1,A+Alen+1,Cmp1);
  sort(B+1,B+Blen+1,Cmp2);
 
  int R=300*300/2; memset(G,-63,sizeof(G)); memset(F,-63,sizeof(F)); F[0]=G[0]=0;
  for(int i=1;i<=Alen;i++)
  {
    for(int j=R;j>=max(-A[i].x,0);j--)
    {
      if(G[j]>=0) F[j+A[i].y]=max(G[j]+A[i].L,F[j+A[i].y]);
    }
    for(int j=0;j<=R;j++) G[j]=F[j];
  }
 
  for(int i=1;i<=Blen;i++)
  {
    for(int j=R;j>=max(-B[i].x,0);j--)
    {
      if(G[j]>=0) F[j+B[i].y]=max(G[j]+B[i].L,F[j+B[i].y]);
    }
    for(int j=0;j<=R;j++) G[j]=F[j];
  }
  return printf("%d\n",F[0]),0;
}
/*
x1 y1
x2 y2
 
P+(y1+x2) > P+(y2+x1)
y1-x1>y2-x2
 
*/
View Code

 

 

[Hnoi2017]单旋

 题目写着单旋直接打单旋交上去肯定被草了 其实就是个暴力

考虑只要知道插入一个节点的深度 或者询问最大最小值的深度

我们不妨画图找找规律

如果是最小值的话 这个点上面的点的深度都+1 子树的-1

最大值也是

那么我们就可以用权值建线段树搞一下就好了 

关键还有一个插入 我们考虑维护整个树的形态 然后找出比这个数小一点和大一点的数

然后哪个数的深度比较大 就插在哪个数下面

树的形态肯定也不能用splay来写 发现每一次 比如把最小值挪上去 就是把最小值的右子树变成它父亲的左子树 然后自己的右子树指向原来的树根

维护一个fa son 还有anc就好了

细节比较多

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <climits>
#include <set>
#include <map>
 
using namespace std;
const int Maxn=4000010;
const int inf=1000000000;
int lc[Maxn],rc[Maxn],C[Maxn],lazy[Maxn]; int rt,tot;
 
void Push_down(int u)
{
  if(lazy[u])
  {
    if(lc[u]){C[lc[u]]+=lazy[u]; lazy[lc[u]]+=lazy[u]; }
    if(rc[u]){C[rc[u]]+=lazy[u]; lazy[rc[u]]+=lazy[u]; }
    lazy[u]=0;
  }
}
 
void Link(int &u,int L,int R,int k,int c)
{
  if(!u) u=++tot;
  if(L==R){C[u]=c; return ;}
  int mid=(L+R)>>1; Push_down(u);
  if(k<=mid) Link(lc[u],L,mid,k,c);
  else Link(rc[u],mid+1,R,k,c);
}
 
void Change(int u,int L,int R,int l,int r,int c)
{
  if(!u) return ;
  if(L==l && R==r){lazy[u]+=c; C[u]+=c; return ;}
  int mid=(L+R)>>1; Push_down(u);
  if(r<=mid) Change(lc[u],L,mid,l,r,c);
  else if(l>mid) Change(rc[u],mid+1,R,l,r,c);
  else Change(lc[u],L,mid,l,mid,c),Change(rc[u],mid+1,R,mid+1,r,c);
}
 
int Query(int u,int L,int R,int k)
{
  if(L==R) return C[u];
  int mid=(L+R)>>1; Push_down(u);
  if(k<=mid) return Query(lc[u],L,mid,k);
  else return Query(rc[u],mid+1,R,k);
}
 
set<int>S;
set<int> :: iterator it1,it2,it;
 
int N; map<int,int>fa; map<int,int>son[2]; int anc;
 
void Splay1(int x)
{
  if(x==anc) return ;
  son[0][fa[x]]=son[1][x]; if(son[1][x]) fa[son[1][x]]=fa[x]; 
  son[1][x]=anc; fa[anc]=x; fa[x]=0; anc=x;
}
void Splay2(int x)
{
  if(x==anc) return ;
  son[1][fa[x]]=son[0][x]; if(son[0][x]) fa[son[0][x]]=fa[x];
  son[0][x]=anc; fa[anc]=x; fa[x]=0; anc=x;
}
 
int main()
{
   
  scanf("%d",&N); rt=tot=0; S.insert(0); S.insert(INT_MAX);
  for(int i=1;i<=N;i++)
  {
    int op,x; scanf("%d",&op);
    if(op==1)
    {
      scanf("%d",&x);
      if(S.size()==2) S.insert(x),Link(rt,1,inf,x,1),anc=x,printf("1\n");
      else
      {
        //for(it=S.begin(); it!=S.end() ;++it) printf("%d ",*it); printf("\n");
         
        it1=S.lower_bound(x);
          --it1;
        it2=S.lower_bound(x);
         
        //printf("%d %d\n",*it1,*it2);
         
        int dx=0,dy=0;
         
        dx=Query(rt,1,inf,*it1);
        dy=Query(rt,1,inf,*it2);
         
        if(dx>dy) Link(rt,1,inf,x,dx+1),son[1][*it1]=x,fa[x]=*it1,printf("%d\n",dx+1);
        else Link(rt,1,inf,x,dy+1),son[0][*it2]=x,fa[x]=*it2,printf("%d\n",dy+1);
         
        S.insert(x);
      }
    }
    else if(op==2)
    {
      x=*(++S.begin()); printf("%d\n",Query(rt,1,inf,x));
      if(fa[x]) Change(rt,1,inf,fa[x],inf,1); Link(rt,1,inf,x,1);
      Splay1(x);
    }
    else if(op==3)
    {
      x=*(++S.rbegin()); printf("%d\n",Query(rt,1,inf,x));
      if(fa[x]) Change(rt,1,inf,1,fa[x],1); Link(rt,1,inf,x,1);
      Splay2(x);
    }
    else if(op==4)
    {
      x=*(++S.begin()); printf("%d\n",Query(rt,1,inf,x));
      if(fa[x]) Change(rt,1,inf,fa[x],inf,1); Link(rt,1,inf,x,1);
      Splay1(x); anc=son[1][x]; fa[son[1][x]]=son[1][x]=0;
      S.erase(x); Change(rt,1,inf,1,inf,-1);
    }
    else if(op==5)
    {
      x=*(++S.rbegin()); printf("%d\n",Query(rt,1,inf,x));
      if(fa[x]) Change(rt,1,inf,1,fa[x],1); Link(rt,1,inf,x,1);
      Splay2(x); anc=son[0][x]; fa[son[0][x]]=son[0][x]=0;
      S.erase(x); Change(rt,1,inf,1,inf,-1);
    }
  }
   
  return 0;
}
View Code

 

 

填坑计划:

bzoj3786

bzoj4567

bzoj4269

bzoj4917

bzoj4833

bzoj4922

ceoi 2004

四边形不等式优化dp

雅礼集训8.3 B题

posted @ 2017-07-05 10:39  wohenshuai  阅读(204)  评论(1编辑  收藏  举报