[GDOI2017集训&做题记录&日记]

引语:

之前做了好多题 学了很多看似很厉害的算法 然而好像并没有什么卵用

大概说一下之前学的东西:

数学(莫比乌斯反演 杜教筛 线性求逆元 Lucas 中国剩余定理 FFT NTT(原根) )

LCT(一点题)

概率(看了一点,差不多等于没学)

KM(做了道模版题和例题)

..........

(懒得写总结 都在笔记上)

 

然而学的东西好像不是很扎实 个人认为只有数论学的好一点

还是拼命刷题适合我 要适当的总结才行

 

省选集训的内容:

1.liao bzoj题表150题左右

2.学习后缀自动机 并且复习LCT 有时间的话巩固概率

3.网络流 数论的进一步学习

4.二分匹配及其相关与dfs树及其相关与FFT的复习

 

2017.3.14

早上看了点Polya和Burside的论文 吃饭前用手推门被玻璃刮了层皮... 中午看了道题liao告诉我是后缀自动机不可做

下午做了一道比较sb的题

 

[Ahoi2008]Meet 紧急集合

三个点 一开始想树链dfs序交并补 发现不用 只有lca是可行的 所以变成有根树 求出三个lca分别判断 但是要注意 统计答案的时候不能直接用dep abs求 还要做三次lca

 

跑步前看了道题 晚上爽了一下

[HNOI2005]狡猾的商人

一开始用高斯消元做 感觉很对啊 为什么WA了呢?? 自由元随便搞 无解变成false

#include<bits/stdc++.h>
using namespace std;
const int Maxn=1100;
const double EPS=1e-10;
int w,N,M; double A[Maxn][Maxn]; bool bo[Maxn]; double F[Maxn];
bool Gauss()
{
  for(int i=1;i<=N;i++)
  {
    if(fabs(A[i][i])<EPS)
    {
      bo[i]=1;
      for(int j=i+1;j<=M;j++)
        if(fabs(A[i][i])>EPS)
        {
          bo[i]=0;
          for(int k=1;k<=N+1;k++) swap(A[j][k],A[i][k]);
          break;
        }
    }
    if(bo[i]==1) continue;
    for(int j=i+1;j<=M;j++)
    {
      double kk=A[j][i]/A[i][i];
      for(int k=i;k<=N+1;k++) A[j][k]-=A[i][k]*kk;
    }
  }
   
  for(int i=1;i<=M;i++)
  {
    bool bk=false;
    for(int j=1;j<=N;j++) if(fabs(A[i][j])>EPS){bk=true; break;}
    if(!bk&&(fabs(A[i][N+1])>EPS)) return 0;
  }
   
  for(int i=N;i>=1;i--)
  {
    if(bo[i]){F[i]=0; continue;}
    F[i]=A[i][N+1]; for(int j=N;j>i;j--) F[i]-=F[j]*A[i][j];
  }
   
  for(int i=1;i<=M;i++)
  {
    double ansj=0;
    for(int j=1;j<=N;j++) ansj+=F[j]*A[i][j];
    if(fabs(ansj-A[i][N+1])>EPS) return 0;
  }
  return 1;
}
int main()
{
  scanf("%d",&w);
  while(w--)
  {
    scanf("%d%d",&N,&M);
    for(int i=1;i<=M;i++)
    {
      int x,y; double d; scanf("%d%d%lf",&x,&y,&d);
      for(int j=x;j<=y;j++) A[i][j]=1;
      A[i][N+1]=d;
    }
    bool o=Gauss();
    if(o) printf("true\n");
    else printf("false\n");
  }
  return 0;
}
View Code

上面是WA的

然后膜题解 没有做过类似的题啊 时间复杂度O(N)?? 我一开始想的是 O(N^3)???

好像很妙 用前缀和来列出不等式 dis[v]-dis[s-1]=v 然后判断是否所有的都满足

对于简单的等号不等式 我们可以用带权并查集做(很快就忘 总结一下)

数学测验爆炸了 发现平时做题不能慢吞吞 要思考的快 考试节奏才不会乱 不然一上来就不适应 导致超多错误出现

 

2017.3.15

早上被语文老师草了因为没写作文 他问我怎么办我说明天交..

中午看了道题,下午写了

 

[NOI2010]超级钢琴

感觉题目描述很简单 选k段不同的子串的和 使得总和也最大 很明显每个点贪心 然后贪完后一分为二 记录左边和右边即可

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int Maxn=500010;
struct node
{
  int c,p,l,r,id;
  node(){};
  node(int _c,int _p,int _l,int _r,int _id){c=_c; p=_p; l=_l; r=_r; id=_id;}
  friend bool operator <(node x,node y){return x.c<y.c;}
};
priority_queue<node>Q; int N,K,L,R; int A[Maxn]; int Sum[Maxn];
int tot,lc[Maxn<<2],rc[Maxn<<2],C[Maxn<<2],P[Maxn<<2],rt;
void Link(int &u,int L,int R,int k,int c)
{
  if(!u) u=++tot,C[u]=INT_MAX;
  
  if(L==R){C[u]=c; P[u]=L; 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);
  
  if(C[lc[u]]<=C[rc[u]]) P[u]=P[lc[u]],C[u]=C[lc[u]];
  else P[u]=P[rc[u]],C[u]=C[rc[u]];
}
pair<int,int> Query(int u,int L,int R,int l,int r)
{
  if(l>r) return make_pair(INT_MAX,-1);
  if(L==l&&R==r) return make_pair(C[u],P[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
  {
    pair<int,int> p1=Query(lc[u],L,mid,l,mid);
    pair<int,int> p2=Query(rc[u],mid+1,R,mid+1,r);
    if(p1.first<=p2.first) return p1;
    else return p2;
  }
}
int main()
{
  scanf("%d%d%d%d",&N,&K,&L,&R);
  for(int i=1;i<=N;i++) scanf("%d",&A[i]),Sum[i]=Sum[i-1]+A[i];
  tot=0; for(int i=0;i<=N;i++)
    Link(rt,0,N,i,Sum[i]);
  for(int i=L;i<=N;i++)
  {
    pair<int,int> p=Query(rt,0,N,max(0,i-R),i-L);
    Q.push(node(Sum[i]-p.first,p.second,max(0,i-R),i-L,i));
  }
  LL ans=0;
  while(K--)
  {
    node x=Q.top(); ans+=x.c;
    
    pair<int,int> p1=Query(rt,0,N,x.l,x.p-1);
    pair<int,int> p2=Query(rt,0,N,x.p+1,x.r);
    
    if(p1.second!=-1) Q.push(node(Sum[x.id]-p1.first,p1.second,x.l,x.p-1,x.id));
    if(p2.second!=-1) Q.push(node(Sum[x.id]-p2.first,p2.second,x.p+1,x.r,x.id));
    
    Q.pop();
  }
  return printf("%lld\n",ans),0;
}
/*
4 3 2 3
3
2
-6
8
*/
View Code

 

晚上看了道题没写出来

 

2017.3.16

早上背了故事 中午把题写出来(看了hwzer博客 顺便励志了一下)

楼房重建

原谅我线段树没学好 然后一开始想用set 好像set没查找某个数是第几大的功能

线段树套splay?? 然而我没写过也不会 所以膜hwzer

好像合并有点难?? 

 改变当前的值左边是没有影响的 但是对右边也许会有 然而这样我们通过不断合并区间去算右边的值与总和

#include<bits/stdc++.h>
using namespace std;
const int Maxn=100010;
int tot,lc[Maxn*4],rc[Maxn*4],rt; int C[Maxn*4]; double Val[Maxn*4];
void Link(int &u,int L,int R,int k)
{
  if(!u) u=++tot;
  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);
}
int Calc(int u,int L,int R,double c)
{
  if(L==R) return Val[u]>c;
  int mid=(L+R)>>1;
  if(c>Val[lc[u]]) return Calc(rc[u],mid+1,R,c);
  else return C[u]-C[lc[u]]+Calc(lc[u],L,mid,c);
}
void Change(int u,int L,int R,int k,double y)
{
  if(L==R){Val[u]=y; C[u]=1; return ;}
  int mid=(L+R)>>1;
  if(k<=mid) Change(lc[u],L,mid,k,y);
  else Change(rc[u],mid+1,R,k,y);
  Val[u]=max(Val[lc[u]],Val[rc[u]]);
  C[u]=C[lc[u]]+Calc(rc[u],mid+1,R,Val[lc[u]]);
}
int N,M;
int main()
{
  scanf("%d%d",&N,&M); memset(C,0.0,sizeof(C));
  for(int i=1;i<=N;i++) Link(rt,1,N,i);
  for(int i=1;i<=M;i++)
  {
    int x,y; scanf("%d%d",&x,&y); double k=(double)y/x;
    Change(rt,1,N,x,k);
    printf("%d\n",C[1]);
  }
  return 0;
}
View Code

 

2017.3.17

写了两天这道题,今天是最后一天上课了哈哈哈哈哈哈哈哈哈哈哈

[Noi2013]快餐店

考虑n个点n条边 即可想到这是一棵环套树 然后对于环上的点 进行处理 很快想到分类讨论

在子树里面的 一定是最长两条链上 tree dp愉悦得处理一下

然后考虑走环的 最长路径的中间位置就是答案 但是至于怎么找呢 我好像不是很会

其实环上的点 有一个小小的性质 也就是说每次环上的点怎么走 总会剩下一条边在环上没有走过

所以我们可以枚举每一条边 割掉 这样的话就变成了一棵树了 对不对??

至于环上的转移 想一想?? 不难 只是想到用前缀和比较难想 可能之前没做过

用线段树转移且找到环上两个不同的点成最长链 最长链的长度就是两点间距离加上自身下面的链的长度 利用前缀和可以减

利用前缀和有个好处就是 每次转移的时候不用修改 只要修改一个点

根据题解所说 dis[x] sum[x] 分别表示环上的x点子树的链和到环上的第1个点的距离(当然已经标过号了)

维护两颗线段树 dis[x]+sum[x] dis[x]-sum[x] 即可 开longlong最后除2

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL Maxn=100010;
struct node
{
  LL x,y,next,d;
  node(){}
  node(LL _x,LL _y,LL _d){x=_x; y=_y; d=_d;}
}edge[Maxn*4],H[Maxn]; LL len,first[Maxn]; LL Hlen=0;
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;}
stack<LL>S; bool bo[Maxn]; bool boo=0; LL N; bool h[Maxn];
void Dfs(LL x,LL fa)
{
  for(LL k=first[x];k!=-1;k=edge[k].next)
  {
    LL y=edge[k].y;
    if(!bo[y]){bo[y]=1; S.push(k); Dfs(y,x); if(boo) return ; else S.pop();}
    else if(y!=fa)
    {
      LL ik=S.top(); H[++Hlen]=node(x,y,edge[k].d); h[x]=1;
      do
      {
        H[++Hlen]=node(edge[ik].x,edge[ik].y,edge[ik].d); h[edge[ik].x]=1; S.pop();
        if(!S.empty()) ik=S.top();
        else break;
      }while(edge[ik].y!=y); boo=1;
    }
  }
}
LL Dis[Maxn]; LL Max1[Maxn],Max2[Maxn];
LL Dfs2(LL x,LL fa)
{
  for(LL k=first[x];k!=-1;k=edge[k].next)
  {
    LL y=edge[k].y;
    if(h[y]||(y==fa)) continue;
    LL D=Dfs2(y,x)+edge[k].d;
    Dis[x]=max(Dis[x],D);
    if(Max1[x]<D) Max2[x]=Max1[x],Max1[x]=D;
    else if(Max2[x]<D) Max2[x]=D;
  }
  return Dis[x];
}
LL D[Maxn],Sum[Maxn],ide[Maxn]; LL id;
LL rt[2],lc[Maxn*10],rc[Maxn*10],C1[Maxn*10],C2[Maxn*10],P1[Maxn*10],P2[Maxn*10],tot;
void update(LL u)
{
  LL c1,c2,c3,c4,p1,p2,p3,p4;
  c1=C1[lc[u]]; c2=C2[lc[u]]; c3=C1[rc[u]]; c4=C2[rc[u]];
  p1=P1[lc[u]]; p2=P2[lc[u]]; p3=P1[rc[u]]; p4=P2[rc[u]];
  LL c5=LLONG_MIN,c6=LLONG_MIN; LL p5,p6;
  if(c1>c5){c5=c1; p5=p1;}
  else if(c1>c6){c6=c1; p6=p1;}
  if(c2>c5){c5=c2; p5=p2;}
  else if(c2>c6){c6=c2; p6=p2;}
  if(c3>c5){c5=c3; p5=p3;}
  else if(c3>c6){c6=c3; p6=p3;}
  if(c4>c5){c5=c4; p5=p4;}
  else if(c4>c6){c6=c4; p6=p4;}
  C1[u]=c5; P1[u]=p5; C2[u]=c6; P2[u]=p6;
}
void Link(LL &u,LL L,LL R,LL k,LL c)
{
  if(!u){u=++tot; C1[u]=C2[u]=LLONG_MIN;}
  if(L==R){C1[u]=c; P1[u]=k; return ;}
  LL mid=(L+R)>>1;
  if(k<=mid) Link(lc[u],L,mid,k,c);
  else Link(rc[u],mid+1,R,k,c);
  update(u);
}
int main()
{
  scanf("%lld",&N); len=0; memset(first,-1,sizeof first);
  for(LL i=1;i<=N;i++){LL x,y,d; scanf("%lld%lld%lld",&x,&y,&d); ins(x,y,d); ins(y,x,d);}
  memset(bo,0,sizeof bo);
  Dfs(1,0);
  memset(Dis,0,sizeof Dis); memset(Max1,0,sizeof(Max1)); memset(Max2,0,sizeof(Max2));
  for(LL i=1;i<=Hlen;i++) Dfs2(H[i].y,0);
  LL Hn=0;
  for(LL i=1;i<=Hlen;i++) Hn=max(Hn,Max1[H[i].y]+Max2[H[i].y]);
  id=0; LL s=0;
  for(LL i=1;i<=Hlen;i++)
  {
    LL x=H[i].y; ide[x]=++id;
    D[id]=Dis[x]; Sum[id]=s; if(i!=Hlen) s+=H[i].d; 
  }
  for(LL i=1;i<=id;i++)
    Link(rt[0],1,id,i,D[i]-Sum[i]),Link(rt[1],1,id,i,D[i]+Sum[i]);
  LL Hw=LLONG_MAX; LL z=0; z+=H[Hlen].d;
  for(LL i=1;i<=Hlen;i++)
  {
    LL c1,c2,c3,c4,p1,p2,p3,p4;
    c1=C1[rt[0]]; c2=C2[rt[0]]; c3=C1[rt[1]]; c4=C2[rt[1]];
    p1=P1[rt[0]]; p2=P2[rt[0]]; p3=P1[rt[1]]; p4=P2[rt[1]];
    if(p1==p3) Hw=min(Hw,max(c1+c4,c2+c3));
    else Hw=min(Hw,c1+c3);
    Sum[ide[H[i].y]]=s+z;
    Link(rt[0],1,id,ide[H[i].y],D[ide[H[i].y]]-Sum[ide[H[i].y]]);
    Link(rt[1],1,id,ide[H[i].y],D[ide[H[i].y]]+Sum[ide[H[i].y]]);
    z+=H[i].d;
  }
  LL ans=0;
  if(Hw==INT_MAX) ans=Hn;
  else ans=max(Hn,Hw);
  return printf("%.1lf\n",double(ans)/2.0),0;
}
View Code

其实代码很短只是略显恶心

 

染色

逃了个物理考试来写题好爽

首先题目好像很繁琐 我们要化归一下

wph:你可以把白色个数的期望=sigma(每个格子被选中的概率)

好像很对...变成每个小白格的期望 也就是概率

那么就是格子被选中的个数了 发现经过这个格子还要选中很难 不妨做出不选中的??

而且染M次的话也就是1-M次不选中的概率=选中的概率

用A[i]记录长度为i的区间随便染一次的方案数

搞一下即可

#include<bits/stdc++.h>
using namespace std;
const int Maxn=1000010;
int Sum[Maxn]; int N,M,S,T; double A[Maxn];
double Quick_Power(double fm,int k)
{
  if(k==1) return fm;
  if(k==0) return 1;
  double ffm=Quick_Power(fm,k>>1);
  if(k&1) return ffm*ffm*fm;
  else return ffm*ffm;
}
int main()
{
  scanf("%d%d%d%d",&N,&M,&S,&T);
  memset(A,0.0,sizeof(A));
  for(int i=S;i<=T;i++) A[i]=A[i-1]+i-S+1;
  for(int i=T+1;i<=N;i++) A[i]=A[i-1]+T-S+1;
  double ans=0.0;
  for(int i=1;i<=N;i++)
  {
    double s=(A[N-i]+A[i-1])/A[N];
    ans+=1.0-Quick_Power(s,M);
  }
  return printf("%.3lf\n",ans),0;
}
View Code

 

2017.3.18

Mato的文件管理

停课第一天 一大早起来补了个刀

很明显求逆序对数 然后是区间询问 莫队+树状数组随便写 还有离散化

写了不够30行..

#include<bits/stdc++.h>
using namespace std;
const int Maxn=50010;
struct node{int l,r,id;}Q[Maxn];
int N,M,A[Maxn]; int id; int mp[Maxn];
int q; bool Cmp(const node &x,const node &y){if(x.l/q!=y.l/q) return x.l<y.l; return x.r<y.r;}
int ans[Maxn]; int tr[Maxn]; int low_bit(int x){return x&(-x);}
void Change(int x,int opt){for(int i=x;i<=id;i+=low_bit(i)) tr[i]+=opt;}
int Query(int x){if(x==0) return 0; int ans=0; for(int i=x;i;i-=low_bit(i)) ans+=tr[i]; return ans;}
pair<int,int> B[Maxn]; bool cmp(const pair<int,int> &x,const pair<int,int> &y){return x.first<y.first;}
int main()
{
  scanf("%d",&N); for(int i=1;i<=N;i++) scanf("%d",&A[i]),B[i].first=A[i],B[i].second=i;
  sort(B+1,B+N+1,cmp); id=0; for(int i=1;i<=N;i++){if((B[i].first!=B[i-1].first)||(i==1)) id++; mp[B[i].second]=id;}
  q=(int)sqrt(N); scanf("%d",&M);
  for(int i=1;i<=M;i++) scanf("%d%d",&Q[i].l,&Q[i].r),Q[i].id=i;
  sort(Q+1,Q+M+1,Cmp);
  int L=1; int R=0; int s=0;
  for(int i=1;i<=M;i++)
  {
    while(R<Q[i].r){R++; s+=Query(id)-Query(mp[R]); Change(mp[R],1);}
    while(L<Q[i].l){s-=Query(mp[L]-1); Change(mp[L],-1); L++;}
    while(R>Q[i].r){s-=Query(id)-Query(mp[R]); Change(mp[R],-1); R--;}
    while(L>Q[i].l){L--; s+=Query(mp[L]-1); Change(mp[L],1);}
    ans[Q[i].id]=s;
  }
  for(int i=1;i<=M;i++) printf("%d\n",ans[i]);
  return 0;
}
View Code 

 

Gty的二逼妹子序列

好题啊 我好垃圾啊 一眼树状数组+莫队 发现会T

然而来自liao的蔑视:

你考虑一下 把每个修改改为O(1) 询问为O(N^0.5) 是不是可以过了啊

我:什么??!? 询问可以做到O(N^0.5) 再分块?

看来还是我太弱了 膜liao 第一次做这种分块 好强啊

 

 好像很有道理 总结一下:

对于O(1) 改 O(N^0.5) 询问 分块做

对于O(log N) 改 O(log N) 询问 树状数组或者线段树做

#include<bits/stdc++.h>
using namespace std;
const int Maxn=100010;
inline int Read()
{
  int x=0; int f=1; char ch=getchar();
  while((ch<'0')||(ch>'9')){if(ch=='-') f=-1; ch=getchar();}
  while((ch>='0')&&(ch<='9')){x=x*10+ch-'0'; ch=getchar();}
  return x*f;
}
struct node{int l,r,a,b,id;}Q[Maxn*10]; int q;
bool cmp(const node &x,const node &y){if((x.l/q)!=(y.l/q)) return x.l<y.l; return x.r<y.r;} 
int N,M; int A[Maxn];

int Sum[Maxn],s[Maxn];
int qn;
void Change(int x,int opt)
{
  if(opt==1){s[x]++; if(s[x]==1) Sum[x/qn]++;}
  if(opt==-1){s[x]--; if(s[x]==0) Sum[x/qn]--;}
}
int Query(int x,int y)
{
  int a=x/qn; int b=y/qn; int ans=0;
  for(int i=a+1;i<b;i++) ans+=Sum[i];
  if(a==b){for(int i=x;i<=y;i++) if(s[i]) ans++;}
  else
  {
    for(int i=x;i<=min(qn*(a+1)-1,N);i++) if(s[i]) ans++;
    for(int i=qn*b;i<=y;i++) if(s[i]) ans++;
  }
  return ans;
}
int ans[Maxn*10];
int main()
{
  N=Read(); M=Read();
  for(int i=1;i<=N;i++) A[i]=Read();
  for(int i=1;i<=M;i++) Q[i].l=Read(),Q[i].r=Read(),Q[i].a=Read(),Q[i].b=Read(),Q[i].id=i;
  q=(int)(ceil(sqrt(M))); qn=(int)(ceil(sqrt(N))); sort(Q+1,Q+M+1,cmp);
  int L=1; int R=0;
  for(int i=1;i<=M;i++)
  {
    while(R<Q[i].r){R++; Change(A[R],1);}
    while(L>Q[i].l){L--; Change(A[L],1);}
    while(R>Q[i].r){Change(A[R],-1); R--;}
    while(L<Q[i].l){Change(A[L],-1); L++;}
    ans[Q[i].id]=Query(Q[i].a,Q[i].b);
  }
  for(int i=1;i<=M;i++) printf("%d\n",ans[i]);
  return 0;
}
View Code

 晚上买书颓废

 

2017.3.19

中午很垃圾的写了这题

极光

昨天晚上推了推 有点恶心这题

graze(x,y)=|x-y|+|a[x]-a[y]| <=k

我们分类讨论 其实我们可以发现

列出四个式子 但是我们发现 这并不好做 因为一共有四维 然而编号顺序也有一维 所以我们化简一下 试图将两式合并 比如(1) (4)

接下来条件二三合并 得到两个大的条件

再把顺序条件合上 一共三个条件 做CDQ分治

注意 题目K没有给范围好像给 1A..

#include<bits/stdc++.h>
using namespace std;
const int Maxn=40010;
const int inf=120000;
struct node
{
  int x,y,i,k;
  node(){}
  node(int _x,int _y,int _i,int _k){x=_x; y=_y; i=_i; k=_k;}
}Q[Maxn*3],A[Maxn*3],B[Maxn*3]; int Alen,Blen,Qlen; int N,M; int H[Maxn*3]; char st[10];
//A Modify B Query
bool Cmp(const node &x,const node &y){return x.i<y.i;}
bool Cmp1(const node &x,const node &y){return x.x<y.x;}
int tr[Maxn*10]; int ans[Maxn*3]; int low_bit(int x){return x&(-x);}
void Add(int x){for(int i=x;i<=inf*2;i+=low_bit(i)) tr[i]++;}
int Query(int x){if(x==0) return 0; int ans=0; for(int i=x;i>=1;i-=low_bit(i)) ans+=tr[i]; return ans;}
void CDQ(int L,int R)
{
  if(L==R) return ;
  int mid=(L+R)>>1; Alen=Blen=0;
  for(int i=L;i<=mid;i++)
    if(Q[i].i) A[++Alen]=Q[i];
  for(int i=mid+1;i<=R;i++)
    if(!Q[i].i)
    {
      B[++Blen]=Q[i]; B[Blen].i=i;
      B[Blen].x=Q[i].x+Q[i].k;
      
      B[++Blen]=Q[i]; B[Blen].i=-i;
      B[Blen].x=Q[i].x-Q[i].k-1;
    }
  
  sort(A+1,A+Alen+1,Cmp1);
  sort(B+1,B+Blen+1,Cmp1);
  
  int j=1;
  for(int i=1;i<=Blen;i++)
  {
    while(A[j].x<=B[i].x&&j<=Alen)
    {
      Add(A[j].y+120000);
      j++;
    }
    if(B[i].i>0) ans[B[i].i]+=Query(min(inf*2,B[i].y+B[i].k+120000))-Query(max(0,B[i].y-B[i].k+120000-1));
    else ans[-B[i].i]-=Query(min(inf*2,B[i].y+B[i].k+120000))-Query(max(0,B[i].y-B[i].k+120000-1));
  }
  for(int i=1;i<=Alen;i++) for(int j=A[i].y+120000;j<=inf*2;j+=low_bit(j)) tr[j]=0;
  
  CDQ(L,mid);
  CDQ(mid+1,R);
}
int main()
{
  scanf("%d%d",&N,&M);
  for(int i=1;i<=N;i++) scanf("%d",&H[i]);
  for(int i=1;i<=N;i++) Q[++Qlen]=node(i-H[i],i+H[i],1,0);
  for(int i=1;i<=M;i++)
  {
    scanf("%s",st+1); int x,y; scanf("%d%d",&x,&y);
    if(st[1]=='Q') Q[++Qlen]=node(x-H[x],x+H[x],0,y);
    else{H[x]=y; Q[++Qlen]=node(x-H[x],x+H[x],1,0);}
  }
  memset(ans,0,sizeof(ans));
  CDQ(1,Qlen);
  for(int i=1;i<=Qlen;i++) if(!Q[i].i) printf("%d\n",ans[i]);
  return 0;
}
/*
Query 0
Modify 1
*/
View Code

后来问wph怎么做 其实是正方形旋转45度得到的矩形内含有多少个点 比较推荐这种方法

自己的方法后来推了一下还有一个充分必要条件的问题不会证..太垃圾

 

晚上写了道狗屎题

 

[Baltic2013]ballmachine

第一次做Baltic的题就让我有点畏惧

首先题目是放若干个球 取球

那么我们考虑 它放的顺序 肯定是按照某种特定顺序放的 所以我们把那某种顺序用tree dp做出来

得到一个数组 就是某种顺序 我们考虑 取一个球 它上面的就会掉下来一个 那么线段树维护这个球上面有多少个 跳倍增找到上面的点

可以知道这个点就是被掉下来了 也就是说这个点已经空了 所以的话它下面的那些点都要在线段树上-1 也就是连续的一段-1

放球呢 暴力放 因为它每次取球都只取一个 算了算时间复杂度是不会超出的 那么用优先队列取出刚才我们得到顺序的那个数组最前面的位置

显然会放在这个位置上 同样的 在下面的那些球要在线段树上+1

当然了 掉下来之后那个位置空的 要放进优先队列里面

#include<bits/stdc++.h>
using namespace std;
const int Maxn=100010;
struct node{int x,y,next;}edge[Maxn]; int first[Maxn],len;
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; int root=0; int fa[Maxn][20];
int Minx[Maxn]; vector<pair<int,int> >V[Maxn];
bool Cmp(const pair<int,int> &x,const pair<int,int> &y){return x.second<y.second;}
void Dfs(int x)
{
  Minx[x]=x;
  for(int k=first[x];k!=-1;k=edge[k].next)
  {
    int y=edge[k].y;
    fa[y][0]=x; Dfs(y); Minx[x]=min(Minx[x],Minx[y]);
    V[x].push_back(make_pair(y,Minx[y]) );
  }
  sort(V[x].begin(),V[x].end(),Cmp);
}
int id,dfn[Maxn],ide[Maxn],S[Maxn];
void Dfs1(int x)
{
  S[x]=id;
  for(int i=0;i<V[x].size();i++) Dfs1(V[x][i].first); 
  id++; dfn[x]=id; ide[id]=x;
}
priority_queue<int,vector<int>,greater<int> > Q;
int tot,rt; int lc[Maxn*4],rc[Maxn*4],lazy[Maxn*4],C[Maxn*4];
void Link(int &u,int L,int R,int k)
{
  if(!u) u=++tot;
  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);
}
void Push_down(int u)
{
  if(lazy[u])
  {
    C[lc[u]]+=lazy[u]; lazy[lc[u]]+=lazy[u];
    C[rc[u]]+=lazy[u]; lazy[rc[u]]+=lazy[u];
    lazy[u]=0;
  }
}
void Change(int u,int L,int R,int l,int r,int c)
{
  if(L==l&&R==r){C[u]+=c; lazy[u]+=c; return ;}
  Push_down(u);
  int mid=(L+R)>>1;
  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];
  Push_down(u);
  int mid=(L+R)>>1;
  if(k<=mid) return Query(lc[u],L,mid,k);
  else return Query(rc[u],mid+1,R,k);
}
int main()
{  scanf("%d%d",&N,&q); len=0; memset(first,-1,sizeof(first));
  for(int i=1;i<=N;i++){int x; scanf("%d",&x); if(x==0) root=i; ins(x,i);}
  for(int i=1;i<=N;i++) Minx[i]=INT_MAX;
  Dfs(root);
  id=0; memset(dfn,0,sizeof(dfn)); Dfs1(root);
  for(int j=1;j<=18;j++) for(int i=1;i<=N;i++)  fa[i][j]=fa[fa[i][j-1]][j-1];
  for(int i=1;i<=id;i++) Q.push(i),Link(rt,1,id,i);
  for(int i=1;i<=q;i++)
  {
    int op,x; scanf("%d%d",&op,&x);
    if(op==1)
    {
      int last=0;
      while(x--)
      {
        int pos=ide[Q.top()]; Q.pop(); last=pos;
        Change(rt,1,id,S[pos]+1,dfn[pos],1);
      }printf("%d\n",last);
    }
    else if(op==2)
    {
      int deep=Query(rt,1,id,dfn[x])-1; printf("%d\n",deep);
      for(int i=18;i>=0;i--) if(deep>=(1<<i)) deep-=(1<<i),x=fa[x][i];
      Change(rt,1,id,S[x]+1,dfn[x],-1); Q.push(dfn[x]);
    }
  }
  return 0;
}
View Code

 

2017.3.20

好爽啊终于可以一整天做题 今天做题算是很冷静 但是做的题好像不是很好 都很难..

 

[Baltic2013]brunhilda

liao把这道题有病的放在了数据结构里面..

很好玩的一道题 首先我们考虑dp dp[i]表示取多少次变成0 显然转移dp[i]=dp[i-i%p]+1

但是这样显然不能满足题目的时间限制

那么我们考虑有没有一点决定性的东西

定理:取最远的一定更优 (翻自栋爷的解释)

同理 dp这个数组也应该是递增的 证法同样

所以我们可以用一个指针j 表示上一个可以取到哪里 然后的话j不可以取了就跳到右边

判断j是否合法 也很简单 就是i-j的距离 一定要小于当前数的最大的质因数

至于最大的质因数 我们也可以先线性筛 然后dp求 当然最大质因数要是输入里面的数

不合法的情况就是j越过i了 注意取到不合法也是不合法

#include<bits/stdc++.h>
using namespace std;
const int Maxn=10000010;
const int Maxm=1000010;
int prime[Maxm],Minx[Maxn]; int V[Maxn]; int maxx=0,pri=0;
void Prime()
{
  V[1]=0; for(int i=2;i<=maxx;i++) V[i]=1;
  for(int i=2;i<=maxx;i++)
  {
    if(V[i]){prime[++pri]=i; Minx[i]=i;}
    for(int j=1;(j<=pri)&&(i*prime[j]<=maxx);j++)
    {
      V[i*prime[j]]=0;
      Minx[i*prime[j]]=min(Minx[i],prime[j]);
      if(i%prime[j]==0) break;
    }
  }
}
int P[Maxm]; int q[Maxm]; int N,Q;
int main()
{
  scanf("%d%d",&N,&Q); for(int i=1;i<=N;i++) scanf("%d",&P[i]);
  for(int i=1;i<=Q;i++) scanf("%d",&q[i]),maxx=max(maxx,q[i]);
  Prime();
  sort(P+1,P+N+1);
  N=unique(P+1,P+N+1)-(P+1);
  for(int i=1;i<=maxx;i++) V[i]=0;
  for(int i=1;i<=N;i++) V[P[i]]=P[i];
  for(int i=2;i<=maxx;i++) V[i]=max(V[i/Minx[i]],V[Minx[i]]);
  for(int i=1;i<=maxx;i++) Minx[i]=0;
  V[0]=P[N];
  int j=0;
  for(int i=1;i<=maxx;i++)
  {
    while((i-j>=V[j])&&(j<=i)) j++;
    if(j>=i) Minx[i]=INT_MAX;
    else
    {
      if(Minx[j]==INT_MAX) Minx[i]=INT_MAX;
      else Minx[i]=Minx[j]+1;
    }
  }
  for(int i=1;i<=Q;i++) if(Minx[q[i]]==INT_MAX) printf("oo\n"); else printf("%d\n",Minx[q[i]]);
  return 0;
}
/*
2 2
2 3
5
6
*/
View Code

 

 

Snow

 一开始想到了直接就是两颗线段树 一个维护位置有没有扫过 一个维护工人剩下的多少

然而发现很垃圾 打到一半全删

膜Claris 注意每个位置扫过的就不再扫了 所以扫的位置是有限的 影响的人我们可以用线段树来维护

线段从小到大给出 离散化 然后二分影响的范围 扫过的位置为了避免重新去访问 我们可以用并查集压缩路径

定义Find(fa[i])一访问到i这个点就可以去到Find(fa[i])了

#include<bits/stdc++.h>
using namespace std;
const int Maxn=300010;
int ide[Maxn*2],len;
int T,N; pair<int,int>pr[Maxn];
 
int rt,tot,lc[Maxn*8],rc[Maxn*8],C[Maxn*8],P[Maxn*8],lazy[Maxn*8];
void Link(int &u,int L,int R,int k,int c)
{
  if(!u) u=++tot;
  if(L==R){C[u]=c; P[u]=k; 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);
  if(C[lc[u]]>C[rc[u]]) C[u]=C[rc[u]],P[u]=P[rc[u]];
  else C[u]=C[lc[u]],P[u]=P[lc[u]];
}
 
void Push_down(int u)
{
  if(lazy[u])
  {
    lazy[lc[u]]+=lazy[u]; lazy[rc[u]]+=lazy[u];
    C[lc[u]]+=lazy[u]; C[rc[u]]+=lazy[u];
    lazy[u]=0;
  }
}
 
void Change(int u,int L,int R,int l,int r,int c)
{
  if(L==l&&R==r){C[u]+=c; lazy[u]+=c; return ;}
  Push_down(u);
  int mid=(L+R)>>1;
  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);
  }
  if(C[lc[u]]>C[rc[u]]) C[u]=C[rc[u]],P[u]=P[rc[u]];
  else C[u]=C[lc[u]],P[u]=P[lc[u]];
}
 
void Change2(int u,int L,int R,int k,int c)
{
  if(L==R){C[u]=c; return ;}
  Push_down(u);
  int mid=(L+R)>>1;
  if(k<=mid) Change2(lc[u],L,mid,k,c);
  else Change2(rc[u],mid+1,R,k,c);
  if(C[lc[u]]>C[rc[u]]) C[u]=C[rc[u]],P[u]=P[rc[u]];
  else C[u]=C[lc[u]],P[u]=P[lc[u]];
}
 
int nex[Maxn*2],fro[Maxn*2];
 
int fa[Maxn*2]; int Find(int x){if(x==fa[x]) return x; else return fa[x]=Find(fa[x]);}
 
inline void read(int&a){char c;while(!(((c=getchar())>='0')&&(c<='9')));a=c-'0';while(((c=getchar())>='0')&&(c<='9'))(a*=10)+=c-'0';}
 
int main()
{
  read(T); read(N);
  len=0; for(int i=1;i<=N;i++) read(pr[i].first),read(pr[i].second),ide[++len]=pr[i].first,ide[++len]=pr[i].second;
  sort(ide+1,ide+len+1);
  int j=0; for(int i=1;i<=len;i++) if(ide[i]!=ide[j]) ide[++j]=ide[i]; len=j;
  tot=0; rt=0; for(int i=1;i<=N;i++) Link(rt,1,N,i,pr[i].second-pr[i].first+1);
   
  for(int i=1;i<=len;i++)
  {
    int pos=ide[i];
    int L=1; int R=N; int ret=-1;
    while(L<=R)
    {
      int mid=(L+R)>>1;
      if(pos>pr[mid].second) L=mid+1;
      else if(pos>=pr[mid].first&&pos<=pr[mid].second) ret=mid,L=mid+1;
      else R=mid-1;
    }
    nex[i]=ret;
  }
   
  for(int i=1;i<=len;i++)
  {
    int pos=ide[i];
    int L=1; int R=N; int ret=-1;
    while(L<=R)
    {
      int mid=(L+R)>>1;
      if(pos<pr[mid].first) R=mid-1;
      else if(pos>=pr[mid].first&&pos<=pr[mid].second) ret=mid,R=mid-1;
      else L=mid+1;
    }
    fro[i]=ret;
  }
   
  memset(fa,0,sizeof(fa));
  for(int i=1;i<=len+1;i++) fa[i]=i;
  for(int i=1;i<=N;i++)
  {
    int pos=P[rt];
    int L=lower_bound(ide+1,ide+len+1,pr[pos].first)-ide;
    int R=lower_bound(ide+1,ide+len+1,pr[pos].second)-ide; int j=L;
    printf("%d\n",pos);
    while(1)
    {
      j=Find(fa[j]); if(j>=R) break;
      Change(rt,1,N,max(fro[j],fro[j+1]),min(nex[j],nex[j+1]),-(ide[j+1]-ide[j]));
      fa[j]=Find(fa[j+1]);
    }
    Change2(rt,1,N,pos,INT_MAX);
  }
  return 0;
}
View Code

 

 

[Pa2011]Hard Choice 艰难的选择

好题啊啊啊啊 很明显就可以想到 删边变成加边 倒过来做 维护生成树

如果两个点是在生成树上的话 就满足 一开始拼命想边分 但不会动态维护 yy了一下好像也不是很会

想到用LCT liao大佬说不用..

依旧膜Claris 还是LCT大法好

把边赋值 有权表示还没形成生成树 无权表示形成生成树 边权建虚点存

首先把没删的边按顺序建成一棵树(如果有一些已经建了 再建就变成环的 就别建了)

然后把上面别建的边的两个端点都扔进去找出路径 很显然这条路径就可以变成生成树了 把所有的权值都变为无权

然后就是从后到前插边 也是一样 不联通的联通 已经联通的就把所有权值变为0

连不连通并查集大法好 复习了一遍LCT

#include<bits/stdc++.h>
using namespace std;
const int Maxn=200010;
 
struct node
{
  int son[2],fa,val,sum; bool rev,flag;
}tr[Maxn<<1];
 
bool Is_root(int x)
{
  if((tr[tr[x].fa].son[0]!=x)&&(tr[tr[x].fa].son[1]!=x)) return true;
  return false;
}
 
void update(int x){tr[x].sum=tr[tr[x].son[0]].sum+tr[tr[x].son[1]].sum+tr[x].val;}
void Push_down(int x)
{
  if(tr[x].rev)
  {
    tr[tr[x].son[1]].rev^=1; tr[tr[x].son[0]].rev^=1;
    swap(tr[x].son[0],tr[x].son[1]);
    tr[x].rev=0;
  }
  if(tr[x].flag)
  {
    tr[tr[x].son[0]].val=tr[tr[x].son[0]].sum=0;
    tr[tr[x].son[1]].val=tr[tr[x].son[1]].sum=0;
    tr[tr[x].son[0]].flag=tr[tr[x].son[1]].flag=1;
    tr[x].flag=0;
  }
}
 
void Rotate(int x)
{
  int y=tr[x].fa; int z=tr[y].fa;
  int a=tr[y].son[1]==x; int b=tr[z].son[1]==y;
  int g=tr[x].son[a^1];
  if(!Is_root(y)) tr[z].son[b]=x; tr[x].fa=z;
  tr[x].son[a^1]=y; tr[y].fa=x;
  tr[y].son[a]=g; if(g) tr[g].fa=y;
  update(y);
}
 
stack<int>S;
void Preserve(int x)
{
  while(!Is_root(x)) S.push(x),x=tr[x].fa; S.push(x);
  while(!S.empty()) Push_down(S.top()),S.pop();
}
 
void Splay(int x)
{
  Preserve(x);
  while(!Is_root(x))
  {
    int y=tr[x].fa; int a=tr[y].son[1]==x;
    int z=tr[y].fa; int b=tr[z].son[1]==y;
    if(!Is_root(y))
    {
      if(a==b) Rotate(y);
      else Rotate(x);
    }
    Rotate(x);
  }
  update(x);
}
 
void Access(int x)
{
  int last=0;
  while(x!=0)
  {
    Splay(x);
    tr[x].son[1]=last; update(x);
    last=x;
    x=tr[x].fa;
  }
}
 
void Make_root(int x)
{
  Access(x);
  Splay(x);
  tr[x].rev^=1;
}
void Link(int x,int y)
{
  Make_root(y);
  tr[y].fa=x;
}
void Split(int x,int y)
{
  Make_root(x);
  Access(y);
  Splay(y);
}
 
int N,M,z;
struct Que{char ch; int x,y;}A[Maxn],Q[Maxn];
map<int,bool>Del[Maxn]; int fa[Maxn]; int Find(int x){if(x==fa[x]) return x; else return fa[x]=Find(fa[x]);}
 
void Makezero(int x,int y)
{
  int xx=Find(x); int yy=Find(y);
  if(xx!=yy)
  {
    N++; tr[N].sum=1; tr[N].val=1; fa[xx]=yy;
    Link(x,N); Link(N,y); Del[x][y]=Del[y][x]=1;
    return ;
  }
  Split(x,y); tr[y].sum=tr[y].val=0; tr[y].flag=1;
}
 
int Query(int x,int y){if(Find(x)!=Find(y)) return 1; Split(x,y); return tr[y].sum;}
 
int ans[Maxn];
 
int main()
{
  scanf("%d%d%d",&N,&M,&z);
  for(int i=1;i<=N;i++) fa[i]=i;
  for(int i=1;i<=M;i++) scanf("%d%d",&A[i].x,&A[i].y);
  for(int i=1;i<=z;i++)
  {
    scanf("\n%c",&Q[i].ch); scanf("%d%d",&Q[i].x,&Q[i].y);
    if(Q[i].ch=='Z') Del[Q[i].x][Q[i].y]=Del[Q[i].y][Q[i].x]=1;
  }
   
  for(int i=1;i<=M;i++)
  {
    int xx=Find(A[i].x); int yy=Find(A[i].y);
    if(Del[A[i].x][A[i].y]) continue;
    if(xx!=yy)
    {
      N++; tr[N].sum=1; tr[N].val=1; fa[xx]=yy;
      Link(A[i].x,N); Link(N,A[i].y); Del[A[i].x][A[i].y]=Del[A[i].y][A[i].x]=1;
    }
  }
   
  for(int i=1;i<=M;i++) if(!Del[A[i].x][A[i].y])
      Makezero(A[i].x,A[i].y);
  for(int i=z;i>=1;i--)
  {
    if(Q[i].ch=='Z') Makezero(Q[i].x,Q[i].y);
    else ans[i]=Query(Q[i].x,Q[i].y);
  }
   
  for(int i=1;i<=z;i++) if(Q[i].ch=='P') puts(ans[i]>0?"NIE":"TAK");
  return 0;
}
View Code 

 

选择

双倍经验好题!

#include<bits/stdc++.h>
using namespace std;
const int Maxn=300010;
struct node
{
  int son[2],fa,val,sum; bool rev,flag;
}tr[Maxn<<1];
bool Is_root(int x)
{
  if((tr[tr[x].fa].son[0]!=x)&&(tr[tr[x].fa].son[1]!=x)) return true;
  return false;
}
void update(int x){tr[x].sum=tr[tr[x].son[0]].sum+tr[tr[x].son[1]].sum+tr[x].val;}
void Push_down(int x)
{
  if(tr[x].rev)
  {
    tr[tr[x].son[1]].rev^=1; tr[tr[x].son[0]].rev^=1;
    swap(tr[x].son[0],tr[x].son[1]);
    tr[x].rev=0;
  }
  if(tr[x].flag)
  {
    tr[tr[x].son[0]].val=tr[tr[x].son[0]].sum=0;
    tr[tr[x].son[1]].val=tr[tr[x].son[1]].sum=0;
    tr[tr[x].son[0]].flag=tr[tr[x].son[1]].flag=1;
    tr[x].flag=0;
  }
}
void Rotate(int x)
{
  int y=tr[x].fa; int z=tr[y].fa;
  int a=tr[y].son[1]==x; int b=tr[z].son[1]==y;
  int g=tr[x].son[a^1];
  if(!Is_root(y)) tr[z].son[b]=x; tr[x].fa=z;
  tr[x].son[a^1]=y; tr[y].fa=x;
  tr[y].son[a]=g; if(g) tr[g].fa=y;
  update(y);
}
stack<int>S;
void Preserve(int x)
{
  while(!Is_root(x)) S.push(x),x=tr[x].fa; S.push(x);
  while(!S.empty()) Push_down(S.top()),S.pop();
}
void Splay(int x)
{
  Preserve(x);
  while(!Is_root(x))
  {
    int y=tr[x].fa; int a=tr[y].son[1]==x;
    int z=tr[y].fa; int b=tr[z].son[1]==y;
    if(!Is_root(y))
    {
      if(a==b) Rotate(y);
      else Rotate(x);
    }
    Rotate(x);
  }
  update(x);
}
void Access(int x)
{
  int last=0;
  while(x!=0)
  {
    Splay(x);
    tr[x].son[1]=last; update(x);
    last=x;
    x=tr[x].fa;
  }
}
void Make_root(int x){Access(x); Splay(x); tr[x].rev^=1;}
void Link(int x,int y){Make_root(y); tr[y].fa=x;}
void Split(int x,int y){Make_root(x); Access(y); Splay(y);}
int N,M,z;
struct Que{char ch; int x,y;}A[Maxn],Q[Maxn];
map<int,bool>Del[Maxn]; int fa[Maxn]; int Find(int x){if(x==fa[x]) return x; else return fa[x]=Find(fa[x]);}
void Makezero(int x,int y)
{
  int xx=Find(x); int yy=Find(y);
  if(xx!=yy)
  {
    N++; tr[N].sum=1; tr[N].val=1; fa[xx]=yy;
    Link(x,N); Link(N,y); Del[x][y]=Del[y][x]=1;
    return ;
  }
  Split(x,y); tr[y].sum=tr[y].val=0; tr[y].flag=1;
}
int Query(int x,int y){if(Find(x)!=Find(y)) return 1; Split(x,y); return tr[y].sum;}
int ans[Maxn];
int main()
{
  scanf("%d%d%d",&N,&M,&z);
  for(int i=1;i<=N;i++) fa[i]=i;
  for(int i=1;i<=M;i++) scanf("%d%d",&A[i].x,&A[i].y);
  for(int i=1;i<=z;i++)
  {
    scanf("\n%c",&Q[i].ch); scanf("%d%d",&Q[i].x,&Q[i].y);
    if(Q[i].ch=='Z') Del[Q[i].x][Q[i].y]=Del[Q[i].y][Q[i].x]=1;
  }
  for(int i=1;i<=M;i++)
  {
    int xx=Find(A[i].x); int yy=Find(A[i].y);
    if(Del[A[i].x][A[i].y]) continue;
    if(xx!=yy)
    {
      N++; tr[N].sum=1; tr[N].val=1; fa[xx]=yy;
      Link(A[i].x,N); Link(N,A[i].y); Del[A[i].x][A[i].y]=Del[A[i].y][A[i].x]=1;
    }
  }
  for(int i=1;i<=M;i++) if(!Del[A[i].x][A[i].y])
      Makezero(A[i].x,A[i].y);
  for(int i=z;i>=1;i--)
  {
    if(Q[i].ch=='Z') Makezero(Q[i].x,Q[i].y);
    else ans[i]=Query(Q[i].x,Q[i].y);
  }
  for(int i=1;i<=z;i++) if(Q[i].ch=='P') puts(ans[i]>0?"No":"Yes");
  return 0;
}
View Code

 

 

2017.3.21

[ZJOI2007]Hide 捉迷藏

以前记得用动态点分治抄过一发代码  好像没过..(有空再学回动态点分写一发)

我用线段树+括号序列 当作论文题

首先先用左右括号扩起来 连续的一段 左右括号抵消掉 剩下的括号数目就是答案 剩下 】表示要上去 【要下来

已经说了是括号序列(好像有点像先序遍历 但又不完全是)

考虑对这种图转移 一共维护七个值:

dis 表示这一段最远的距离

a 表示左边最大连续一段的】数量 如果有抵消掉的要减去

b 表示右边最大连续一段的【数量 同样的 如果有抵消要减去

lp rp  分别表示在一个黑点左边的一段 或在黑点右边的一段的 】】【【【这种背对的括号的总数目 (也可以理解会前缀后缀)

lm rm 分别表示在黑点左边 【的数目-】的数目 与 黑点右边  】的数目-【的数目

其实具体看岛娘与《数据结构的提炼与压缩》曹钦翔写的

void update(int u)
{
  int a=A[lc[u]]; int b=B[lc[u]]; int c=A[rc[u]]; int d=B[rc[u]];
  dis[u]=max(max(dis[lc[u]],dis[rc[u]]),max(rp[lc[u]]+lm[rc[u]],lp[rc[u]]+rm[lc[u]]));
  lp[u]=max(max(lp[rc[u]]-b+a,lm[rc[u]]+a+b),lp[lc[u]]);
  rp[u]=max(max(rp[lc[u]]-c+d,rm[lc[u]]+c+d),rp[rc[u]]);
  lm[u]=max(lm[lc[u]],lm[rc[u]]+b-a);
  rm[u]=max(rm[rc[u]],rm[lc[u]]+c-d);
  A[u]=a+max(c-b,0); B[u]=d+max(b-c,0);
}

直接看我update也行

#include <bits/stdc++.h>
using namespace std;
const int inf=1e9;
const int Maxn=100010;
struct node{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 V[Maxn*3]; int Vlen; int black; int P[Maxn];
void Dfs(int x)
{
  V[++Vlen]=-1; V[++Vlen]=0; P[x]=Vlen;
  for(int k=first[x];k!=-1;k=edge[k].next)
    if(!P[edge[k].y]) Dfs(edge[k].y);
  V[++Vlen]=-2;
}
int rt,tot,lc[Maxn*12],rc[Maxn*12],lm[Maxn*12],rm[Maxn*12],lp[Maxn*12],rp[Maxn*12],dis[Maxn*12],A[Maxn*12],B[Maxn*12];
void update(int u)
{
  int a=A[lc[u]]; int b=B[lc[u]]; int c=A[rc[u]]; int d=B[rc[u]];
  dis[u]=max(max(dis[lc[u]],dis[rc[u]]),max(rp[lc[u]]+lm[rc[u]],lp[rc[u]]+rm[lc[u]]));
  lp[u]=max(max(lp[rc[u]]-b+a,lm[rc[u]]+a+b),lp[lc[u]]);
  rp[u]=max(max(rp[lc[u]]-c+d,rm[lc[u]]+c+d),rp[rc[u]]);
  lm[u]=max(lm[lc[u]],lm[rc[u]]+b-a);
  rm[u]=max(rm[rc[u]],rm[lc[u]]+c-d);
  A[u]=a+max(c-b,0); B[u]=d+max(b-c,0);
}
void Link(int &u,int L,int R,int k)
{
  if(!u) u=++tot;
  if(L==R)
  {
    if(V[k]==0) lm[u]=rm[u]=lp[u]=rp[u]=0;
    else lm[u]=rm[u]=lp[u]=rp[u]=-inf;
    if(V[k]==-1) B[u]=1;
    if(V[k]==-2) A[u]=1;
    return ;
  }
  int mid=(L+R)>>1;
  if(k<=mid) Link(lc[u],L,mid,k);
  else Link(rc[u],mid+1,R,k);
  update(u);
}
  
int N,M;
int main()
{
  scanf("%d",&N); 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);}
  Vlen=0; memset(V,0,sizeof(V)); black=N; Dfs(1);
  for(int i=1;i<=Vlen;i++) Link(rt,1,Vlen,i);
  scanf("%d",&M);
  for(int i=1;i<=M;i++)
  {
    char st; scanf("\n%c",&st);
    if(st=='G')
    {
      if(black==0) printf("-1\n");
      if(black==1) printf("0\n");
      else printf("%d\n",dis[rt]);
    }
    else
    {
      int x; scanf("%d",&x);
      if(V[P[x]]) black++; else black--;
      V[P[x]]^=1;
      Link(rt,1,Vlen,P[x]);
    }
  }
  return 0;
}
View Code

 

 

装备合成

这道题一开始好开心以为自己想到解法开400棵线段树维护一个树剖 按原树建出来 然后用并查集压缩路径..

码了140行才发现 最大值的影响处理不了

 

膜liao

 

好劲啊...................

每次装备合成新建一个点 连向两个子节点 可以知道每次询问就询问这个点的子树 每次合并每个点就跳到它合并的点 代表以后要询问这个子树

liao:你没做个这种合成 两个点合到一个点的题吗?

改的话只要改最底层即可 上面的都会影响到

#include<bits/stdc++.h>
using namespace std;
const int Maxn=1510;
const int Maxm=410;
struct node
{
  int x,y,next;
}edge[Maxn*10]; int len,first[Maxn*10];
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,M,q; int A[Maxn][Maxm];
struct QQ
{
  int k,a,b,c;
}Q[Maxn*Maxm];
int now[Maxn*10]; int fa[Maxn*10]; int S[Maxn*10],E[Maxn*10],id;
void Dfs(int x)
{
  S[x]=++id;
  for(int k=first[x];k!=-1;k=edge[k].next) Dfs(edge[k].y);
  E[x]=id;
}
 
int tot,rt[Maxm],lc[Maxn*Maxm*10],rc[Maxn*Maxm*10],C[Maxn*Maxm*10];
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;
  if(k<=mid) Link(lc[u],L,mid,k,c);
  else Link(rc[u],mid+1,R,k,c);
  C[u]=max(C[lc[u]],C[rc[u]]);
}
int Query(int u,int L,int R,int l,int r)
{
  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 max(Query(lc[u],L,mid,l,mid),Query(rc[u],mid+1,R,mid+1,r));
}
int main()
{
  scanf("%d%d%d",&N,&M,&q); int n=N;
  for(int i=1;i<=N;i++){now[i]=i; for(int j=1;j<=M;j++) scanf("%d",&A[i][j]);}
  len=0; memset(first,-1,sizeof(first));
  for(int i=1;i<=q;i++) scanf("%d%d%d%d",&Q[i].k,&Q[i].a,&Q[i].b,&Q[i].c);
  for(int i=1;i<=q;i++)
  {
    if(Q[i].k==1)
    {
      N++; fa[now[Q[i].a]]=fa[now[Q[i].b]]=N;
      ins(N,now[Q[i].a]); ins(N,now[Q[i].b]);
      now[Q[i].b]=N;
    }
  }
  N++;
  for(int i=1;i<=n;i++)
    if(!fa[now[i]]){ins(N,now[i]); fa[now[i]]=N;}
  Dfs(N); tot=0;
  for(int i=1;i<=n;i++)
  {
    now[i]=i;
    for(int j=1;j<=M;j++)
      Link(rt[j],1,id,S[i],A[i][j]);
  }
   
  for(int i=1;i<=q;i++)
  {
    if(Q[i].k==1) now[Q[i].b]=fa[now[Q[i].b]];
    else if(Q[i].k==2) printf("%d\n",Query(rt[Q[i].b],1,id,S[now[Q[i].a]],E[now[Q[i].a]]));
    else if(Q[i].k==3) Link(rt[Q[i].b],1,id,S[Q[i].a],Q[i].c);
  }
  return 0;
}
View Code

 

考虑三种情况 和根重合 在根和原根的链上 其它

#include <bits/stdc++.h>
using namespace std;
const int Maxn=100010;
struct node
{
  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 N,M; int Val[Maxn]; int fa[Maxn][20]; int S[Maxn],E[Maxn]; int id; int dep[Maxn];
void Dfs(int x)
{
  S[x]=++id;
  for(int k=first[x];k!=-1;k=edge[k].next)
  {
    int y=edge[k].y;
    dep[y]=dep[x]+1;
    Dfs(y);
  }
  E[x]=id;
}
 
int rt,tot,lc[Maxn*4],rc[Maxn*4],C[Maxn*4];
void Link(int &u,int L,int R,int k,int c)
{
  if(!u) u=++tot,C[u]=INT_MAX;
  if(L==R){C[u]=c; 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);
  C[u]=min(C[lc[u]],C[rc[u]]);
}
int Query(int u,int L,int R,int l,int r)
{
  if(r<l) return INT_MAX;
  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 root;
int main()
{
  scanf("%d%d",&N,&M); len=0; memset(first,-1,sizeof first);
  memset(dep,0,sizeof dep);
  for(int i=1;i<=N;i++)
  {
    int x,y; scanf("%d%d",&x,&y);
    if(x==0) root=i; else fa[i][0]=x,ins(x,i);
    Val[i]=y;
  }
  memset(S,0,sizeof S); memset(E,0,sizeof E); id=0;
  Dfs(root);
   
  for(int i=1;i<=18;i++) for(int j=1;j<=N;j++) fa[j][i]=fa[fa[j][i-1]][i-1];
   
  tot=0; for(int i=1;i<=N;i++) Link(rt,1,N,S[i],Val[i]);
  for(int i=1;i<=M;i++)
  {
    char st; scanf("\n%c",&st);
    if(st=='V')
    {
      int x,y; scanf("%d%d",&x,&y);
      Link(rt,1,N,S[x],y);
    }
    else if(st=='E'){int x; scanf("%d",&x); root=x;}
    else
    {
      int x; scanf("%d",&x); bool bk=false;
      int deep=dep[root]-dep[x];
      int p=root;
       
      int deeps=dep[root]-dep[x]-1;
      int pson=root;
       
      if(x==root){printf("%d\n",C[1]); continue;}
      if(dep[x]<dep[root])
      {
        for(int i=18;i>=0;i--) if(deep>=(1<<i)) deep-=(1<<i),p=fa[p][i];
        if(p==x) bk=true;
      }
      if(bk)
      {
        for(int i=18;i>=0;i--) if(deeps>=(1<<i)) deeps-=(1<<i),pson=fa[pson][i];
        printf("%d\n",min(Query(rt,1,N,1,S[pson]-1),Query(rt,1,N,E[pson]+1,N)));
      }
      else printf("%d\n",Query(rt,1,N,S[x],E[x]));
    }
  }
  return 0;
}
View Code

 

 

雨天的尾巴

好像是一道经典的题目 首先我们考虑最差的情况 就是一条链 然后每次都是整条链增加 这样的话要增加好多次 所以不可行

考虑减少增加次数 其实树结构也可以关于点和点合并 那么就在x y标记 然后LCA(x,y) fa[LCA(x,y)]打减标记

然后合并即可 合并要从下到上

我:什么从下到上合并 不是很慢吗?

其实不的 考虑每个一共有400000条散的链 没有的地方照样不用合 其实也就是logn 自下到上也是如此

#include <bits/stdc++.h>
using namespace std;
const int Maxn=100010;
const int inf=1e9;
struct node
{
  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]; int fa[Maxn][20];
map<int,int>mp; int _=0; int Hash[Maxn]; int A[Maxn],Alen=0;
void Dfs(int x)
{
  for(int k=first[x];k!=-1;k=edge[k].next)
  {
    int y=edge[k].y;
    if(fa[x][0]!=y)
    {
      fa[y][0]=x;
      dep[y]=dep[x]+1;
      Dfs(y);
    }
  }
}
 
int LCA(int x,int y)
{
  if(dep[x]<dep[y]) swap(x,y);
  int deep=dep[x]-dep[y];
  for(int i=18;i>=0;i--) if(deep>=(1<<i)) deep-=(1<<i),x=fa[x][i];
  if(x==y) return x;
  for(int i=18;i>=0;i--) if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
  return fa[x][0];
}
 
int lc[Maxn*55],rc[Maxn*55],C[Maxn*55],P[Maxn*55]; int tot,rt[Maxn];
void update(int u)
{
  if(C[lc[u]]<C[rc[u]]){C[u]=C[rc[u]]; P[u]=P[rc[u]];}
  else{C[u]=C[lc[u]]; P[u]=P[lc[u]];}
}
 
void Link(int &u,int L,int R,int k,int c)
{
  if(!u) u=++tot;
  if(L==R){C[u]+=c; P[u]=k; 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);
  update(u);
}
 
int ans[Maxn];
void Merge(int &u1,int u2,int L,int R)
{
  if(!u2) return ;
  if(!u1){u1=u2; return ;}
  if(L==R){C[u1]+=C[u2]; return ;}
  int mid=(L+R)>>1;
  Merge(lc[u1],lc[u2],L,mid); Merge(rc[u1],rc[u2],mid+1,R);
  update(u1);
}
 
void Dfs1(int x)
{
  for(int k=first[x];k!=-1;k=edge[k].next)
  {
    int y=edge[k].y;
    if(y!=fa[x][0])
    {
      Dfs1(y);
      Merge(rt[x],rt[y],1,_);
    }
  }
  ans[x]=Hash[P[rt[x]]];
}
int N,M;
struct QQ
{
  int x,y,d;
}Q[Maxn];
int main()
{
  scanf("%d%d",&N,&M); 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);}
  Dfs(1);
  for(int i=1;i<=18;i++) for(int j=1;j<=N;j++) fa[j][i]=fa[fa[j][i-1]][i-1];
  tot=0; _=0;
  for(int i=1;i<=M;i++)
  {
    scanf("%d%d%d",&Q[i].x,&Q[i].y,&Q[i].d);
    A[++Alen]=Q[i].d;
  }
  sort(A+1,A+Alen+1);
  _=unique(A+1,A+Alen+1)-(A+1);
  for(int i=1;i<=_;i++) mp[A[i]]=i,Hash[i]=A[i];
  for(int i=1;i<=M;i++)
  {
    int x=Q[i].x; int y=Q[i].y; int d=Q[i].d;
    Link(rt[x],1,_,mp[d],1); Link(rt[y],1,_,mp[d],1);
    int p=LCA(x,y);
    Link(rt[p],1,_,mp[d],-1);
    if(p!=1) Link(rt[fa[p][0]],1,_,mp[d],-1);
  }
  Dfs1(1);
  for(int i=1;i<=N;i++) printf("%d\n",ans[i]);
  return 0;
}
View Code

 2017.3.22

早上写了一道恶心的题

 

[WF2011]Mummy Madness

想一想这道题 你走有余地是不是某个步数你走到的地方别人都不能走到

这样的话很快想到矩阵覆盖问题

OMG又是扫描线(很少打..)
没办法 线段树的扫描线注意一点地方:

1.维护两个标记 一个是bo表示这地方有没有覆盖 一个是c表示这个地方被覆盖了多少次 当然被覆盖多少次是精确覆盖 也就是刚好找到的那个区间下面不算

bo标记要上传 c标记既不上传 又不下放

2.维护两条边是 一条flag=1 一条flag=-1 然后flag=-1的边要往上建一点 因为再上去一点才不覆盖到

3.千万不能随便离散化(后果不敢设想,有可能中间的有一些点被忽略了等等)

4.如果是维护横坐标的 纵坐标相同的一起做 以免询问中出现麻烦

然后只算覆盖的区间 二分答案

#include <bits/stdc++.h>
using namespace std;
const int Maxn=1e5+10;
const int inf=1e6+1; pair<int,int>Point[Maxn];
struct node
{
  int y,x1,x2,opt;
  node(){}
  node(int _y,int _x1,int _x2,int _opt){y=_y; x1=_x1; x2=_x2; opt=_opt;}
}Line[Maxn*2]; int len; pair<int,int>S=make_pair(0,0);
bool Cmp(const node &x,const node &y){if(x.y!=y.y) return x.y<y.y; return x.opt>y.opt;}
 
int rt,tot=0,lc[inf*11],rc[inf*11],C[inf*11]; bool bo[inf*11];
int N;
 
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]+=c;
    if(C[u]) bo[u]=1; else bo[u]=bo[lc[u]]&bo[rc[u]];
    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);
  if(C[u]) bo[u]=1; else bo[u]=bo[lc[u]]&bo[rc[u]];
}
 
bool Check(int k)
{
  len=0;
  for(int i=1;i<=N;i++)
  {
    if((Point[i].first+k<-k)||(Point[i].first-k>k)) continue;
    Line[++len]=node(Point[i].second-k,Point[i].first-k,Point[i].first+k,1);
    Line[++len]=node(Point[i].second+k+1,Point[i].first-k,Point[i].first+k,-1);
  }
 
  Line[++len]=node(-k,-k,k,2);
  Line[++len]=node(k+1,-k,k,3);
 
  sort(Line+1,Line+len+1,Cmp);
 
  for(int i=1;i<=tot;i++) C[i]=bo[i]=lc[i]=rc[i]=0;
  tot=rt=0;
 
  int Lx=INT_MIN,Rx=INT_MAX; int j=1;
  for(int i=1;i<=len;i=j)
  {
    while(Line[j].y==Line[i].y&&j<=len)
    {
      int x1=Line[j].x1; int x2=Line[j].x2;
      if(Line[j].opt==3) return 1;
      if(Line[j].opt==2){Lx=Line[j].x1; Rx=Line[j].x2;}
      if((Line[j].opt==1)||(Line[j].opt==-1))
        Link(rt,0,2*k,max(0,x1+k),min(x2+k,2*k),Line[j].opt);
      j++;
    }
    if(Lx!=INT_MIN&&Rx!=INT_MAX)
        if(!bo[rt]) return 0;
  }
}
int main()
{
  int Tcase=0;
  while(scanf("%d",&N)!=EOF)
  {
    if(N==-1) break;
    printf("Case %d: ",++Tcase);
    for(int i=1;i<=N;i++) scanf("%d%d",&Point[i].first,&Point[i].second);
    int L=1; int R=1000000; int ret=-1;
    while(L<=R)
    {
      int mid=(L+R)>>1;
      if(Check(mid)){R=mid-1; ret=mid;}
      else L=mid+1;
    }
    if(ret==-1) printf("never\n");
    else printf("%d\n",ret);
  }
  return 0;
}
/*
4
-3 5
3 4
-6 -2
1 -5
1
0 -1
-1
*/
View Code

 

晚上让MTCHan帮我下了Sublime3 真的很好用 缩行 对比 编译 主题都比notepad++要好 虽说比赛不能用 听说Mac OSX效果更佳? 回去试试

教主的魔法

很有趣的一道题 看似是很多数据结构一起维护 其实做过之前的题不难想到分块 但是分块之后呢

我:每个块开一棵权值线段树

wph:重构..

发现我的方法是很垃圾的 空间不允许 重构的话也就是N^0.5时间一次 细节有点恶心

#include <bits/stdc++.h>
using namespace std;
const int Maxn=1000010;
const int inf=1000000000;
int N,M; int H[Maxn]; int q;
int rt[1010],B[Maxn]; int Sum[Maxn];
int Count(int l,int r,int c)
{
  int ret=r+1; int L=l; int R=r;
  while(L<=R)
  {
    int mid=(L+R)>>1;
    if(B[mid]>=c){R=mid-1; ret=mid;}
    else L=mid+1;
  }
  return r-ret+1;
}
int main()
{
  scanf("%d%d",&N,&M); for(int i=1;i<=N;i++) scanf("%d",&H[i]); q=(int)sqrt(N);
  for(int i=1;i<=N;i++) B[i]=H[i];
  for(int i=1;i<=q+1;i++) sort(B+min((i-1)*q,N+1),B+min(i*q,N+1));
  for(int j=1;j<=M;j++)
  {
    char st; scanf("\n%c",&st);
    if(st=='A')
    {
      int L,R,C,s=0; scanf("%d%d%d",&L,&R,&C);
      for(int i=(L/q)+1;i<(R/q);i++) s+=Count(q*i,min(q*(i+1)-1,N),C-Sum[i]);
 
      if((L/q)==(R/q)) for(int i=L;i<=R;i++) s+=((H[i]+Sum[i/q])>=C);
      else
      {
        for(int i=L;i<min((L/q+1)*q,N+1);i++) s+=((H[i]+Sum[i/q])>=C);
        for(int i=(R/q)*q;i<=R;i++) s+=((H[i]+Sum[i/q])>=C);
      }
      printf("%d\n",s);
    }
    else if(st=='M')
    {
      int L,R,C; scanf("%d%d%d",&L,&R,&C);
      for(int i=(L/q)+1;i<(R/q);i++) Sum[i]+=C;
 
      if((L/q)==(R/q))
      {
        for(int i=L;i<=R;i++) H[i]+=C;
        for(int i=(L/q)*q;i<min((L/q+1)*q,N+1);i++) B[i]=H[i];
        sort(B+(L/q)*q,B+min((L/q+1)*q,N+1));
      }
      else
      {
        for(int i=L;i<min((L/q+1)*q,N+1);i++) H[i]+=C;
        for(int i=(L/q)*q;i<min((L/q+1)*q,N+1);i++) B[i]=H[i];
        sort(B+(L/q)*q,B+min((L/q+1)*q,N+1));
 
        for(int i=(R/q)*q;i<=R;i++) H[i]+=C;
        for(int i=(R/q)*q;i<min((R/q+1)*q,N+1);i++) B[i]=H[i];
        sort(B+(R/q)*q,B+min((R/q+1)*q,N+1));
      }
    }
  }
  return 0;
}
View Code

 

2017.3.23

K-D-Sequence

昨天晚上被wph D了 说我不用脑子

题目看似很难 发现我看错题意以为公差是随便的 好难啊

那么公差一定的话 好像也不简单

首先确定一个序列是否是等差数列 要满足

1.相临的数的绝对值的差一定$mod D=0$

2.满足

$\lfloor \frac{max\left\{ al...ar \right\} }{D} \rfloor-\lfloor \frac{min\left\{ al...ar \right\} }{D} \rfloor \leqslant R-L+k$

3.对于序列不能有相同的 上式成立只能在自然数范围内 最小最大值为-6 和 6 D为4显然不满足

对于上式 先用预处理 处理出每个位置最远能影响到前面哪里 max和min都要 (开两个单调栈预处理)

对于线段树 先找到合法区间$[last,i]$ 再用线段树分治找到满足不等式的最左端点

维护合法区间 我们需要维护三个值和两个标记

$\lfloor \frac{Max}{D} \rfloor+j$   $-\lfloor \frac{Min}{D} \rfloor+j$   $\lfloor \frac{Max}{D} \rfloor-\lfloor \frac{Min}{D} \rfloor+j$

发现每个区间如果改掉$\lfloor \frac{Max}{D} \rfloor$或$-\lfloor \frac{Min}{D} \rfloor$ 区间最左边的最小 即j最小

打标记也就打$\lfloor \frac{Max}{D} \rfloor$或$-\lfloor \frac{Min}{D} \rfloor$的标记

具体看代码吧

#include <bits/stdc++.h>
using namespace std;
const int Maxn=2*1e5+10;
const int inf=1e9;
int N,K,D; int A[Maxn];
 
int lmin[Maxn],lmax[Maxn]; stack<pair<int,int> >S;
void Pre()
{
  while(!S.empty()) S.pop();
  S.push(make_pair(0,INT_MAX));
  for(int i=1;i<=N;i++)
  {
    while(A[i]>S.top().second) S.pop();
    lmax[i]=S.top().first+1; S.push(make_pair(i,A[i]));
  }
 
  while(!S.empty()) S.pop();
  S.push(make_pair(0,INT_MIN));
  for(int i=1;i<=N;i++)
  {
    while(A[i]<S.top().second) S.pop();
    lmin[i]=S.top().first+1; S.push(make_pair(i,A[i]));
  }
}
 
int rt,tot,lc[Maxn*4],rc[Maxn*4],Maxc[Maxn*4],Minc[Maxn*4],C[Maxn*4],ln[Maxn*4],lx[Maxn*4];
void Link(int &u,int L,int R,int k)
{
  if(!u) u=++tot,lx[u]=INT_MAX,ln[u]=INT_MAX;
  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);
}
 
void Push_down(int u,int L,int R)
{
  if(ln[u]!=INT_MAX)
  {
    int mid=(L+R)>>1;
    ln[lc[u]]=ln[rc[u]]=ln[u];
    Minc[lc[u]]=-ln[u]+L; Minc[rc[u]]=-ln[u]+mid+1;
    C[lc[u]]=Maxc[lc[u]]-ln[u]; C[rc[u]]=Maxc[rc[u]]-ln[u];
    ln[u]=INT_MAX;
  }
  if(lx[u]!=INT_MAX)
  {
    int mid=(L+R)>>1;
    lx[lc[u]]=lx[rc[u]]=lx[u];
    Maxc[lc[u]]=lx[u]+L; Maxc[rc[u]]=lx[u]+mid+1;
    C[lc[u]]=Minc[lc[u]]+lx[u]; C[rc[u]]=Minc[rc[u]]+lx[u];
    lx[u]=INT_MAX;
  }
}
 
void Change1(int u,int L,int R,int l,int r,int c)
{
  if(L==l&&R==r){Maxc[u]=c+L; C[u]=c+Minc[u]; lx[u]=c; return ;}
  int mid=(L+R)>>1;
  Push_down(u,L,R);
  if(r<=mid) Change1(lc[u],L,mid,l,r,c);
  else if(l>mid) Change1(rc[u],mid+1,R,l,r,c);
  else
  {
    Change1(lc[u],L,mid,l,mid,c);
    Change1(rc[u],mid+1,R,mid+1,r,c);
  }
  Maxc[u]=min(Maxc[lc[u]],Maxc[rc[u]]);
  Minc[u]=min(Minc[lc[u]],Minc[rc[u]]);
  C[u]=min(C[lc[u]],C[rc[u]]);
}
 
void Change2(int u,int L,int R,int l,int r,int c)
{
  if(L==l&&R==r){Minc[u]=-c+L; C[u]=Maxc[u]-c; ln[u]=c; return ;}
  int mid=(L+R)>>1;
  Push_down(u,L,R);
  if(r<=mid) Change2(lc[u],L,mid,l,r,c);
  else if(l>mid) Change2(rc[u],mid+1,R,l,r,c);
  else
  {
    Change2(lc[u],L,mid,l,mid,c);
    Change2(rc[u],mid+1,R,mid+1,r,c);
  }
  Maxc[u]=min(Maxc[lc[u]],Maxc[rc[u]]);
  Minc[u]=min(Minc[lc[u]],Minc[rc[u]]);
  C[u]=min(C[lc[u]],C[rc[u]]);
}
 
int Dfs(int u,int L,int R,int k)
{
  int mid=(L+R)>>1;
  if(L==R){if(C[u]>k) return INT_MAX; else return L;}
  Push_down(u,L,R);
  if(C[lc[u]]<=k) return Dfs(lc[u],L,mid,k);
  else return Dfs(rc[u],mid+1,R,k);
}
 
int Query(int u,int L,int R,int l,int r,int k)
{
  if(L==l&&R==r) return Dfs(u,L,R,k);
  Push_down(u,L,R);
  int mid=(L+R)>>1;
  if(r<=mid) return Query(lc[u],L,mid,l,r,k);
  else if(l>mid) return Query(rc[u],mid+1,R,l,r,k);
  else
  {
    return min(Query(lc[u],L,mid,l,mid,k),Query(rc[u],mid+1,R,mid+1,r,k));
  }
}
 
map<int,int>mp;
 
int main()
{
  scanf("%d%d%d",&N,&K,&D);
  for(int i=1;i<=N;i++) scanf("%d",&A[i]),A[i]+=inf;
  if(D==0)
  {
    int j,px,py,len=0;
    for(int i=1;i<=N;i=j)
    {
      j=i; while(A[j]==A[i]&&j<=N) j++;
      if(j-i>len) len=j-i,px=i,py=j-1;
    }printf("%d %d\n",px,py);
  }
  else
  {
    Pre(); rt=tot=0;
    for(int i=1;i<=N;i++) Link(rt,1,N,i);
 
    int last=1;  int len=0,px,py;
    for(int i=1;i<=N;i++)
    {
      if((A[i]-A[i-1])%D!=0) last=i;
      if(mp[A[i]]) last=max(last,mp[A[i]]+1); mp[A[i]]=i;
      Change1(rt,1,N,max(last,lmax[i]),i,A[i]/D);
      Change2(rt,1,N,max(last,lmin[i]),i,A[i]/D);
      int x=Query(rt,1,N,last,i,K+i);
      if(i-x+1>len) len=i-x+1,px=x,py=i;
    }
    printf("%d %d\n",px,py);
  }
  return 0;
}
/*
6 1 2
4 3 2 8 6 2
*/
View Code

 

Gty的游戏

昨天下午码了这题一直没发数据然后没调 发了数据之后才发现自己太垃圾了

这道题就是splay维护dfs序 不要按点的编号来建 首先跑一次dfs 每个点找到进去时的位置和出来时的位置 也就是每个点拆成两个点 两个点之间的一段的dfs序就是这个点子树下的

考虑博弈 距离这层为偶数层的可以不管 奇数层的就像简单NIM游戏一样取 当然了 每次只能取小于等于L的 直接MOD (L+1) 理由很简单 你取多少我都能筹够L+1和你抵消

题目就变成了子树奇数层的xor和 和动态开点 动态开点只需找到你父亲的dfs序插在右边即可

维护层数xor和就用两个变量存一下就好了 当然每个点还要维护自己原来在树上是奇数层还是偶数层

各种恶心... 记得插完点之后要splay回上去 这样的话整棵树才能维护

#include <bits/stdc++.h>
using namespace std;
const int Maxn=1e5+10;
struct node
{
  int s[2],son[2],v[2],fa,typ;
}tr[Maxn*2]; int tot,rot;
int N,M,L; vector<int>G[Maxn]; int Val[Maxn]; int S[Maxn*2]; int dep[Maxn]; int id=0;
int Pos[Maxn][2]; map<int,int>mp;
void Dfs(int x)
{
  S[++id]=x;
  for(int i=0;i<G[x].size();i++)
  {
    int y=G[x][i];
    dep[y]=dep[x]^1;
    Dfs(y);
  }
  S[++id]=-x;
}
  
void update(int u)
{
  tr[u].s[0]=tr[u].v[0]; tr[u].s[1]=tr[u].v[1];
  if(tr[u].son[0]) tr[u].s[0]^=tr[tr[u].son[0]].s[0],tr[u].s[1]^=tr[tr[u].son[0]].s[1];
  if(tr[u].son[1]) tr[u].s[0]^=tr[tr[u].son[1]].s[0],tr[u].s[1]^=tr[tr[u].son[1]].s[1];
}
  
void Rotate(int u)
{
  int y=tr[u].fa; int z=tr[y].fa;
  int a=tr[y].son[1]==u; int b=tr[z].son[1]==y;
  int g=tr[u].son[a^1];
  tr[z].son[b]=u; tr[u].fa=z;
  tr[u].son[a^1]=y; tr[y].fa=u;
  tr[y].son[a]=g; if(g) tr[g].fa=y;
  update(y);
}
  
void Splay(int u,int anc)
{
  while(tr[u].fa!=anc)
  {
    int y=tr[u].fa; int z=tr[y].fa;
    if(z==anc) Rotate(u);
    else
    {
      int a=tr[y].son[1]==u; int b=tr[z].son[1]==y;
      if(a==b) Rotate(y),Rotate(u);
      else Rotate(u),Rotate(u);
    }
  }
  if(anc==0) rot=u;
  update(u);
}
  
int Build(int l,int r,int fa)
{
  if(l>r) return 0;
  int mid=(l+r)>>1;
  if(S[mid]>0)
  {
    Pos[S[mid]][0]=mid;
    tr[mid].v[dep[S[mid]]]=Val[S[mid]];
  }
  else Pos[-S[mid]][1]=mid;
  tr[mid].fa=fa;
  tr[mid].typ=dep[abs(S[mid])];
  tr[mid].son[0]=Build(l,mid-1,mid);
  tr[mid].son[1]=Build(mid+1,r,mid);
  update(mid);
  return mid;
}
  
void Insert(int u,int v,int c)
{
  Splay(Pos[u][0],0);
  Splay(Pos[u][1],Pos[u][0]);
  u=Pos[u][1]; while(tr[u].son[0]) u=tr[u].son[0];
  tr[u].son[0]=++tot; Pos[v][0]=tot; tr[tot].typ=dep[v]; tr[tot].fa=u;
  tr[tot].v[dep[v]]=c; u=tr[u].son[0];
  tr[u].son[1]=++tot; Pos[v][1]=tot; tr[tot].typ=dep[v]; tr[tot].fa=u;
  Splay(tot,0);
}
  
int main()
{
  scanf("%d%d",&N,&L);
  for(int i=1;i<=N;i++) scanf("%d",&Val[i]),Val[i]%=(L+1),mp[i]=i;
  for(int i=1;i<N;i++){int x,y; scanf("%d%d",&x,&y); G[x].push_back(y);}
  id=0; Dfs(1);
  rot=Build(1,id,0); tot=N<<1;
  scanf("%d",&M); int last=0;
  for(int i=1;i<=M;i++)
  {
    int opt; scanf("%d",&opt);
    if(opt==1)
    {
      int x; scanf("%d",&x); x^=last;
      x=mp[x];
      Splay(Pos[x][0],0);
      Splay(Pos[x][1],Pos[x][0]);
      int typ=tr[Pos[x][1]].typ;
      int ans=tr[tr[Pos[x][1]].son[0]].s[typ^1];
      puts(ans?"MeiZ":"GTY");
      last+=(ans>0);
    }
    else if(opt==2)
    {
      int x,y; scanf("%d%d",&x,&y); x^=last; y^=last;
      x=mp[x];
      Splay(Pos[x][0],0);
      int typ=tr[Pos[x][0]].typ;
      tr[Pos[x][0]].v[typ]=y%(L+1); update(Pos[x][0]);
    }
    else if(opt==3)
    {
      int u,v,c; scanf("%d%d%d",&u,&v,&c);
      u^=last; v^=last; c^=last;
      c%=(L+1);
      u=mp[u]; v=mp[v]=++N; dep[v]=dep[u]^1;
      Insert(u,v,c);
    }
  }
  return 0;
}
View Code

 

数据结构篇结束了! 接下来是dp专题

 

[SDOI2009]学校食堂Dining

之前就ac了 然后又做了一遍

F[i][j][k]表示i-1个同学取完饭后 包括自己的八个同学状态 最后是谁取反的

转移即可 注意边界

#include <bits/stdc++.h>
using namespace std;
const int Maxn=1010;
int C; pair<int,int>pr[Maxn]; int N;
int F[Maxn][256][16]; int bit[10];
int main()
{
  scanf("%d",&C);
  while(C--)
  {
      scanf("%d",&N); for(int i=1;i<=N;i++) scanf("%d%d",&pr[i].first,&pr[i].second);
      memset(F,63,sizeof(F));
      int fr=7; for(int i=1;i<=fr;i++) fr=min(fr,i+pr[i].second);
      for(int i=0;i<=8;i++) bit[i]=(1<<i);
      for(int i=1;i<=fr;i++) F[1][bit[i-1]][8+i-1]=0;
      for(int i=1;i<=N;i++)
      {
        for(int j=0;j<256;j++)
        {
          for(int k=1;k<=15;k++)
          {
            if(F[i][j][k]<99999999)
            {
              int fr=15;
              for(int kk=8;kk<=fr;kk++) if(!(j&bit[kk-8])) fr=min(fr,pr[i+(kk-8)].second+kk);
                for(int kk=8;kk<=fr;kk++)
                  if(!(j&bit[kk-8]))
                  {
                    F[i][j|bit[kk-8]][kk]=min(F[i][j|bit[kk-8]][kk],
                    F[i][j][k]+((pr[i+(kk-8)].first|pr[i+(k-8)].first)-(pr[i+(kk-8)].first&pr[i+(k-8)].first)));
                  }
                if(j&bit[0]) F[i+1][j>>1][k-1]=min(F[i+1][j>>1][k-1],F[i][j][k]);
            }
          }
        }
      }

      int minx=INT_MAX;
      for(int i=1;i<=7;i++) minx=min(minx,F[N+1][0][i]);
      printf("%d\n",minx);
  }
  return 0;
}
View Code 

 

[SCOI2009]粉刷匠

奶牛省选题 发现行独立

F[i][j] i行j次最大 G[j][r] 每行前r个用j次 num[l][r] 一次刷l到r的贡献

再背包一下

#include <bits/stdc++.h>
using namespace std;
const int Maxn=60;
char st[Maxn][Maxn]; int N,M,T;
int F[Maxn][Maxn];
int G[Maxn][Maxn];
int num[Maxn][Maxn];

int A[Maxn*Maxn],B[Maxn*Maxn];
int main()
{
  scanf("%d%d%d",&N,&M,&T);
  for(int i=1;i<=N;i++) for(int j=1;j<=M;j++) scanf("\n%c",&st[i][j]);
  for(int i=1;i<=N;i++)
  {
    memset(F[i],0,sizeof(F[i]));
    memset(num,0,sizeof(num));
    for(int l=1;l<=M;l++)
    {
      int sb=0,sw=0;
      for(int r=l;r<=M;r++)
      {
        if(st[i][r]=='1') sw++;
        else sb++; num[l][r]=max(sw,sb);
      }
    }
    memset(G,0,sizeof(G));
    for(int l=0;l<=M;l++)
      for(int j=0;j<=l;j++)
          for(int r=l+1;r<=M;r++)
            G[r][j+1]=max(G[r][j+1],G[l][j]+num[l+1][r]);
    
    for(int j=0;j<=M;j++) F[i][j]=G[M][j];
  }

  memset(A,0,sizeof(A)); memset(B,0,sizeof(B));
  for(int i=1;i<=N;i++)
  {
    for(int j=0;j<=T;j++) A[j]=B[j];
    memset(B,0,sizeof(B));
    for(int j=0;j<=T;j++)
      for(int k=0;k<=M;k++)
        if(j+k<=T)
          B[j+k]=max(B[j+k],A[j]+F[i][k]);
  }
  int ans=0;
  for(int i=0;i<=T;i++) ans=max(ans,B[i]);
  return printf("%d\n",ans),0;
}
View Code 

 

2017.3.24

[JSOI2009]火星藏宝图

发现只有最靠近的点才会更新答案 因为$a^2+b^2<=\left( a+b \right)^2$

按纵坐标排序 横坐标再排 F[i]表示i列高度 dp[i]表示i列答案 从左到右高度减少即可

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL Maxn=200010;
struct node
{
  LL x,y,d;
  friend bool operator <(const node &x,const node &y)
  {
    if(x.y!=y.y) return x.y<y.y; return x.x<y.x;
  }
}
Point[Maxn];
LL N,M; LL F[Maxn]; LL dp[Maxn];
int main()
{
  scanf("%lld%lld",&N,&M);
  for(LL i=1;i<=N;i++) scanf("%lld%lld%lld",&Point[i].x,&Point[i].y,&Point[i].d);
  memset(F,0,sizeof(F)); memset(dp,-126,sizeof(dp));
  sort(Point+1,Point+N+1); F[1]=1; dp[1]=Point[1].d;
  for(LL i=2;i<=N;i++)
  {
    LL maxx=0; LL last=0; LL ans=-LLONG_MAX;
    for(LL j=Point[i].x;j>=1;j--)
    {
      if(F[j]>last)
      {
        ans=max(ans,dp[j]+Point[i].d-(Point[i].x-j)*(Point[i].x-j)-(Point[i].y-F[j])*(Point[i].y-F[j]));
        last=F[j];
      }
    }
    F[Point[i].x]=Point[i].y;
    dp[Point[i].x]=ans;
  }
  return printf("%lld\n",dp[M]),0;
}
View Code 

 

[CQOI2007]涂色paint

我好菜啊这道题都不会做..

首先我想的dp是 F[l][r]表示这一段的最大值 然后枚举中间点做底色 两边如果和底色相同的点之间的区间进行累加

发现是错误的比如数据: ACCDBABBDA 这样的话先染外面的A 里面的A最后再染一次

发现自己很垃圾不会做 膜cgh 发现两个区间合并 只要两边相同就可以减一

理由如下:

两边相同的话可以先用一次染完两边然后再搞中间

如果子区间也有两边都是一样的话 同样也可以把子区间两边给染了

子区间应该各种染色方式都出现过

#include <bits/stdc++.h>
using namespace std; const int Maxn=60; char st[Maxn]; int F[Maxn][Maxn]; int main() {scanf("%s",st+1); int N=strlen(st+1); memset(F,63,sizeof(F)); for(int i=1;i<=N;i++) F[i][i]=1; for(int L=1;L<=N;L++) for(int i=1;i<=N-L+1;i++) for(int p=i;p<i+L-1;p++) F[i][i+L-1]=min(F[i][i+L-1],F[i][p]+F[p+1][i+L-1]-(st[i]==st[i+L-1])); return printf("%d\n",F[1][N]),0; }
View Code 

 

[Ioi2007]Miners 矿工配餐

F[i][sta][stb]表示第i个两堆矿石的前两个状态

#include <bits/stdc++.h>
using namespace std;
const int Maxn=100010;
int F[2][16][16]; char st[Maxn];
int num(int x){if(st[x]=='M') return 1; if(st[x]=='F') return 2; if(st[x]=='B') return 3;}
int N; int sta[16][4]; int val[16][4];
int main()
{
  scanf("%d",&N);
  for(int i=1;i<=N;i++) scanf("\n%c",&st[i]);
  for(int i=0;i<16;i++)
      for(int j=1;j<=3;j++) sta[i][j]=((i*4)%16)+j;
  
  memset(val,0,sizeof(val));
  for(int i=0;i<16;i++)
  {
    int last1=i/4; int last2=i%4;
    for(int j=1;j<=3;j++)
    {
      val[i][j]++; if(last1!=0&&last1!=j) val[i][j]++;
      if(last2!=0&&last2!=j&last2!=last1) val[i][j]++;
    }
  }
  
  int ST=0; memset(F[ST],-63,sizeof(F[ST])); F[ST][0][0]=0;
  for(int i=0;i<N;i++)
  {
      memset(F[1-ST],-63,sizeof(F[1-ST]));
    int kk=num(i+1);
      for(int j=0;j<16;j++)
        for(int k=0;k<16;k++)
        {
          if(F[ST][j][k]>=0)
          {
            F[1-ST][sta[j][kk]][k]=max(F[1-ST][sta[j][kk]][k],F[ST][j][k]+val[j][kk]);
            F[1-ST][j][sta[k][kk]]=max(F[1-ST][j][sta[k][kk]],F[ST][j][k]+val[k][kk]);
          }
        }
      ST=1-ST;
  }

  int maxx=0;
  for(int i=0;i<16;i++) for(int j=0;j<16;j++) maxx=max(maxx,F[ST][i][j]);
  return printf("%d\n",maxx),0;
}
View Code 

 

字符串游戏

不会做 首先是考虑F[i][j]剩下多少个的 然后不会转移

膜cgh 考虑当所有能全部删掉的块求出来后 就可以用类似背包的东西求出答案

对于怎么求出所有的块我们考虑dp

F[i][j][k][l]表示区间[i,j]可以变成只剩下第k串的前l个位 但是算了一下要for 5次

所以的话我们可以考虑优化 因为都是bo数组 我们不妨改成int F[i][j][k]表示[i,j]可以变成只剩下第k串的位置 的二进制表示

转移的时候要注意 一开始初始化先匹配一个位置 但是注意一个位置就匹配完的串

for的时候不能按长度先for 也不能正着按i先for 只能倒着先for i 再正着for j 因为有可能一个点右边的串合了很长一段 再合当前点(隔了很长一段的意思)

#include <bits/stdc++.h>
using namespace std;
const int Maxn=151;
int F[Maxn][Maxn][31]; char str[Maxn]; char st[31][21]; int len[31];
bool c[Maxn][Maxn]; int pos[2000000];
int G[Maxn]; int low_bit(int x){return x&(-x);}
int main()
{
  scanf("%s",str+1); int N=strlen(str+1);
  int M; scanf("%d",&M);
  for(int i=1;i<=M;i++) scanf("%s",st[i]+1),len[i]=strlen(st[i]+1);

  memset(F,0,sizeof(F)); memset(c,0,sizeof(c));
  for(int i=1;i<=N;i++)
  {
    for(int k=1;k<=M;k++)
    {
      if(st[k][1]==str[i])
      {
        if(len[k]==1) c[i][i]=1;
        else F[i][i][k]=2;
      }
    }
    if(c[i][i]==1) for(int k=1;k<=M;k++) F[i][i][k]|=1;
  }

  pos[1]=0; for(int i=1;i<=20;i++) pos[1<<i]=i;

  for(int i=N;i>=1;i--)
  {
    for(int j=1;j<=N;j++)
    {
      if(c[i][j]==1)
        for(int k=1;k<=M;k++) F[i][j][k]|=1;
      for(int k=1;k<=M;k++)
      {
        if(F[i][j][k])
        {
          int bit; int l=F[i][j][k];
          while(l)
          {
            bit=low_bit(l); int p=pos[bit];
            for(int jj=j;jj<=N;jj++)
            {
              if((!c[j+1][jj])&&(jj!=j)) continue;
                if(str[jj+1]==st[k][p+1])
                {
                  if(p+1==len[k]) c[i][jj+1]=1;
                  else F[i][jj+1][k]|=(1<<(p+1));
                }
              }
              l-=bit;
          }
        }
      }
    }
  }

  memset(G,63,sizeof(G)); G[0]=0;
  for(int i=1;i<=N;i++)
  {
      G[i]=min(G[i],G[i-1]+1);
    for(int j=i;j<=N;j++) if(c[i][j]) G[j]=min(G[j],G[i-1]);
  }
  return printf("%d\n",G[N]),0;
}
View Code 

 

2017.3.25

【POJ Challenge】消失之物

听栋爷说FFT可以过。。。。。。。随便码了一发爽了一下 顺便复习一遍

FFT做法:顺着做一次 反着做一次 两个数组合并.. 本机跑要7 8s交上去AC

#include <bits/stdc++.h>
using namespace std;
const int Maxn=2010;
int N,M; int W[Maxn];
int F[Maxn][Maxn],G[Maxn][Maxn];
struct node
{
  double r,i;
  node(){}
  node(double _r,double _i){r=_r; i=_i;}
  node friend operator + (const node &x,const node &y){return node(x.r+y.r,x.i+y.i);}
  node friend operator - (const node &x,const node &y){return node(x.r-y.r,x.i-y.i);}
  node friend operator * (const node &x,const node &y){return node(x.r*y.r-x.i*y.i,x.r*y.i+y.r*x.i);}
}A[Maxn*5],B[Maxn*5]; int R[Maxn*5];
const double pi=acos(-1);
void DFT(node *a,int n,int fh)
{
  for(int i=0;i<n;i++) if(i>R[i]) swap(a[i],a[R[i]]);
  for(int i=1;i<n;i<<=1)
  {
    node wn=node(cos(pi/i),sin(pi/i)*fh);
    for(int j=0;j<n;j+=i<<1)
    {
      node w=node(1,0);
      for(int k=0;k<i;k++,w=w*wn)
      {
        node x=a[j+k],y=a[j+k+i]*w;
        a[j+k]=x+y; a[j+k+i]=x-y;
      }
    }
  }
}
void FFT(int *a,int *b)
{
  for(int i=0;i<=M;i++) A[i].r=a[i];
  for(int i=0;i<=M;i++) B[i].r=b[i];
  int n=M,m; m=n<<1; int L=0; for(n=1;n<=m;n<<=1) L++;
  for(int i=0;i<n;i++) R[i]=(R[i>>1]>>1)|((i&1)<<(L-1));
  DFT(A,n,1); DFT(B,n,1);
  for(int i=0;i<n;i++) A[i]=A[i]*B[i];
  DFT(A,n,-1);
  for(int i=1;i<=M;i++) printf("%d",int(((A[i].r+0.5)/n))%10); printf("\n");
  for(int i=0;i<n;i++) A[i].r=A[i].i=B[i].r=B[i].i=0;
}

int main()
{
  scanf("%d%d",&N,&M); for(int i=1;i<=N;i++) scanf("%d",&W[i]);
  memset(F,0,sizeof(F)); F[0][0]=1;
  for(int i=1;i<=N;i++)
  {
      for(int j=0;j<=M;j++) F[i][j]=F[i-1][j];
    for(int j=M;j>=W[i];j--)
    {
      if(F[i][j-W[i]]) F[i][j]+=F[i][j-W[i]];
      F[i][j]%=10;
    }
  }

  memset(G,0,sizeof(G)); G[N+1][0]=1;
  for(int i=N;i>=1;i--)
  {
      for(int j=0;j<=M;j++) G[i][j]=G[i+1][j];
    for(int j=M;j>=W[i];j--)
    {
      if(G[i][j-W[i]]) G[i][j]+=G[i][j-W[i]];
      G[i][j]%=10;
    }
  }

  for(int i=1;i<=N;i++)
      FFT(F[i-1],G[i+1]);
  return 0;
}
View Code

正解也是一个不难的dp 然而好像被吓到了 想不出来 C[i][j]表示不用i到达j F[j]表示总的背包 转移显然

#include <bits/stdc++.h>
using namespace std;
const int Maxn=2010; int F[Maxn]; int C[Maxn][Maxn]; int w[Maxn],N,M;
int main()
{
  scanf("%d%d",&N,&M); for(int i=1;i<=N;i++) scanf("%d",&w[i]); F[0]=1;
  for(int i=1;i<=N;i++)
    for(int j=M;j>=w[i];j--)
      F[j]+=F[j-w[i]],F[j]%=10;
  for(int i=1;i<=N;i++)
  {
    C[i][0]=1; for(int j=1;j<w[i];j++) C[i][j]=F[j];
    for(int j=w[i];j<=M;j++) C[i][j]=(F[j]-C[i][j-w[i]]+10)%10;
    for(int j=1;j<=M;j++) printf("%d",(C[i][j]+10)%10); printf("\n");
  }
  return 0;
}
View Code 

 

[中山市选2009]树

打了个高斯消元爽了一下 自由元要搜索记得

#include <bits/stdc++.h>
using namespace std;
const int Maxn=110;
int N;
 
bitset<Maxn>A[Maxn]; int ans[Maxn]; int id[Maxn];
void Gauss()
{
  int k=1;
  for(int i=1;i<=N;i++)
  {
    for(int j=k+1;j<=N;j++)
      if(A[j][i]){swap(A[j],A[k]); break;}
    if(A[k][i])
    {
      for(int j=k+1;j<=N;j++)
        if(A[j][i]) A[j]^=A[k];
      id[i]=k++;
    }
  }
}
 
int s=INT_MAX;
void Dfs(int x,int sum)
{
  if(x==0){s=min(s,sum); return ;}
  if(sum>s) return ;
  if(!id[x]){for(int i=0;i<2;i++) Dfs(x-1,sum+(ans[x]=i));}
  else
  {
    int k=id[x];
    ans[x]=A[k][N+1];
    for(int i=x+1;i<=N;i++) if(A[k][i]) ans[x]^=ans[i];
    Dfs(x-1,sum+ans[x]);
  }
}
 
int main()
{
  while(scanf("%d",&N)&&N)
  {
    memset(A,0,sizeof(A)); memset(ans,0,sizeof(ans)); memset(id,0,sizeof(id));
    for(int i=1;i<N;i++)
    {
      int x,y; scanf("%d%d",&x,&y);
      A[x][y]=A[y][x]=1;
    }
    for(int i=1;i<=N;i++) A[i][N+1]=A[i][i]=1;
    Gauss();
    s=INT_MAX; Dfs(N,0);
    printf("%d\n",s);
  }
  return 0;
}
View Code

 

[SHOI2014]概率充电器

很容易想到 那些充电的会往四边扩散 所以只需扯随便一个点做根 正着扫和逆着扫就出来了

但是转移就有点蛋疼了 表示不会

膜题解 首先有$F[i]$表示从子树上来的期望 很快就会有

$F[i]=F[son[i]]*edge[k].d-F[son[i]]*F[i]*edge[k].d$

因为本来就亮着的上去也没有用 所以要减掉 在这里 期望也就是亮着的概率 做过很多题 是可以等价的 因为是平均下来的

但是在父亲方向传过来的话 我们就有

$yy$表示除了从$son[i]$上来影响到的其它影响到的期望

$dp[i]$表示总的期望

很快就有$yy-yy*F[son[i]]*edge[k].d+F[son[i]]*edge[k].d=dp[x]$

解出$yy$然后$dp[son[i]]=yy*edge[k].d-yy*F[son[i]]*edge[k].d$

当然了 解得$yy$时移项 发现分母为0 即$1-(F[son[i]]*edge[k].d)=0$

也就是说下面的可以百分百影响到上面的 所以下面的肯定为1

#include<bits/stdc++.h>
using namespace std;
const int Maxn=500010;
struct node{int x,y,next; double d;}edge[Maxn*2]; int len,first[Maxn];
void ins(int x,int y,double d)
{
  len++; edge[len].x=x; edge[len].y=y; edge[len].d=d; edge[len].next=first[x]; first[x]=len;
}
int N; double C[Maxn];
void Dfs1(int x,int fa)
{
  for(int k=first[x];k!=-1;k=edge[k].next)
  {
    int y=edge[k].y; if(y==fa) continue;
    Dfs1(y,x);
    C[x]+=C[y]*edge[k].d-C[x]*C[y]*edge[k].d;
  }
}
double dp[Maxn]; double ans=0.0;
void Dfs2(int x,int fa)
{
  ans+=dp[x];
  for(int k=first[x];k!=-1;k=edge[k].next)
  {
    int y=edge[k].y; if(y==fa) continue;
    if(fabs(1.0-edge[k].d*C[y])>(1e-12))
    {
      double yother=(dp[x]-C[y]*edge[k].d)/(1.0-edge[k].d*C[y]);
      dp[y]=(C[y]+yother*edge[k].d)-(C[y]*yother*edge[k].d);
    }
    else dp[y]=1.0;
    Dfs2(y,x);
  }
}
int main()
{
  scanf("%d",&N); len=0; memset(first,-1,sizeof(first));
  for(int i=1;i<N;i++){int x,y; double d; scanf("%d%d%lf",&x,&y,&d); d/=100; ins(x,y,d); ins(y,x,d);}
  memset(C,0.0,sizeof(C));
  for(int i=1;i<=N;i++){double d; scanf("%lf",&d); C[i]+=d/100;}
  Dfs1(1,0);
  dp[1]=C[1];
  Dfs2(1,0);
  return printf("%.6lf\n",ans),0;
}
View Code

 

2017.3.26

[Noip2016]换教室

发现自己的概率期望好垃圾 所以就做多了一题(当年noip2016这道题没时间做0分自信)

现在看回这道题 会做的是水题 不会做的算是好题

我属于后者 花了一个下午做这道题 感觉不会很通 发现我的状态设错了

我的$F[i][j][0/1]$表示第$i$段时间 包括这一段时间总过用了j次 最后在c还是d上

正解$F[i][j][0/1]$表示第$i$段时间 包括这一段时间总过用了j次 当前这一段时间有没有用

发现我这样设是错的 是因为 无法统计答案 答案需要最小值 在c和在d上都是属于同一种状态 也就是说一个期望去到c 一个期望去到d 两个算的时候是不能拆开的

状态设对转移显然

#include <bits/stdc++.h>
using namespace std;
const int Maxn=2010; double F[Maxn][Maxn][2];
int pos[2][Maxn]; double Dis[310][310]; double p[Maxn];
int N,M,V,E;
int main()
{
  scanf("%d%d%d%d",&N,&M,&V,&E);
  for(int i=1;i<=N;i++) scanf("%d",&pos[0][i]);
  for(int i=1;i<=N;i++) scanf("%d",&pos[1][i]);
  for(int i=1;i<=N;i++) scanf("%lf",&p[i]);

  for(int i=1;i<=V;i++) for(int j=1;j<=V;j++) Dis[i][j]=DBL_MAX/3;
  for(int i=1;i<=V;i++) Dis[i][i]=0;
  for(int i=1;i<=E;i++)
  {
      int x,y; double d; scanf("%d%d%lf",&x,&y,&d);
      Dis[x][y]=min(Dis[x][y],d); Dis[y][x]=min(Dis[y][x],d);
  }
  for(int k=1;k<=V;k++) for(int i=1;i<=V;i++) for(int j=1;j<=V;j++)
    if(i!=j&&j!=k&&i!=k) Dis[i][j]=min(Dis[i][j],Dis[i][k]+Dis[k][j]);
 
  for(int i=1;i<=N;i++) for(int j=0;j<=M;j++) for(int k=0;k<2;k++) F[i][j][k]=DBL_MAX/3;
  F[1][0][0]=0; F[1][1][1]=0;
  
  
  for(int i=2;i<=N;i++)
  {
    for(int j=0;j<=i&&j<=M;j++)
    {
        //if((F[i][j][0]==1e9)||(F[i][j][1]==1e9)) continue;
        if(j<i) F[i][j][0]=min(F[i-1][j][0]+Dis[pos[0][i-1]][pos[0][i]],
                         F[i-1][j][1]+Dis[pos[0][i-1]][pos[0][i]]*(1-p[i-1])+Dis[pos[1][i-1]][pos[0][i]]*p[i-1]);
        if(j) F[i][j][1]=min(F[i-1][j-1][0]+Dis[pos[0][i-1]][pos[0][i]]*(1-p[i])+Dis[pos[0][i-1]][pos[1][i]]*p[i],
                         F[i-1][j-1][1]+Dis[pos[0][i-1]][pos[0][i]]*(1-p[i-1])*(1-p[i])+
                         Dis[pos[1][i-1]][pos[0][i]]*p[i-1]*(1-p[i])+
                         Dis[pos[0][i-1]][pos[1][i]]*(1-p[i-1])*p[i]+
                         Dis[pos[1][i-1]][pos[1][i]]*p[i-1]*p[i]);
    }
  }
  
  double minx=DBL_MAX/3;
  for(int i=0;i<=M&&i<=N;i++) for(int j=0;j<2;j++) minx=min(minx,F[N][i][j]);
  return printf("%.2lf\n",minx),0;
}
View Code

 2017.3.27

 

[Baltic2013]numbers

改了点地方就A了我也不知道为什么错

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
LL F[20][11][11][2][2]; //bit last_second last_first prefix_zero full
//10 0x
LL L,R; int A[20]; LL len;
LL Query(LL x)//0-x
{
  memset(F,0,sizeof(F));
  len=0; while(x) A[++len]=x%10,x/=10;
  
  if(len==0) return 1;
  if(len==1) return A[1]+1;
  
  for(LL i=A[len];i>=0;i--)
    for(LL j=((i==A[len])?A[len-1]:9);j>=0;j--)
    {
      if(i==0&&j==0){F[len-1][0][0][1][0]+=1; continue;}
      if(i==j) continue;
      if(i==A[len]&&j==A[len-1]){F[len-1][A[len]][A[len-1]][0][1]+=1; continue;}
      if(i==0&&j!=0){F[len-1][10][j][0][0]+=1; continue;}
      F[len-1][i][j][0][0]+=1;
    }
  
  for(LL L=len-2;L>=1;L--)
  {
    //prefix 0
    for(LL i=9;i>=0;i--)
    {
      if(i==0){F[L][0][0][1][0]+=F[L+1][0][0][1][0]; continue;}
      else{F[L][10][i][0][0]+=F[L+1][0][0][1][0]; continue;}
    }
    //full
    for(LL i=A[L];i>=0;i--)
    {
      if(i==A[L+1]||i==A[L+2]) continue;
      if(i==A[L]){F[L][A[L+1]][A[L]][0][1]+=F[L+1][A[L+2]][A[L+1]][0][1]; continue;}
      else{F[L][A[L+1]][i][0][0]+=F[L+1][A[L+2]][A[L+1]][0][1]; continue;}
    }

    for(LL i=10;i>=0;i--)
      for(LL j=9;j>=0;j--)
        for(LL k=9;k>=0;k--)
        {
          if(i==j||j==k||i==k) continue;
          F[L][j][k][0][0]+=F[L+1][i][j][0][0];
        }
  }

  LL ans=0;
  for(LL i=10;i>=0;i--) for(LL j=9;j>=0;j--)
      ans+=F[1][i][j][0][0]+F[1][i][j][0][1]+F[1][i][j][1][0]+F[1][i][j][1][1];
  return ans;
}
int main()
{
  scanf("%lld%lld",&L,&R);
  printf("%lld\n",Query(R)-Query(L-1));
  return 0;
}
View Code 

 

[Cqoi2011]放棋子

考虑每行每列是单独的所以我们可以随便交换

这样的话也给我们带来麻烦 对于每个颜色选多少行我们是可以做的 这是个突破点

错误的状态定义:$F[k][i][j]$表示k种颜色占i*j行 $G[k][i][j]$表示前k种颜色占i*j行

正确的状态定义:$F[k][i][j]$表示k种颜色恰好占i*j行 $G[k][i][j]$表示前k种颜色占i*j行

说说两者的区别 错误的定义 显然当G需要转移的时候 因为有空行的原因 所以的话组合起来就会出错

后者没有空行 转移时直接组合就可以

$f[k,i,j]=\binom{ij}{a[k]}-\Sigma f[k,x,y]* \binom{i}{x}* \binom{j}{y}$

$g[k,i,j]=\Sigma g[k-1,x,y]*f[k,x,y]* \binom{i}{x}* \binom{j}{y}$

最后所有方案数累加即可

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL Maxn=50;
const LL Maxm=50;
const LL Mod=1000000009;
LL G[Maxm][Maxn][Maxn]; LL F[Maxm][Maxn][Maxn];
LL N,M,c; LL A[Maxn]; LL C[1010][1010];
void Calc()
{
  for(LL i=1;i<=1000;i++) C[i][0]=C[i][i]=1LL;
  for(LL i=2;i<=1000;i++) for(LL j=1;j<i;j++) C[i][j]=(C[i-1][j-1]+C[i-1][j])%Mod;
}
int main()
{
  scanf("%lld%lld%lld",&N,&M,&c);
  for(LL i=1;i<=c;i++) scanf("%lld",&A[i]);
  Calc();
  for(LL k=1;k<=c;k++)
  {
    for(LL i=1;i<=min(N,A[k]);i++) for(LL j=1;j<=min(M,A[k]);j++)
    {
      if(i*j>=A[k])
      {
        F[k][i][j]=C[i*j][A[k]];
        for(LL x=1;x<=i;x++) for(LL y=1;y<=j;y++)
        {
          if(i==x&&j==y) continue;
          if(x*y>=A[k]) F[k][i][j]=(F[k][i][j]-F[k][x][y]*C[i][x]%Mod*C[j][y]%Mod+Mod)%Mod;
        }
      }
    }
  }
 
  G[0][0][0]=1; LL tot=0;
  for(LL k=1;k<=c;k++)
  {
    tot+=A[k];
    for(LL i=1;i<=min(N,tot);i++) for(LL j=1;j<=min(tot,M);j++)
    {
      if(i*j>=tot)
      {
        for(LL x=1;x<=min(min(N,A[k]),i);x++) for(LL y=1;y<=min(j,min(M,A[k]));y++)
          if(x*y>=A[k])
            G[k][i][j]=(G[k][i][j]+G[k-1][i-x][j-y]*F[k][x][y]%Mod*C[i][x]%Mod*C[j][y]%Mod+Mod)%Mod;
      }
    }
  }
 
  LL ans=0;
  for(LL i=1;i<=N;i++) for(LL j=1;j<=M;j++) ans=(ans+G[c][i][j]*C[N][i]%Mod*C[M][j]%Mod+Mod)%Mod; 
  return printf("%lld\n",ans),0;
}
View Code 

 

[HAOI2006]数字序列

很好的一道题 首先先看第一问 常态把严格上升到不降 然后找出最长不下降 剩下的就是要改变的 是不是很有道理!

对于第二问 我不会做 但是略显搞笑 数据是随机的 这个很重要 我们想一想要改的 就是原来是最长不降子序列 序列元素相邻的一段

类似这样

然而我们期望要把这些点移成这样

 

然而我们发现一个东西 上面的点下来 和在下面的点上来 把一段的点移到下面横线上 一段的点移到上面横线上时最优的 理由如下

1.首先我们一定要把这一段分割使得左边的在下面(左端点)右边的在上面(右端点)

2.既然左边的在下面 那么我们考虑 连续的一段左边的 如果在原来在下面的点更多的话 那么肯定是让上面的点下来 反之就在右边 让下面的点上去

3.我们一定会找到最优的分割点 当然如果上面和下面的点相同时 我们可以随便放在一边

4.假设对于放在左边的一段 原来下面的点更优的话 那么一定不会出现在两条线中间 而出现在下面的线上 因为下面的线更优 当然也不会出现在线下 这样不满足上升子

5.有可能会问 端点也有可能向下 但是考虑端点向下只有可能是有一个更大的区间使端点向下 试想一下 最左边最小的端点需要向下吗?

由此我们可以得出一个结论 最长不降子序列相邻的两个点中间的点 其中必定有一个分割点 使得左边在下面 右边在上面的 而且这样分割使得代价最小 时间复杂度O(N^3) 因为数据随机 所以可以过

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL Maxn=35010;
const LL inf=1e9;
LL N,A[Maxn]; LL mn[Maxn]; LL R=0;
LL F[Maxn];
LL Find(LL x)
{
  LL l=0; LL r=R; LL ret;
  while(l<=r)
  {
    LL mid=(l+r)>>1;
    if(mn[mid]<=x){ret=mid; l=mid+1;}
    else r=mid-1;
  }
  return ret;
}
 
vector<LL>V[Maxn]; LL Sum1[Maxn],Sum2[Maxn],G[Maxn];
int main()
{
  scanf("%lld",&N); for(LL i=1;i<=N;i++) scanf("%lld",&A[i]),A[i]-=i; A[++N]=INT_MAX;
 
  for(LL i=1;i<=N;i++) mn[i]=INT_MAX;
  A[0]=mn[0]=INT_MIN; F[0]=0;
  for(LL i=1;i<=N;i++)
  {
    LL L=Find(A[i]);
    F[i]=L+1; R=max(R,L+1); mn[L+1]=min(mn[L+1],A[i]);
  }
 
  V[0].push_back(0);
  for(LL i=1;i<=N;i++) V[F[i]].push_back(i);
  for(LL i=1;i<=R;i++) sort(V[i].begin(),V[i].end());
 
  for(LL i=1;i<=N;i++) G[i]=INT_MAX; G[0]=0;
  for(LL i=1;i<=N;i++)
  {
    for(LL j=0;j<V[F[i]-1].size();j++)
    {
      LL l=V[F[i]-1][j];
      if(l>i) break; if(A[l]>A[i]) continue;
      Sum1[l]=Sum2[i+1]=0;
      for(LL k=l+1;k<=i;k++) Sum1[k]=abs(A[k]-A[l]),Sum2[k]=abs(A[k]-A[i]);
      for(LL k=l+1;k<=i;k++) Sum1[k]+=Sum1[k-1];
      for(LL k=i;k>=l+1;k--) Sum2[k]+=Sum2[k+1];
      for(LL k=l;k<=i;k++) G[i]=min(G[i],Sum1[k]+Sum2[k+1]+G[l]);
    }
  }
  return printf("%lld\n%lld\n",N-F[N],G[N]),0;
}
View Code 

 

小P的牧场

一眼斜率dp 常态推公式 首先有

for(int i=N;i>=0;i--) S[i]=S[i+1]+(N-i+1)*B[i];
for(int i=0;i<=N;i++) P[i]=P[i-1]+B[i];

之后可以推出$j>k$ $\frac{fj-fk+Sj+1-Sk+1}{Pk-Pj}>=(N-i+1)$时$j$比$k$优

#include<bits/stdc++.h>
using namespace std;
const int Maxn=1000010;
int N; double A[Maxn],B[Maxn]; double S[Maxn],P[Maxn]; int Q[Maxn],head,tail; double F[Maxn];
double Slop(int k,int j){return (F[j]-F[k]+S[j+1]-S[k+1])/(P[k]-P[j]);}
int main()
{
  scanf("%d",&N); for(int i=1;i<=N;i++) scanf("%lf",&A[i]); for(int i=1;i<=N;i++) scanf("%lf",&B[i]);
  for(int i=N;i>=0;i--) S[i]=S[i+1]+(N-i+1)*B[i];
  for(int i=0;i<=N;i++) P[i]=P[i-1]+B[i];
  head=tail=1; Q[head]=0;
  for(int i=1;i<=N;i++)
  {
    while(head<tail&&Slop(Q[head],Q[head+1])>=N-i+1)
      head++;
    F[i]=F[Q[head]]+S[Q[head]+1]-S[i]-(P[i-1]-P[Q[head]])*(N-i+1)+A[i];
    while(head<tail&&Slop(Q[tail-1],Q[tail])<=Slop(Q[tail],i)) tail--;
    Q[++tail]=i;
  }
  return printf("%.0lf\n",F[N]),0;
}
/*
4
2 4 2 4
3 1 4 2
*/
View Code

 

2017.3.28

 

[Noi2011]Noi嘉年华

首先我们考虑怎么做第一问 我一开始想的是dp 一维前面有多少个嘉年华 一维是A选了多少个嘉年华 对方最大多少

发现嘉年华不好转移 所以变成时间为第一维 所以有

$F[i][j]$表示时间i时已经在A广场举办了j个嘉年华 B最多可以举办多少个 转移想一想即可

对于第二问 有一些嘉年华一定要选 那么就是这段时间一定要用上 我们考虑把这段时间单独出来算 然后加上一头一尾的

所以我们再定义$G[i][j]$和$F[i][j]$意思一样 但是是倒叙的

所以就有$max \left( min \left( x+y,F[i-1][x]+G[j+1][y]+num[i][j] \right) \right)$

对于x和y所有都会被算到 所以的话不用担心A广场多还是B广场多的问题 反正就是所有的状态都会被算到

$num[i][j]$表示这段时间内的活动个数 另外活动如果要举办在一段时间内一定是越多越好

然后发现 当$x$递增的时候 $y$一定要递减才能取到最大值 因为要是min尽量的平均 也就是$x+y$和$F[i-1][x]+G[j+1][y]$尽量平均

而$x$与$F[i-1][x]$成反相关 $y$与$G[j+1][y]$也成反相关 所以很容易得知结论

然后直接做就好了

#include <bits/stdc++.h>
using namespace std;
const int Maxn=210;
int N; pair<int,int>pr[Maxn]; int num[Maxn*2][Maxn*2];
map<int,int>mp; int Time[Maxn*2],len;

int F[Maxn*2][Maxn],G[Maxn*2][Maxn],H[Maxn*2][Maxn*2]; int ans[Maxn];
int main()
{
  scanf("%d",&N); len=0;
  for(int i=1;i<=N;i++)
  {
      int L; scanf("%d%d",&pr[i].first,&L); pr[i].second=pr[i].first+L-1;
    Time[++len]=pr[i].first; Time[++len]=pr[i].second;
  }
  sort(Time+1,Time+len+1);
  len=unique(Time+1,Time+len+1)-(Time+1);
  for(int i=1;i<=len;i++) mp[Time[i]]=i;
  memset(num,0,sizeof(num));
  for(int i=1;i<=N;i++)
      for(int j=mp[pr[i].first];j>=1;j--)
        for(int k=mp[pr[i].second];k<=len;k++) num[j][k]++;

  memset(F,-63,sizeof(F)); F[0][0]=0;
  for(int i=1;i<=len;i++) for(int j=0;j<=N;j++) for(int k=0;k<i;k++)
  {
      F[i][j]=max(F[i][j],F[i-1][j]);
      F[i][j]=max(F[i][j],F[k][j]+num[k+1][i]);
      if(j-num[k+1][i]>=0) F[i][j]=max(F[i][j],F[k][j-num[k+1][i]]);
  }
  memset(G,-63,sizeof(G)); G[len+1][0]=0;
  for(int i=len;i>=1;i--) for(int j=0;j<=N;j++) for(int k=i+1;k<=len+1;k++)
  {
      G[i][j]=max(G[i][j],G[i+1][j]);
      G[i][j]=max(G[i][j],G[k][j]+num[i][k-1]);
      if(j-num[i][k-1]>=0) G[i][j]=max(G[i][j],G[k][j-num[i][k-1]]);
  }

  memset(H,0,sizeof(H));
  for(int i=1;i<=len;i++) for(int j=i;j<=len;j++) 
  {
    int y=N;
    for(int x=0;x<=N;x++)
    {
      while((min(x+y-1,F[i-1][x]+G[j+1][y-1]+num[i][j])>=min(x+y,F[i-1][x]+G[j+1][y]+num[i][j]))&&
              (y>0)) y--;
      H[i][j]=max(H[i][j],min(x+y,F[i-1][x]+G[j+1][y]+num[i][j]));
    }
  }

  for(int j=0;j<=N;j++) ans[0]=max(ans[0],min(F[len][j],j));
  for(int k=1;k<=N;k++)
  {
    for(int i=0;i<=mp[pr[k].first];i++)
      for(int j=mp[pr[k].second];j<=len;j++) ans[k]=max(ans[k],H[i][j]);
  }

  for(int i=0;i<=N;i++) printf("%d\n",ans[i]);
  return 0;
}
/*
5
8 2
1 5
5 3
3 2
5 3
*/
View Code

 

[POI2015]Myjnie

 又是做过的题不会做..

F[i][j][k]表示洗车i到j 最低消费为k 的最大利润

G[i][j][k]表示max(F[i][j][k..m]) 当然要离散化

最后搜索用多两个数组记录路径 分别是 i到j这一段选到的k 还有分割点(上一步的dp为中链式dp)

发现搜索到k=0的时候 也就是不存在分割点 里面的数可以任意填 就填回之前的就好了

#include <bits/stdc++.h>
using namespace std;
const int Maxn=51; const int Maxm=4010;
struct node{int a,b,c;}T[Maxm]; int N,M;
int mp[500010],V[Maxm],len;
 
int Val[Maxn][Maxm];
int F[Maxn][Maxn][Maxm],G[Maxn][Maxn][Maxm],preF[Maxn][Maxn][Maxm];
int preG[Maxn][Maxn][Maxm];
int A[Maxn];
void Dfs(int l,int r,int k,int c)
{
  if(l>r) return ;
  if(k==0){for(int i=l;i<=r;i++) A[i]=c; return ;}
  else
  {
    A[preF[l][r][k]]=V[k]; int mid=preF[l][r][k];
    if(mid-1>=l) Dfs(l,mid-1,preG[l][mid-1][k],V[k]);
    if(mid+1<=r) Dfs(mid+1,r,preG[mid+1][r][k],V[k]);
  }
}
 
int main()
{
  scanf("%d%d",&N,&M); len=0;
  for(int i=1;i<=M;i++){scanf("%d%d%d",&T[i].a,&T[i].b,&T[i].c); V[++len]=T[i].c;}
  sort(V+1,V+len+1); len=unique(V+1,V+len+1)-(V+1);
  for(int i=1;i<=len;i++) mp[V[i]]=i;
 
  for(int L=1;L<=N;L++)
    for(int i=1;i<=N-L+1;i++)
    {
      int j=i+L-1;
 
      for(int k=1;k<=N;k++) for(int p=1;p<=len;p++) Val[k][p]=0;
      for(int k=1;k<=M;k++) if(T[k].a>=i&&T[k].b<=j) for(int p=T[k].a;p<=T[k].b;p++) Val[p][mp[T[k].c]]++;
      for(int k=1;k<=N;k++) for(int p=len;p>=1;p--) Val[k][p]+=Val[k][p+1];
 
      for(int k=len;k>=1;k--)
      {
        for(int p=i;p<=j;p++)
        {
          if(G[i][p-1][k]+G[p+1][j][k]+Val[p][k]*V[k]>F[i][j][k])
          {
            F[i][j][k]=G[i][p-1][k]+G[p+1][j][k]+Val[p][k]*V[k];
            preF[i][j][k]=p;
          }
        }
        if(G[i][j][k+1]<F[i][j][k]) G[i][j][k]=F[i][j][k],preG[i][j][k]=k;
        else G[i][j][k]=G[i][j][k+1],preG[i][j][k]=preG[i][j][k+1];
      }
    }
 
  printf("%d\n",G[1][N][1]);
 
  Dfs(1,N,preG[1][N][1],V[preG[1][N][1]]);
  for(int i=1;i<=N;i++) printf("%d ",A[i]); printf("\n");
 
  return 0;
}
View Code

 

[cqoi2013]二进制a+b

简单的dp  很容易想到F[i][a][b][c][0/1]表示i位A用了多少个1 balabala 有无进位

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL Maxn=32;
LL A,B,C; LL A1,B1,C1; LL bit[Maxn];
LL len; LL F[Maxn][Maxn][Maxn][Maxn][2];
 
LL Dfs(LL x)
{
  if(x==0){len=max(len,1LL); return 0LL;}
  LL ans=0;
  for(LL i=30;i>=0;i--) if(x&bit[i]) ans++,len=max(len,i);
  return ans;
}
int main()
{
  scanf("%lld%lld%lld",&A,&B,&C);
  for(LL i=0;i<=30;i++) bit[i]=(1<<i);
  A1=Dfs(A);
  B1=Dfs(B);
  C1=Dfs(C);
  for(LL i=0;i<=len;i++)
    for(LL a=0;a<=A1;a++)
      for(LL b=0;b<=B1;b++)
        for(LL c=0;c<=C1;c++)
          for(LL opt=0;opt<2;opt++)
            F[i][a][b][c][opt]=INT_MAX;
  F[0][0][0][0][0]=0; F[0][0][1][1][0]=F[0][1][0][1][0]=1;
  F[0][1][1][0][1]=2;
  for(LL i=0;i<len;i++)
  {
    for(LL a=0;a<=A1;a++)
      for(LL b=0;b<=B1;b++)
        for(LL c=0;c<=C1;c++)
          for(LL opt=0;opt<2;opt++)
            if(F[i][a][b][c][opt]!=INT_MAX)
            {
              F[i+1][a][b][c+opt][0]=min(F[i+1][a][b][c+opt][0],F[i][a][b][c][opt]);
 
              F[i+1][a+1][b][c+(opt^1)][opt&1]=min(F[i+1][a+1][b][c+(opt^1)][opt&1],F[i][a][b][c][opt]+bit[i+1]);
              F[i+1][a][b+1][c+(opt^1)][opt&1]=min(F[i+1][a][b+1][c+(opt^1)][opt&1],F[i][a][b][c][opt]+bit[i+1]);
              F[i+1][a+1][b+1][c+opt][1]=min(F[i+1][a+1][b+1][c+opt][1],F[i][a][b][c][opt]+(bit[i+1]<<1));
            }
  }
  printf("%lld\n",(F[len][A1][B1][C1][0]==INT_MAX)?(-1):F[len][A1][B1][C1][0]);
  return 0;
}
View Code

 

2017.3.29

 

Coci2009 [podjela]

好题 发现一个很有趣的事实 一个子树下面 如果是亏的话 它的父亲一定要选到这个点 如果是盈利的话 它的父亲可选这个点可不选这个点

当然了 也有可能它的父亲的父亲给这个点提供 而不是兄弟提供 

那么我们考虑N^2 因为NM 会超时 这也是一个方向 就是F[i][j]表示第i个点为子树 一共用了j次交换 然后类似背包一样做 负数一定选 正数可选择

答案一定不超过N 树上背包是O(N^2)的 这个时间复杂度算出来是要靠常识(可以看作每个节点是一个物件 然后做简单的合并罢了)

当然打法上有不同 我是记录了上下界 然后用一个数组辅助转移

 

[HAOI2011]Problem c

好题啊...被虐的体无完肤

首先如果我们但从每个人来看 他们的编号序是无序的 所以从人来DP 最起码要用到状压 然后数据给了不允许

那么我们考虑从编号入手 发现剩余的人 得到编号的顺序肯定是从前到后的 这样的话我们只需要考虑那些自由的人得到编号的种类和数目 不用关心是谁得到

所以我们就有$F[i][j]$表示前$i$个编号有$j$个人 $sum[i]$表示到$i$这个编号一共可以有多少人可以编号$\leq i$有 $cnt[i]$表示第$i$个编号 有多少个一定会选到

$F[i][j]=F[i-1][j-k]+\binom{sum[i]-cnt[i]-(j-k)}{k-cnt[i]}$

$sum[i]-cnt[i]-(j-k)$表示自由人有多少个$k-cnt[i]$表示当前要选多少个

发现合法解就是对于任意的位置 都有足够这个位置的数目 塞满前面的人才合法 F[N][N]为答案

有点不详细请膜popoqqq

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL Maxn=310;
LL T,N,M,MOD,A[Maxn]; pair<LL,LL>pr[Maxn]; LL F[Maxn][Maxn]; LL cnt[Maxn];
LL C[Maxn][Maxn]; LL sum[Maxn];
int main()
{
  scanf("%lld",&T);
  while(T--)
  {
    scanf("%lld%lld%lld",&N,&M,&MOD); memset(cnt,0LL,sizeof(cnt));
    for(int i=0;i<=300;i++) C[0][i]=1LL; C[1][0]=C[1][1]=1LL;
    for(LL i=2;i<=300;i++){C[i][0]=1LL; for(LL j=1;j<=i;j++) C[i][j]=(C[i-1][j]+C[i-1][j-1])%MOD;}
 
    for(LL i=1;i<=M;i++) scanf("%lld%lld",&pr[i].first,&pr[i].second),cnt[pr[i].second]++; LL s=N-M;
    bool bk=true; memset(sum,0LL,sizeof(sum)); sum[0]=s;
    for(LL i=1;i<=N;i++)
    {
      sum[i]=sum[i-1]+cnt[i];
      if(sum[i]<i){bk=false; break;}
    }if(!bk){printf("NO\n"); continue;}
 
    memset(F,0LL,sizeof(F)); F[0][0]=1LL;
    for(LL i=1;i<=N;i++)
      for(LL j=i;j<=sum[i];j++)
        for(LL k=cnt[i];k<=j;k++)
          if(j-k>=i-1)
            F[i][j]=(F[i][j]+F[i-1][j-k]*C[sum[i]-cnt[i]-(j-k)][k-cnt[i]]%MOD)%MOD;
 
    printf("YES %lld\n",F[N][N]);
  }
  return 0;
}
View Code

 

 

[NOI2009]变换序列

sb题意 发现每个点最多有四个匹配 然后就是二分图匹配 字典序最小就倒着匹配 因为后面的肯定会抢前面的

#include <bits/stdc++.h>
using namespace std;
const int Maxn=10010; vector<int>V[Maxn];
int N; int chw[Maxn]; int match[Maxn]; int T;
bool Find(int x)
{
  for(int i=0;i<V[x].size();i++)
  {
    int y=V[x][i];
    if(chw[y]!=T)
    {
      chw[y]=T;
      if(match[y]==0||Find(match[y])){match[y]=x; return 1;}
    }
  }
  return 0;
}
int bo[Maxn];
int main()
{
  scanf("%d",&N);
  for(int i=1;i<=N;i++)
  {
    int d; scanf("%d",&d);
    if(i>d) V[i].push_back(i-d); if(i+d<=N) V[i].push_back(i+d);
    int D=N-d;
    if(i>D) V[i].push_back(i-D); if(i+D<=N) V[i].push_back(i+D);
  }
  for(int i=1;i<=N;i++) sort(V[i].begin(),V[i].end());
  int ans=0; for(int i=N;i>=1;i--){T=i; if(Find(i)) ans++;}
  if(ans<N) printf("No Answer\n");
  else
  {
    for(int i=1;i<=N;i++) bo[match[i]-1]=i-1;
    for(int i=0;i<N-1;i++) printf("%d ",bo[i]); printf("%d\n",bo[N-1]);
  }
  return 0;
}
View Code

 

 

[ZJOI2011]细胞

又是神题 首先我们考虑当只有一串东西断开的时候 它的方案数是斐波那契数列

然后我们考虑 对于一串数字 有1000位 这样的话我们的斐波那契数列 就算是用快速幂做也很处理

首先我们观察dp$dp[i]=\Sigma dp[j]*f(sum[j+1][i])$

首先dp是一个矩阵 然而矩阵的乘就是斐波那契的第几项

我们现在要求的是sum[j+1][i]这个矩阵 我们可以预处理 我们假设现在已经预处理了数字21 现在要做数字121 就是要在前面添加1

那么我们可以预处理出10^?次方的斐波那契矩阵 然后后面如果有一个数字进来再进行次幂运算就好了

至于预处理出10^?次方的斐波那契矩阵 首先第一个 指数是不可以mod的.. 再是 10^100=(10^10*10^10*..)乘十次 当然也可以快速幂

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL Maxn=1e3+10;
const LL Mod=1e9+7;
struct node
{
  LL a[3][3];
  friend node operator *(const node &x,const node &y)
  {
    node z;
    for(LL i=1;i<=2;i++) for(LL j=1;j<=2;j++) for(LL k=1;k<=2;k++) z.a[i][j]=(z.a[i][j]+x.a[i][k]*y.a[k][j]%Mod)%Mod;
    return z;
  }
  friend node operator +(const node &x,const node &y)
  {
    node z;
    for(LL i=1;i<=2;i++) for(LL j=1;j<=2;j++) z.a[i][j]=(x.a[i][j]+y.a[i][j])%Mod;
    return z;
  }
  node(){memset(a,0,sizeof(a));}
}bit[Maxn],A,B,Sum[Maxn][Maxn],P,F[Maxn],ny;
node Quick_Power(node x,LL k)
{
  if(k==0) return P;
  if(k==1) return x;
  node z=Quick_Power(x,k>>1);
  if(k&1) return z*z*x;
  else return z*z;
}
LL N; char st[Maxn];
int main()
{
  scanf("%lld",&N); for(LL i=1;i<=N;i++) scanf("\n%c",&st[i]);
 
  A.a[1][1]=0; A.a[1][2]=1; A.a[2][1]=1; A.a[2][2]=1;
  B.a[1][1]=0; B.a[2][1]=1;
  P.a[1][1]=1; P.a[1][2]=0; P.a[2][1]=0; P.a[2][2]=1;
  ny.a[1][1]=-1; ny.a[1][2]=1; ny.a[2][1]=1; ny.a[2][2]=0;
 
  LL k=1; bit[0]=Quick_Power(A,1);
  for(LL i=1;i<=N;i++){bit[i]=bit[i-1]; for(int j=1;j<=9;j++) bit[i]=bit[i]*bit[i-1];}
  for(LL j=1;j<=N;j++)
  {
    Sum[j][j]=Quick_Power(bit[0],st[j]-'0');
    for(LL i=j-1;i>=1;i--)
      Sum[i][j]=Sum[i+1][j]*Quick_Power(bit[j-i],st[i]-'0');
  }
 
  F[0]=P; for(LL i=1;i<=N;i++) for(LL j=0;j<i;j++) F[i]=F[i]+F[j]*Sum[j+1][i];
  return printf("%lld\n",(F[N]*ny*B).a[1][1]),0;
}
View Code

 

dp系列好像也很快的结束了(还剩一道汉诺塔 等着填坑) 接下来是图论系列

 

2017.3.30

[SCOI2008]配对

很快想到费用流 然后就不会了 KM?貌似这题O(N) 等等我们来找找这道题有一个特点 集合满足互异性 所以集合里面的数都不相同

这样的话 A对应的有且最多只有1个 在B中出现和A相同

这就我们考虑先把A和B排序进行贪心 对于A中的每个位置 只有可能和B中3个进行匹配

 

想想四个为什么不行 就知道一定只有小于等于三个了 不相同的才和别人匹配 不然和自己的对号匹配是最优的 因为有且最多只有一个和你相同 所以下一个肯定不会和你相同

三个点的情况是上诉这种 有奇数点的奇怪匹配 要想到才行

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL Maxn=1e5+10;
LL A[Maxn],N,F[Maxn],B[Maxn];
int main()
{
  scanf("%lld",&N); for(LL i=1;i<=N;i++) scanf("%lld%lld",&A[i],&B[i]);
  sort(A+1,A+N+1); sort(B+1,B+N+1);
  for(LL i=1;i<=N;i++) F[i]=LLONG_MAX>>1; F[0]=0;
  for(LL i=1;i<=N;i++)
  {
    if(A[i]!=B[i]) F[i]=min(F[i],F[i-1]+abs(A[i]-B[i]));
    if(i-2>=0) F[i]=min(F[i],F[i-2]+abs(A[i-1]-B[i])+abs(A[i]-B[i-1]));
    if(i-3>=0)
    {
      F[i]=min(F[i],F[i-3]+abs(A[i-2]-B[i])+abs(A[i-1]-B[i-2])+abs(A[i]-B[i-1]));
      F[i]=min(F[i],F[i-3]+abs(A[i-1]-B[i])+abs(A[i-2]-B[i-1])+abs(B[i-2]-A[i]));
    }
  }
  if(F[N]>=(LLONG_MAX>>1)) printf("-1\n"); else printf("%lld\n",F[N]);
  return 0;
}
View Code

 

Hack

大视野居然还会出这种神题 汗..

 

这就是一种直接跑最小割不合法的情况 然而要把这种情况把它变成不存在 只要建反向的inf边即可

 

 

 

这样就不会还是像刚才这样子流了

为什么呢 首先两个要割的边在两个圈圈不相交的地方 那么一条往两个圈圈的直径里面反着跑 可以找到另外一条边 而这条直径恰好在一条路上

严谨的证明我不会 只可以粗略的了解一下 毕竟是神题 还有要删掉到不了的边

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL Maxn=10010;
const LL inf=1e14;
struct node{LL x,y,c,next,other;}edge[Maxn],A[Maxn]; LL len,first[Maxn];
void ins(LL x,LL y,LL c)
{
  len++; LL k1=len; edge[len].x=x; edge[len].y=y; edge[len].c=c; edge[len].next=first[x]; first[x]=len;
  len++; LL k2=len; edge[len].x=y; edge[len].y=x; edge[len].c=inf; edge[len].next=first[y]; first[y]=len;
  edge[k1].other=k2; edge[k2].other=k1;
}
LL N,M; bool F[110][110];
LL dep[Maxn]; queue<LL>Q;
bool Bfs()
{
  Q.push(0); memset(dep,0,sizeof(dep)); dep[0]=1;
  while(!Q.empty())
  {
    LL x=Q.front();
    for(LL k=first[x];k!=-1;k=edge[k].next)
    {
      LL y=edge[k].y;
      if(dep[y]==0&&edge[k].c)
      {
        dep[y]=dep[x]+1;
        Q.push(y);
      }
    }
    Q.pop();
  }
  return dep[N-1]>0;
}
LL Dfs(LL x,LL flow)
{
  if(x==N-1) return flow;
  LL delta=0;
  for(LL k=first[x];k!=-1;k=edge[k].next)
  {
    LL y=edge[k].y;
    if(edge[k].c&&dep[y]==dep[x]+1&&flow>delta)
    {
      LL minf=Dfs(y,min(edge[k].c,flow-delta));
      edge[k].c-=minf; edge[edge[k].other].c+=minf;
      delta+=minf;
    }
  }
  if(delta==0) dep[x]=0;
  return delta;
}
int main()
{
  scanf("%lld%lld",&N,&M); len=0; memset(first,-1,sizeof(first));
  for(LL i=0;i<N;i++) for(LL j=0;j<N;j++) F[i][j]=0;
  for(LL i=1;i<=M;i++){scanf("%lld%lld%lld",&A[i].x,&A[i].y,&A[i].c); F[A[i].x][A[i].y]=1;}
  for(LL i=0;i<N;i++) F[i][i]=1;
  for(LL k=0;k<N;k++) for(LL i=0;i<N;i++) for(LL j=0;j<N;j++) F[i][j]=(F[i][j]|(F[i][k]&F[k][j]));
  for(LL i=1;i<=M;i++)
    if(F[0][A[i].x]&&F[0][A[i].y]&&F[A[i].x][N-1]&&F[A[i].y][N-1])
      ins(A[i].x,A[i].y,A[i].c);
  LL ans=0,delta;
  while(Bfs())
  {
    if(delta=Dfs(0,inf)) ans+=delta;
    if(ans>inf) break;
  }
  return printf("%lld\n",(ans>=inf)?(-1):ans),0;
}
View Code

 

 

[Noi2010]海拔

noi的题普遍质量都很好啊 首先这道题有一个盲点 就是忽悠大家有小数 其实高度要不是0要不是1 想一下 这条边最优为什么不用尽它?

而且这道题很快就可以看出来 相当于每个点标号为0/1 然后不同编号的点连边 并且这些编号相同的点是连在一块的

就相当于网格图 割掉一些边 使得0/1分成 两边 跑最小割

注意是网格图跑最小割 我们可以用对偶图跑最短路

最短路经过的边相当于最小割割掉的边 最短路的起点和终点 最小割的起点和终点分别在一对角

但是这些是有方向的边 那应该怎么办呢

我们看要切掉红色的边 一定是左边是0右边是1 那么我们就应该向上走 其它情况也是大概如此

最后网格图最好跑Dijkstra+Heap 效率高

#include <bits/stdc++.h>
using namespace std;
const int Maxn=510;
struct node{int x,y,next,d;}edge[Maxn*Maxn*8]; 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;}
int N,ST,ED; int D[Maxn*Maxn]; priority_queue<pair<int,int> ,vector<pair<int,int> >,greater<pair<int,int> > >Q;
void Dijkstra_Heap()
{
  memset(D,63,sizeof(D)); D[ST]=0;
  Q.push(make_pair(D[ST],ST));
  while(!Q.empty())
  {
    pair<int,int> x=Q.top(); Q.pop();
    if(D[x.second]<x.first) continue;
    for(int k=first[x.second];k!=-1;k=edge[k].next)
    {
      int y=edge[k].y;
      if(D[y]>D[x.second]+edge[k].d)
      {
        D[y]=D[x.second]+edge[k].d;
        Q.push(make_pair(D[y],y));
      }
    }
  }
}
int main()
{
  scanf("%d",&N); ST=N*N+1; ED=N*N+2; len=0; memset(first,-1,sizeof(first));
  for(int i=1;i<=N;i++){int d; scanf("%d",&d); ins(i,ED,d);}
  for(int i=1;i<N;i++) for(int j=1;j<=N;j++) {int d; scanf("%d",&d); ins(i*N+j,(i-1)*N+j,d);}
  for(int i=1;i<=N;i++){int d; scanf("%d",&d); ins(ST,(N-1)*N+i,d);}
   
  for(int i=1;i<=N;i++)
  {
    int d; scanf("%d",&d); ins(ST,(i-1)*N+1,d);
    for(int j=1;j<N;j++){scanf("%d",&d); ins((i-1)*N+j,(i-1)*N+j+1,d);}
    scanf("%d",&d); ins((i-1)*N+N,ED,d);
  }
 
  for(int i=1;i<=N;i++){int d; scanf("%d",&d); ins(ED,i,d);}
  for(int i=1;i<N;i++) for(int j=1;j<=N;j++) {int d; scanf("%d",&d); ins((i-1)*N+j,i*N+j,d);}
  for(int i=1;i<=N;i++){int d; scanf("%d",&d); ins((N-1)*N+i,ST,d);}
 
  for(int i=1;i<=N;i++)
  {
    int d; scanf("%d",&d); ins((i-1)*N+1,ST,d);
    for(int j=1;j<N;j++){scanf("%d",&d); ins((i-1)*N+j+1,(i-1)*N+j,d);}
    scanf("%d",&d); ins(ED,(i-1)*N+N,d);
  }
 
  Dijkstra_Heap();
  return printf("%d\n",D[ED]),0;
}
View Code 

 

学习小组

经典的费用流模型 Ci*A^2=1*Ci+3*Ci+5*Ci....按照奇数建边就好 每一条流量为1 蓝书上有讲类似的例题

至于参与学生尽量多 就是每个学生至少要选一门 从学生连向汇点流量为K-1就好 就是剩下一个一定要选课程 而选课程的话 就是连到课程流量为1费用为0 课程流到汇点费用为上面的东西

 

#include <bits/stdc++.h>
using namespace std;
const int Maxn=110;
const int inf=1e9;
struct node{int x,y,d,c,next,other;}edge[Maxn*Maxn*Maxn]; int len,first[Maxn*2];
void ins(int x,int y,int d,int c)
{
  len++; int k1=len; edge[len].x=x; edge[len].y=y; edge[len].d=d; 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].d=-d; edge[len].c=0; edge[len].next=first[y]; first[y]=len;
  edge[k1].other=k2; edge[k2].other=k1;
}
int N,M,K; int C[Maxn],F[Maxn]; int ST,ED;
queue<int>Q; bool V[Maxn*2]; int pos[Maxn*2],pre[Maxn*2],D[Maxn*2];
bool Spfa()
{
  while(!Q.empty()) Q.pop(); Q.push(ST);
  memset(D,63,sizeof(D)); D[ST]=0;
  memset(V,0,sizeof(V)); V[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(edge[k].c&&D[y]>D[x]+edge[k].d)
      {
        D[y]=D[x]+edge[k].d; pre[y]=k; pos[y]=x;
        if(!V[y])
        {
          V[y]=1;
          Q.push(y);
        }
      }
    }
    Q.pop(); V[x]=0;
  }
  return D[ED]<inf;
}
 
int main()
{
  scanf("%d%d%d",&N,&M,&K); len=0; memset(first,-1,sizeof(first)); ST=N+M+1; ED=N+M+2;
  for(int i=1;i<=M;i++) scanf("%d",&C[i]);
  for(int i=1;i<=M;i++) scanf("%d",&F[i]);
  for(int i=1;i<=N;i++) ins(ST,i,0,K),ins(i,ED,0,K-1);
  for(int i=1;i<=N;i++) for(int j=1;j<=M;j++)
  {
    char st; scanf("\n%c",&st);
    if(st=='1') ins(i,j+N,0,1);
  }
  for(int i=1;i<=M;i++) for(int j=1;j<=100;j++) ins(i+N,ED,C[i]*(2*j-1)-F[i],1);
  int ans=0;
  while(Spfa())
  {
    int minf=INT_MAX;
    for(int i=ED;i!=ST;i=pos[i]) minf=min(minf,edge[pre[i]].c);
    for(int i=ED;i!=ST;i=pos[i]) ans=(ans+edge[pre[i]].d*minf),edge[pre[i]].c-=minf,edge[edge[pre[i]].other].c+=minf;
  }
  return printf("%d\n",ans),0;
}
View Code

 

2017.3.31

[Hnoi2013]切糕

 

好题啊 看错了几年的题意 首先光滑的第一不是像我们想象的那样 一切就是一个平面 而是一个又凹又凸的都行(有点变态)

先考虑不限制流量 每一条纵轴是单独的 就是说 这么多个平面内的值是任选的 而且每个格子只要有一层选到就行 我们把格子的点的值 都抽象得赋值在一条向上的纵轴 然后最小割

要限制范围 那么就可以每一条纵轴连到相临的纵轴 -d的位置一条inf边 正确性画图显然(inf下面的边不能割)

#include <bits/stdc++.h>
using namespace std;
const int Maxn=50;
const int inf=1e9;
struct node{int x,y,c,next,other;}edge[Maxn*Maxn*Maxn*30]; int len,first[Maxn*Maxn*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 N,M,H,D; int ST,ED;
int dx[4]={-1,0,1,0}; int dy[4]={0,1,0,-1};
 
queue<int>Q; int dep[Maxn*Maxn*Maxn];
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));
      edge[k].c-=minf; edge[edge[k].other].c+=minf;
      delta+=minf;
    }
  }
  if(delta==0) dep[x]=0;
  return delta;
}
int main()
{
  scanf("%d%d%d",&N,&M,&H); len=0; memset(first,-1,sizeof(first));
  scanf("%d",&D); ST=N*M*(H+1)+1; ED=ST+1;
  for(int k=1;k<=H;k++)
  {
    for(int i=1;i<=N;i++) for(int j=1;j<=M;j++)
    {
      int x; scanf("%d",&x);
      ins((k-1)*N*M+(i-1)*M+j,k*N*M+(i-1)*M+j,x);
    }
  }
  for(int k=D+1;k<=H+1;k++)
  {
    for(int i=1;i<=N;i++) for(int j=1;j<=M;j++)
    {
      for(int kk=0;kk<4;kk++)
      {
        int x=i+dx[kk]; int y=j+dy[kk];
        if(x>0&&y>0&&x<=N&&y<=M) ins((k-1)*N*M+(i-1)*M+j,(k-D-1)*N*M+(x-1)*M+y,inf);
      }
    }
  }
  for(int i=1;i<=N;i++) for(int j=1;j<=M;j++){ins(ST,(i-1)*M+j,inf); ins(H*N*M+(i-1)*M+j,ED,inf);}
  int ans=0,delta;
  while(Bfs())
  {
    if(delta=Dfs(ST,inf)) ans+=delta;
  }
  return printf("%d\n",ans),0;
}
View Code

 

[Violet 1]迷宫花园

显然具有二分性 直接二分就好了

#include <bits/stdc++.h>
using namespace std;
const int Maxn=110;
struct node{int x,y; double d,next;}edge[Maxn*Maxn*50]; int len,first[Maxn*Maxn];
void ins(int x,int y,double d){len++; edge[len].x=x; edge[len].y=y; edge[len].d=d; edge[len].next=first[x]; first[x]=len;}
priority_queue<pair<double,int>,vector<pair<double,int> >,greater<pair<double,int> > >Q;
double D[Maxn*Maxn]; int N,M,ST,ED; double L; char st[Maxn][Maxn];
void Dijkstra_Heap()
{
  for(int i=1;i<=N*M+2;i++) D[i]=1e9; D[ST]=0.0;
  while(!Q.empty()) Q.pop(); Q.push(make_pair(0.0,ST));
  while(!Q.empty())
  {
    pair<double,int> x=Q.top(); Q.pop();
    if(x.first>D[x.second]) continue;
    for(int k=first[x.second];k!=-1;k=edge[k].next)
    {
      int y=edge[k].y;
      if(D[y]>D[x.second]+edge[k].d)
      {
        D[y]=D[x.second]+edge[k].d;
        Q.push(make_pair(D[y],y));
      }
    }
  }
}
int dx[4]={-1,0,1,0}; int dy[4]={0,1,0,-1};
void Build(double d)
{
  len=0; memset(first,-1,sizeof(first));
  for(int i=1;i<=N;i++) for(int j=1;j<=M;j++)
    if(st[i][j]!='#')
    {
      if(st[i][j]=='S') ST=(i-1)*M+j;
      if(st[i][j]=='E') ED=(i-1)*M+j;
      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)
        {
          if(k==0||k==2) ins((i-1)*M+j,(x-1)*M+y,d);
          else ins((i-1)*M+j,(x-1)*M+y,1.0);
        }
      }
    }
}
int main()
{
  int T; scanf("%d",&T);
  while(T--)
  {
    scanf("%lf%d%d\n",&L,&N,&M); len=0; memset(first,-1,sizeof(first));
    for(int i=1;i<=N;i++)
      gets(st[i]+1);
 
    double l=0.0; double r=10.0; double ret;
    while(r-l>=1e-7)
    {
      double mid=(l+r)/2.0; ret=mid;
      Build(mid);
      Dijkstra_Heap();
      if(D[ED]==L) break;
      if(D[ED]>L) r=mid;
      else l=mid;
    }
    printf("%.5lf\n",ret);
  }
  return 0;
}
View Code

 

[COCI2013]hiperprostor

很简单的想到跑最短路遇到多少个x 然后就是一些形如$y=kx+b$的直线所在的所有横坐标为整数 纵坐标为横坐标带入所有直线 得到的y的最小值

然后我们考虑维护这些直线 使得所有直线都有贡献

 

然后根据直线的交点 等差数列求行 细节有点恶心 枚举队列的直线 把相交左端点等于last 右端点下取整 然后在当前的直线上等差数列 下次跳last是右端点的下取整+1

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int Maxn=510;
  
struct E{int x,y,next,d;}edge[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;}
  
struct node
{
  int x,k,d;
  node(){}
  node(int _x,int _k,int _d){x=_x; k=_k; d=_d;}
  friend bool operator <(const node &x,const node &y){return x.d>y.d;}
};
  
int N,M; char st[15]; int bit[15]; 
int D[Maxn][Maxn]; priority_queue<node>Q; int ST,ED;
void Dijsktra_Heap()
{
  while(!Q.empty()) Q.pop(); Q.push(node(ST,0,0));
  memset(D,63,sizeof(D)); D[ST][0]=0;
  while(!Q.empty())
  {
    node x=Q.top(); Q.pop();
    if(D[x.x][x.k]<x.d) continue;
    for(int k=first[x.x];k!=-1;k=edge[k].next)
    {
      int y=edge[k].y;
      if(edge[k].d==-1)
      {
        if(x.k+1>500) continue;
        if(D[y][x.k+1]>D[x.x][x.k]) D[y][x.k+1]=D[x.x][x.k],Q.push(node(y,x.k+1,D[y][x.k+1]));
      }
      else if(D[y][x.k]>D[x.x][x.k]+edge[k].d) D[y][x.k]=D[x.x][x.k]+edge[k].d,Q.push(node(y,x.k,D[y][x.k]));
    }
  }
}
  
  
pair<int,int>Line[Maxn],pr[Maxn]; int l;//first k second b
  
int S[Maxn],top;
double X(pair<int,int> x,pair<int,int> y)
{
  if(x.first==0) return double(x.second-y.second)/double(y.first);
  return double(y.second-x.second)/double(x.first-y.first);
}
  
int main()
{
  len=0; memset(first,-1,sizeof(first));
  scanf("%d%d",&N,&M); bit[1]=1; for(int i=2;i<=9;i++) bit[i]=bit[i-1]*10;
  for(int i=1;i<=M;i++)
  {
    int x,y; scanf("%d%d",&x,&y); scanf("%s",st+1);
    int l=strlen(st+1);
    if(l==1&&st[1]=='x') ins(x,y,-1);
    else{int d=0; for(int j=1;j<=l;j++) d+=(st[j]-'0')*bit[l-j+1]; ins(x,y,d);}
  }
  
  int K; scanf("%d",&K);
  while(K--)
  {
    scanf("%d%d",&ST,&ED);
    Dijsktra_Heap();
    for(int i=0;i<=500;i++) pr[i]=make_pair(i,D[ED][i]);
  
    bool bk=0;
    for(int i=0;i<=500;i++) if(pr[i].second<999999999){bk=1; break;}
    if(bk==0){printf("0 0\n"); continue;}
    if(pr[0].second>999999999){printf("inf\n"); continue;}
  
    l=0;
    for(int i=500;i>=0;i--)
    {
      if(pr[i].second>999999999) continue;
      while(l&&(pr[i].second<Line[l].second)) l--;
      Line[++l]=pr[i];
    }
  
    memset(S,0,sizeof(S)); top=0;
    for(int i=1;i<=l;i++) if(Line[i].second<999999999)
    {
      while(top>1&&X(Line[i],Line[S[top]])<=X(Line[i],Line[S[top-1]])) top--;
      S[++top]=i;
    }
  
    if(top==1) printf("%d %d\n",1,Line[S[top]].second);
    else
    {
      int last=1; int s=0; LL tot=0; int lasty=-1;
      for(int i=1;i<top;i++)
      {
        int a1=last; int an=floor(X(Line[S[i]],Line[S[i+1]]));
        if(a1<=an)
        {
          int y1=a1*Line[S[i]].first+Line[S[i]].second;
          int yn=an*Line[S[i]].first+Line[S[i]].second; lasty=yn;
          s+=an-a1+1; tot+=LL(y1+yn)*(an-a1+1)/2;
        }
        last=floor(X(Line[S[i]],Line[S[i+1]])+1.0);
      }
      if(lasty!=Line[S[top]].second) s++,tot+=Line[S[top]].second;
      printf("%d %lld\n",s,tot);
    }
  }
  return 0;
}
View Code

 

2017.4.1

卡牌配对

算是好题吧 首先我们要看懂题意 至多一个互质 就是说至少两个不互质 有可能有三种的情况

直接暴力建边显然会超时我们考虑从质因子那里入手

发现只有46个质因子 分三组 AB AC BC 表示对于每一组属性的卡片成对的质因子

怎么说好呢其实很简单 就是你每张卡片 在A中有质因子3 在B中有质因子5 那么你就连到 (AB,3,5)这个地方就行

拆点 3*46*46个三元组 然后左右连 然后对于每张卡片都分解建边

玄学的复杂度水过

#include <bits/stdc++.h>
using namespace std;
const int Maxn=50;
const int inf=1e9;
struct node{int x,y,c,next,other;}edge[Maxn*Maxn*Maxn*Maxn]; int len,first[Maxn*Maxn*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*Maxn*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(edge[k].c,flow-delta));
      edge[k].c-=minf; edge[edge[k].other].c+=minf;
      delta+=minf;
    }
  }
  if(delta==0) dep[x]=0;
  return delta;
}
 
int A[30010],B[30010],C[30010]; int N,M;
 
int prime[210]; bool V[210]; int pri;
void Prime()
{
  memset(V,1,sizeof(V)); V[1]=0; pri=0;
  for(int i=2;i<=200;i++)
  {
    if(V[i]) prime[++pri]=i;
    for(int j=1;(j<=pri)&&(i*prime[j]<=200);j++)
    {
      V[i*prime[j]]=0;
      if(i%prime[j]==0) break;
    }
  }
}
 
vector<int>P[210];
 
int main()
{
  Prime();
 
  scanf("%d",&N); scanf("%d",&M);
  len=0; memset(first,-1,sizeof(first)); ST=N+M+pri*pri*3+1; ED=ST+1;
 
  for(int i=1;i<=N;i++) ins(ST,i,1);
  for(int i=1;i<=N;i++) scanf("%d%d%d",&A[i],&B[i],&C[i]);
 
  for(int i=1;i<=200;i++) for(int j=1;j<=pri;j++) if(i%prime[j]==0) P[i].push_back(j);
 
  for(int i=1;i<=N;i++)
  {
    for(int a=0;a<P[A[i]].size();a++) for(int b=0;b<P[B[i]].size();b++)
       ins(i,N+(P[A[i]][a]-1)*pri+P[B[i]][b],1);
    for(int a=0;a<P[A[i]].size();a++) for(int c=0;c<P[C[i]].size();c++)
       ins(i,N+pri*pri+(P[A[i]][a]-1)*pri+P[C[i]][c],1);
    for(int b=0;b<P[B[i]].size();b++) for(int c=0;c<P[C[i]].size();c++)
       ins(i,N+pri*pri*2+(P[B[i]][b]-1)*pri+P[C[i]][c],1);
  }
 
  for(int i=1;i<=M;i++) ins(N+pri*pri*3+i,ED,1);
  for(int i=1;i<=M;i++) scanf("%d%d%d",&A[i],&B[i],&C[i]);
  for(int i=1;i<=M;i++)
  {
    for(int a=0;a<P[A[i]].size();a++) for(int b=0;b<P[B[i]].size();b++)
       ins(N+(P[A[i]][a]-1)*pri+P[B[i]][b],N+pri*pri*3+i,1);
    for(int a=0;a<P[A[i]].size();a++) for(int c=0;c<P[C[i]].size();c++)
       ins(N+pri*pri+(P[A[i]][a]-1)*pri+P[C[i]][c],N+pri*pri*3+i,1);
    for(int b=0;b<P[B[i]].size();b++) for(int c=0;c<P[C[i]].size();c++)
       ins(N+pri*pri*2+(P[B[i]][b]-1)*pri+P[C[i]][c],N+pri*pri*3+i,1);
  }
 
  int ans=0; int delta;
  while(Bfs())
    {if(delta=Dfs(ST,inf)) ans+=delta;}
  return printf("%d\n",ans),0;
}
View Code

 

[Noi2011]兔兔与蛋蛋

 好劲啊这道题 首先我们从博弈的角度来看这道题

 先手放棋子后手挪 不能挪到之前的位置 不能挪的就是输

哈哈这道题 其实就是相临格子黑白染色 然后假设棋子在黑格子 建成二分图 那么后手肯定是从左边的点沿着匹配边走到右边的点

然后先手就从右边的点 走非匹配边到匹配边 .....一直玩下去 谁没得走谁输

我们可以把输做个定义:

当整个图有点没有匹配的话 肯定是后手输

否则就是先手输

 

那么我们再看回这道题

把空格看作棋子 然后原图黑白染色

首先 一个可以证明的结论是 空格不会走到原来的地方!! 这个很重要

至于证明 可以自己画一画 每次空格想走到回来的时候 最后一个格子肯定和空格第一次交换的格子是颜色相同的

然后的话 至于判断这一步是不是错误的 当且仅当这一步走完之后 原来的人必胜 下一个人走的时候 下个人是必胜的

这道题的点是一步步走的 所以的话 我们要考虑必胜或必败和点的关系

为了方便处理 我们把空格的点放在右边

那么这样的话 当前空格这个位置是必胜的 当且仅当 这个空格的位置肯定是匹配的必须点

想一想 这个空格如果不是必须点 走到对面的时候 就可以变成了 左右的点调转的一个局面 就是不是必须点 肯定走到一条边 使得整个图又变成了

又回到了原来的样子(扭转乾坤??)

所以当且仅当这个空格是二分匹配必须点的时候 才必胜 否则必败

在这里不用所有点都匹配 所有点匹配是对于整个图来说所有的人都不是绝顶聪明的

这里而是见一步走一步的其实画个图更明了

 

 

#include <bits/stdc++.h>
using namespace std;
const int Maxn=45;
struct node{int x,y,next;}edge[Maxn*Maxn*10]; int len,first[Maxn*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,M; char st[Maxn][Maxn]; int dx[4]={-1,0,1,0}; int dy[4]={0,1,0,-1}; int ans[Maxn*Maxn];
 
int match[Maxn*Maxn]; int chw[Maxn*Maxn];
bool Find(int x)
{
  for(int k=first[x];k!=-1;k=edge[k].next)
  {
    int y=edge[k].y;
    if(!chw[y])
    {
      chw[y]=1;
      if(match[y]==0||(Find(match[y]))){match[y]=x; return 1;}
    }
  }return 0;
}
 
int num[Maxn][Maxn],Col[Maxn][Maxn]; int xb,yb;
vector<int>V;
int main()
{
  scanf("%d%d",&N,&M);
  for(int i=1;i<=N;i++) for(int j=1;j<=M;j++)
  {
    scanf("\n%c",&st[i][j]);
    if(st[i][j]=='.') xb=i,yb=j;
  }
  int K; scanf("%d",&K); int P=0,x,y; 
 
  for(int T=1;T<=2*K;T++)
  {
    len=0; memset(first,-1,sizeof(first)); int p[2]={0,0};
    for(int i=1;i<=N;i++) for(int j=1;j<=M;j++)
    {
      if(st[i][j]=='.') p[P]++,num[i][j]=p[P],Col[i][j]=P;
      else if(st[i][j]=='X') p[0]++,num[i][j]=p[0],Col[i][j]=0;
      else if(st[i][j]=='O') p[1]++,num[i][j]=p[1],Col[i][j]=1;
    }
 
    for(int i=1;i<=N;i++)
    {
      for(int j=1;j<=M;j++)
      {
        if(Col[i][j]==P)
        {
          for(int k=0;k<4;k++)
          {
            int xx=i+dx[k]; int yy=j+dy[k];
            if(Col[xx][yy]!=P&&xx>0&&yy>0&&xx<=N&&yy<=M) ins(num[xx][yy],num[i][j]);
          }
        }
      }
    }
 
    memset(match,0,sizeof(match));
    for(int i=1;i<=p[P^1];i++){memset(chw,0,sizeof(chw)); Find(i);}
 
    memset(chw,0,sizeof(chw));
    if(Find(match[num[xb][yb]])||(match[num[xb][yb]]==0)) ans[T]=0;
    else ans[T]=1;
 
    scanf("%d%d",&x,&y); swap(st[x][y],st[xb][yb]); xb=x; yb=y;
    P^=1;
 
  }
 
  V.clear();
  for(int T=1;T<=2*K;T+=2)
    if(ans[T]&ans[T+1]) V.push_back(T/2+1);
   
  printf("%d\n",V.size()); for(int i=0;i<V.size();i++) printf("%d\n",V[i]);
  return 0;
}
View Code

 

清明期间抽了点时间去学上下界网络流....

2016.4.5

[Noi2002]Savage

 首先枚举?作为答案 然后可以列出$(Ci+Pi*x) \mod ?$在什么情况下互不相同 ?就是答案 然后列出:

$(Pi-Pj)x-?y=(Cj-Ci)$ 然后用拓展欧几里得求出两个相同的时候的时间 显然$x \not\in \left[ 0,min\left( Li,Lj \right) \right] $

 

#include <bits/stdc++.h>
using namespace std;
const int Maxn=16;
void exgcd(int a,int b,int &x,int &y){if(b==0){x=1; y=0; return ;} else{int tx,ty; exgcd(b,a%b,tx,ty); x=ty; y=tx-a/b*ty;}}
int gcd(int a,int b){return (b==0)?a:gcd(b,a%b);}
struct node{int c,p,l;}A[Maxn]; int N;
int main()
{
  scanf("%d",&N); int maxC=0; for(int i=1;i<=N;i++) scanf("%d%d%d",&A[i].c,&A[i].p,&A[i].l),maxC=max(maxC,A[i].c),A[i].c--;
  for(int b=maxC;b<=1000000;b++)
  {
    bool bk=true;
    for(int i=1;i<=N;i++)
    {
    for(int j=i+1;j<=N;j++)
    {
      int a=A[i].p-A[j].p; int d=A[j].c-A[i].c;
      int g=gcd(a,b); int x,y;
      if(d%g!=0) continue;
      exgcd(a,b,x,y);
      x=x*(d/g); y=y*(d/g);
      x=(x%abs((b/g))+abs((b/g)))%abs((b/g));
      if(x<=min(A[i].l,A[j].l)) bk=false;
      if(!bk) break;
    }
    if(!bk) break;
    }
    if(bk){printf("%d\n",b); break;}
  }
  return 0;
}
View Code

 

 

[HNOI2009]有趣的数列

 记住就好Catalan数 组合数预处理阶乘 然后分解质因数合并

 

#include <bits/stdc++.h>
using namespace std;
const int Maxn=2e6+10;
typedef long long LL;
int prime[Maxn],pri,Minx[Maxn]; bool V[Maxn]; int N,P;
void Prime()
{
  memset(V,1,sizeof(V)); V[1]=0;
  memset(prime,0,sizeof(prime)); pri=0;
  for(int i=2;i<=2000000;i++)
  {
    if(V[i]){prime[++pri]=i; V[i]=0; Minx[i]=i;}
    for(int j=1;(i*prime[j]<=2000000&&j<=pri);j++)
    {
      V[i*prime[j]]=0; Minx[i*prime[j]]=prime[j];
      if(i%prime[j]==0) break;
    }
  }
}
int num[Maxn];
void Count(int x,int opt){while(x!=1) num[Minx[x]]+=opt,x/=Minx[x];}
int main()
{
  scanf("%d%d",&N,&P); Prime();
  for(int i=1;i<=2*N;i++) Count(i,1);
  for(int i=1;i<=N;i++) Count(i,-1);
  for(int i=1;i<=N;i++) Count(i,-1); Count(N+1,-1);
  LL ans=1; for(int i=1;i<=2*N;i++) while(num[i]) ans=(ans*i)%P,num[i]--;
  return printf("%lld\n",ans),0;
}
View Code

 

 

[Sdoi2010]外星千足虫

 xor高斯消元 记录最远交换那行

#include <bits/stdc++.h>
const int Maxn=1010;
using namespace std; char st[2*Maxn]; int N,K;
bitset<Maxn>A[2*Maxn]; int ans[Maxn];
void Gauss()
{
  int far=N;
  for(int i=1;i<=N;i++)
  {
    if(!A[i][i]) for(int j=i+1;j<=K;j++){if(A[j][i]) {swap(A[i],A[j]); far=max(far,j); break; } }
    if(A[i][i]){for(int j=i+1;j<=K;j++) if(A[j][i]) A[j]^=A[i];}
    else{printf("Cannot Determine\n"); return ;}
  }
  printf("%d\n",far);
  for(int i=N;i>=1;i--)
  {
    ans[i]=A[i][N+1];
    for(int j=i+1;j<=N;j++) if(A[i][j]) ans[i]^=ans[j];
  }
  for(int i=1;i<=N;i++) puts(ans[i]?"?y7M#":"Earth");
}
int main()
{
  scanf("%d%d",&N,&K); scanf("\n");
  for(int i=1;i<=K;i++)
  {
    gets(st+1); int len=strlen(st+1);
    int l=0;
    for(int j=1;j<=len;j++)
    {
      if(st[j]=='0') A[i][++l]=0;
      if(st[j]=='1') A[i][++l]=1;
    } 
  }
  Gauss();
  return 0;
}
View Code

 

2017.4.6

[中山市选2011]完全平方数

二分答案然后容斥原理 发现质数的平方很快满

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL Maxn=51000;
LL T,N; bool V[Maxn]; LL pri,prime[Maxn];
void Prime()
{
  memset(V,1,sizeof(V)); V[1]=0; pri=0;
  for(LL i=2;i<=50000;i++)
  {
    if(V[i]) prime[++pri]=i;
    for(LL j=1;(j<=pri)&&(i*prime[j]<=50000);j++)
    {
      V[i*prime[j]]=0;
      if(i%prime[j]==0) break;
    }
  }
}
LL ans=0;
void Dfs(LL x,LL opt,LL s,LL lim)
{
  for(LL i=x;i<=50000;i++)
  {
    if(s*prime[i]*prime[i]<=lim) Dfs(i+1,-opt,s*prime[i]*prime[i],lim);
    else break;
  }
  ans=(ans+(lim/s)*opt);
}
int main()
{
  scanf("%lld",&T); Prime();
  while(T--)
  {
    scanf("%lld",&N);
    LL L=1; LL R=2*1e9; LL ret;
    while(L<=R)
    {
      LL mid=(L+R)>>1; ans=0; Dfs(1,1,1,mid);
      if(ans>=N) ret=mid,R=mid-1;
      else L=mid+1; 
    }
    printf("%lld\n",ret);
  }
  return 0;
}
View Code

 

 

[HAOI2012]外星人

好题 发现$\phi(2)$一消就没了 对于一个$\phi(\left( p^q) \right))$ 发现当p=2是直接消 p不等于2时(p-1)必定为偶数 然后又可以用phi消掉一个2

所以的话有$F(p^q)=F(p^(q-1))+F(p)$ $F(p)=F(p-1)$ $F(2)=F(1)=1$

F函数就是要消多少次 有多少个2 转移显然 但是要判断 如果一开始p没有2 就要消多一次 才能产生2

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL Maxn=1e5+10;
LL prime[Maxn],pri,F[Maxn]; bool V[Maxn];
void Prime()
{
  memset(V,1,sizeof(V)); V[1]=0;
  pri=0; F[1]=1;
  for(LL i=2;i<=1e5;i++)
  {
    if(V[i]){prime[++pri]=i; F[i]=F[i-1];}
    for(LL j=1;(j<=pri)&&(i*prime[j]<=1e5);j++)
    {
      V[i*prime[j]]=0;
      F[i*prime[j]]=F[i]+F[prime[j]];
      if(i%prime[j]==0) break;
    }
  }
}
LL T,M;
int main()
{
  scanf("%lld",&T); Prime();
  while(T--)
  {
    LL ans=0; bool fir=1;
    scanf("%lld",&M);
    for(LL i=1;i<=M;i++)
    {
      LL p,q; scanf("%lld%lld",&p,&q);
      if(p==2) fir=0;
      ans+=F[p]*q;
    }printf("%lld\n",ans+fir);
  }
  return 0;
}
View Code

 

 

[TJOI2015]线性代数

化简有:$\sigma_{j=1}^{N} \left( \sigma_{k=1}^{N} A[k]*B[j,k]-C[j] \right) * A[j]$

发现当$A[j]$与$A[k]$同时为1是$B[k,j]$才算 $C[j]$只和选和不选有关 简单的最小割线性规划模型

#include <bits/stdc++.h>
using namespace std;
const int Maxn=510;
const int inf=1e9;
struct node{int x,y,next,c,other;}edge[Maxn*Maxn*10]; int len,first[Maxn*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*Maxn]; 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();
    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(edge[k].c,flow-delta));
      edge[k].c-=minf; edge[edge[k].other].c+=minf;
      delta+=minf;
    }
  }
  if(delta==0) dep[x]=0;
  return delta;
}
int B[Maxn][Maxn]; int C[Maxn]; int N;
int main()
{
  len=0; memset(first,-1,sizeof(first));
  scanf("%d",&N); int ans=0;
  for(int i=1;i<=N;i++) for(int j=1;j<=N;j++) scanf("%d",&B[i][j]),ans+=B[i][j];
  for(int i=1;i<=N;i++) scanf("%d",&C[i]); ST=N+1; ED=ST+1;
  for(int i=1;i<=N;i++)
  {
    ins(ST,i,B[i][i]); ins(i,ED,C[i]);
    for(int j=i+1;j<=N;j++)
    {
      ins(ST,i,B[i][j]); ins(i,j,B[i][j]);
      ins(ST,j,B[j][i]); ins(j,i,B[j][i]);
    }
  }
  int delta=0;
  while(Bfs())
  {
    if(delta=Dfs(ST,inf)) ans-=delta;
  }
  return printf("%d\n",ans),0;
}
/*
3
1 2 1
3 1 0
1 2 3
2 3 7
*/
View Code

 

Tyvj1952 Easy

期望好题 F[i]表示到第i位的得分期望 L[i]表示连续到第i位的长度期望 注意 期望的平方不等于平方的期望 例子(两面的筛子 1和2)

有"o"$F[i]=F[i-1]+2*L[i-1]+1$ "?"$F[i]=F[i-1]+L[i-1]+0.5$ "x" $F[i]=F[i-1]$ L转移显然

#include <bits/stdc++.h>
using namespace std;
const int Maxn=300010;
double D[Maxn],F[Maxn]; int N; char st[Maxn];
int main()
{
  scanf("%d",&N); for(int i=1;i<=N;i++) scanf("\n%c",&st[i]);
  F[0]=D[0]=0;
  for(int i=1;i<=N;i++)
  {
    if(st[i]=='x') F[i]=F[i-1],D[i]=0;
    if(st[i]=='?') D[i]=(D[i-1]+1)/2.0,F[i]=F[i-1]+D[i-1]+0.5;
    if(st[i]=='o') D[i]=(D[i-1]+1),F[i]=F[i-1]+2*D[i-1]+1;
  }
  return printf("%.4lf\n",F[N]),0;
}
View Code

 

 

 2017.4.7

IncDec Sequence

男神好劲啊 首先差分 然后就是在差分的序列里面 同时一边+1 一边-1 或者只+1 只-1 操作次数为序列中正数和负数的绝对值的最大值 显然

那么 种类呢 考虑差分后是以第一个元素上下波动的 所以的话 正负抵消完后 剩下的可能有一个数 有可能自己消 有可能和1消 所以正数和负数的差的绝对值就是了

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL Maxn=1e5+10;
LL N,A[Maxn],B[Maxn];
int main()
{
  scanf("%lld",&N); for(int i=1;i<=N;i++) scanf("%lld",&A[i]);
  for(int i=2;i<=N;i++) B[i]=A[i]-A[i-1];
  LL pos=0,neg=0;
  for(int i=1;i<=N;i++) if(B[i]<0) neg+=-B[i]; else pos+=B[i];
  printf("%lld\n%lld\n",max(neg,pos),abs(pos-neg)+1ll);
  return 0;
}
View Code

 

[Ontak2013]Miny

 首先 可以炸到的 就用一条有向边 比如说炸了x可以炸到y 那么为了以后处理方便 就是y->x 那么现在就是要求有多少个指到i表示我i炸了其它也炸

但是我们发现 我们有一个半径 可以找到一个区间我们是可以炸的 但是区间连边的话 边数会很多 所以考虑线段树优化构图 区间的话当作一个点

那么显然炸了fa可以炸到son 所以son->fa 然后把一些互相可以炸的点用Tarjan缩成一个点 DAG跑拓扑dp即可 

但是拓扑也是有技巧的 如果我们考虑统计联通块点的个数 那么就会重复统计 所以的话 我们拓扑的是一个联通块最小可以去到哪里 最大可以去到哪里

相减即可得出答案

 

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int Maxn=100010;
struct EDGE{int x,y,next;}edge[Maxn*30],e[Maxn*30]; int len,lene,first[Maxn*4],fir[Maxn*4];
void ins(int x,int y){len++; edge[len].x=x; edge[len].y=y; edge[len].next=first[x]; first[x]=len;}
void eins(int x,int y){lene++; e[lene].x=x; e[lene].y=y; e[lene].next=fir[x]; fir[x]=lene;}
LL X[Maxn],Rad[Maxn],pos[Maxn]; pair<LL,LL>pr[Maxn];
int tot,rt; int lc[Maxn*4],rc[Maxn*4],lx[Maxn*4],rx[Maxn*4];
 
int Link(int &u,int L,int R,int k)
{
  if(!u) u=++tot; lx[u]=L; rx[u]=R;
  if(L==R) return u;
  int mid=(L+R)>>1;
  if(k<=mid){int p=lc[u]; int pos=Link(lc[u],L,mid,k); if(!p) ins(lc[u],u); return pos;}
  else{int p=rc[u]; int pos=Link(rc[u],mid+1,R,k); if(!p) ins(rc[u],u); return pos;}
}
 
void INS(int &u,int L,int R,int l,int r,int x)
{
  if(!u) u=++tot; lx[u]=L; rx[u]=R;
  if(L==l&&R==r){if(x!=u) ins(u,x); return ;}
  int mid=(L+R)>>1;
  if(l>mid){int p=rc[u]; INS(rc[u],mid+1,R,l,r,x); if(!p) ins(rc[u],u);}
  else if(r<=mid){int p=lc[u]; INS(lc[u],L,mid,l,r,x); if(!p) ins(lc[u],u);}
  else
  {
    int p1,p2; p1=lc[u]; p2=rc[u];
    INS(lc[u],L,mid,l,mid,x); INS(rc[u],mid+1,R,mid+1,r,x);
    if(!p1) ins(lc[u],u); if(!p2) ins(rc[u],u);
  }
}
 
int low[Maxn*4],dfn[Maxn*4],id,cnt,belong[Maxn*4],l_[Maxn*4],r_[Maxn*4];
bool insta[Maxn*4]; int S[Maxn*4],tp;
void Dfs(int x)
{
  low[x]=dfn[x]=++id; S[++tp]=x; insta[x]=1;
  for(int k=first[x];k!=-1;k=edge[k].next)
  {
    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(low[x]==dfn[x])
  {
    int i=S[tp]; cnt++;
    do
    {
      i=S[tp]; tp--; belong[i]=cnt; insta[i]=0;
      l_[cnt]=min(l_[cnt],lx[i]);
      r_[cnt]=max(r_[cnt],rx[i]);
    }while(i!=x);
  }
}
 
int T,N;
 
int ru[Maxn*4]; int Q[Maxn*4],head,tail;
 
int Find1(LL x)
{
  int L=1; int R=N; int ret=1;
  while(L<=R)
  {
    int mid=(L+R)>>1;
    if(x<=X[mid]){R=mid-1; ret=mid;}
    else L=mid+1;
  }return ret;
}
 
int Find2(LL x)
{
  int L=1; int R=N; int ret=1;
  while(L<=R)
  {
    int mid=(L+R)>>1;
    if(x>=X[mid]){L=mid+1; ret=mid;}
    else R=mid-1;
  }return ret;
}
 
int main()
{
  scanf("%d",&T); memset(first,-1,sizeof(first)); memset(fir,-1,sizeof(fir));
  while(T--)
  {
    scanf("%d",&N); tot=len=0;
 
    for(int i=1;i<=N;i++) scanf("%lld%lld",&pr[i].first,&pr[i].second);
    sort(pr+1,pr+N+1);
 
    for(int i=1;i<=N;i++) X[i]=pr[i].first,Rad[i]=pr[i].second;
    X[N+1]=(LL)INT_MAX*INT_MAX; tot=rt=0;
 
    for(int i=1;i<=N;i++)
    {
      int L=Find1(X[i]-Rad[i]);
      int R=Find2(X[i]+Rad[i]);
      pos[i]=Link(rt,1,N,i);
      INS(rt,1,N,L,R,pos[i]);
    }
 
    for(int i=1;i<=tot;i++){belong[i]=insta[i]=0; dfn[i]=low[i]=-1; l_[i]=INT_MAX; r_[i]=0;} 
 
    id=cnt=0;
 
    for(int i=1;i<=tot;i++)
      if(dfn[i]==-1) Dfs(i);
 
    for(int i=1;i<=cnt;i++) ru[i]=0; lene=0;
 
    for(int i=1;i<=len;i++)
    {
      int x=edge[i].x; int y=edge[i].y;
      if(belong[x]!=belong[y])
      {
        eins(belong[x],belong[y]);
        ru[belong[y]]++;
      }
    }
 
    head=1; tail=0; for(int i=1;i<=cnt;i++) if(ru[i]==0) Q[++tail]=i;
    while(head<=tail)
    {
      int x=Q[head];
      for(int k=fir[x];k!=-1;k=e[k].next)
      {
        int y=e[k].y;
        l_[y]=min(l_[y],l_[x]);
        r_[y]=max(r_[y],r_[x]);
        ru[y]--; if(ru[y]==0) Q[++tail]=y;
      }
      head++;
    }
    for(int i=1;i<N;i++) printf("%d ",r_[belong[pos[i]]]-l_[belong[pos[i]]]+1);
    printf("%d\n",r_[belong[pos[N]]]-l_[belong[pos[N]]]+1);
 
    for(int i=1;i<=tot;i++) lc[i]=rc[i]=0;
    for(int i=1;i<=tot;i++) first[i]=-1,fir[i]=-1;
  }
  //cout<<(double)clock()<<endl;
  return 0;
}
View Code

 

[CQOI2009]叶子的染色

首先有一个yy的结论 根节点不是叶子节点 随便一个都没关系 (画图?)

然后就是treedp了 F[i][0/1]表示这个点的颜色 然后管理子树下最少用多少个节点 考虑如果和孩子的颜色相同 可以把孩子的颜色去掉 不同就加上

 

#include <bits/stdc++.h>
using namespace std;
const int Maxn=10010;
struct node{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 N,M; int F[Maxn][2]; int Col[Maxn];
void Dfs(int x,int fa)
{
  if(x>M) F[x][0]=F[x][1]=1;
  for(int k=first[x];k!=-1;k=edge[k].next)
  {
    int y=edge[k].y;
    if(y!=fa)
    {
      Dfs(y,x);
      if(y<=M)
      {
        if(Col[y]) F[x][0]+=F[y][1];
        if(!Col[y]) F[x][1]+=F[y][0];
      }
      else
      {
        F[x][0]+=min(F[y][1],(F[y][0]-1));
        F[x][1]+=min(F[y][0],(F[y][1]-1));
      }
    }
  }
}
int main()
{
  len=0; memset(first,-1,sizeof(first));
  scanf("%d%d",&N,&M); memset(F,63,sizeof(F));
  for(int i=1;i<=M;i++){scanf("%d",&Col[i]); F[i][Col[i]]=1; F[i][Col[i]^1]=0; }
  for(int i=1;i<N;i++){int x,y; scanf("%d%d",&x,&y); ins(x,y); ins(y,x);}
  int root=rand()%(N-M)+1+M;
  Dfs(root,0);
  return printf("%d\n",min(F[root][0],F[root][1])),0;
}
View Code

 

 2017.4.8

[HNOI2006]潘多拉的盒子

很强势这道题 首先如果已经知道包不包含关系 就是求最长链 求最长链用tarjan缩点 然后在DAG上求 考虑缩成的点都是一模一样的 所以顺序没有关系只和里面的点的数目有关系

至于包不包含关系 假设有两台机器x和y 当x输出时y不输出 就是x不包含于y 定义二元组F(x,y) 表示x走到哪个零件和y走到哪个零件 如果所有的二元组都合法即整个x包含于y 否则不包含

这道题难点就是抛开构成的串不管 从零件上下手 同时走来做

 

#include <bits/stdc++.h>
using namespace std;
const int Maxn=55;
struct node{int x,y,next;}edge[Maxn*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 s,N,M; bool Print[Maxn][Maxn]; int dir[Maxn][Maxn][2];
queue<pair<int,int> >Q; bool F[Maxn][Maxn];
int Check(int x,int y)
{
  while(!Q.empty()) Q.pop();
  if(!Print[x][0]&&Print[y][0]) return 0;
  Q.push(make_pair(0,0)); memset(F,0,sizeof(F)); F[0][0]=1;
  while(!Q.empty())
  {
    pair<int,int> sta=Q.front();
    for(int k=0;k<2;k++)
    {
      if(!Print[x][dir[x][sta.first][k]]&&Print[y][dir[y][sta.second][k]]) return 0;
      else if(!F[dir[x][sta.first][k]][dir[y][sta.second][k]])
        F[dir[x][sta.first][k]][dir[y][sta.second][k]]=1,Q.push(make_pair(dir[x][sta.first][k],dir[y][sta.second][k]));
    }
    Q.pop();
  }
  return 1; 
}
 
int low[Maxn],dfn[Maxn],cnt,id,belong[Maxn]; bool insta[Maxn]; stack<int>S;
 
vector<int>V[Maxn]; int ru[Maxn];
queue<int>q; int L[Maxn];
 
void Dfs(int x)
{
  low[x]=dfn[x]=++id; S.push(x); insta[x]=1;
  for(int k=first[x];k!=-1;k=edge[k].next)
  {
    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=S.top(); cnt++;
    do
    {
      i=S.top(); belong[i]=cnt; insta[i]=0; L[cnt]++;
      S.pop();
    }while(i!=x);
  }
}
 
int G[Maxn];
int main()
{
  scanf("%d",&s); memset(Print,0,sizeof(Print));
  for(int i=1;i<=s;i++)
  {
    scanf("%d%d",&N,&M);
    for(int j=0;j<M;j++){int x; scanf("%d",&x); Print[i][x]=1;}
    for(int j=0;j<N;j++) scanf("%d%d",&dir[i][j][0],&dir[i][j][1]);
  }
  len=0; memset(first,-1,sizeof(first));
 
  for(int i=1;i<=s;i++)
    for(int j=1;j<=s;j++)
      if((i!=j)&&Check(i,j)) ins(j,i);
 
  memset(low,-1,sizeof(low));
  memset(dfn,-1,sizeof(dfn));
  memset(insta,0,sizeof(insta));
  memset(L,0,sizeof(L));
  id=cnt=0;
  for(int i=1;i<=s;i++) if(dfn[i]==-1) Dfs(i);
 
  memset(ru,0,sizeof(ru));
  for(int i=1;i<=len;i++)
  {
    int x=edge[i].x; int y=edge[i].y;
    if(belong[x]!=belong[y])
      V[belong[x]].push_back(belong[y]),ru[belong[y]]++;
  }
  for(int i=1;i<=cnt;i++){G[i]=L[i]; if(ru[i]==0) q.push(i);} int maxx=0;
  while(!q.empty())
  {
    int x=q.front();
    for(int i=0;i<V[x].size();i++)
    {
      int y=V[x][i];
      G[y]=max(G[x]+L[y],G[y]); maxx=max(maxx,G[y]);
      ru[y]--; if(!ru[y]) q.push(y);
    }q.pop();
  }
  return printf("%d\n",maxx),0;
}
View Code

 

 

[JSOI2009]球队收益

输赢同时割 考虑只管一遍 比赛分别连两个表示赢的点的流量 源点连比赛一个流量 表示赢的点连汇点 每条边一个差分赢了的费用(经典构图) 预处理出所有点都输的费用和已经赢了的费用

 

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
#include<climits>
using namespace std;
typedef long long LL;
const LL Maxn=6010;
struct node{LL x,y,next,d,c,other;}edge[Maxn*50]; LL len,first[Maxn];
void ins(LL x,LL y,LL d,LL c)
{
  len++; LL k1=len; edge[len].x=x; edge[len].y=y; edge[len].d=d; edge[len].c=c; edge[len].next=first[x]; first[x]=len;
  len++; LL k2=len; edge[len].x=y; edge[len].y=x; edge[len].d=-d; edge[len].c=0; edge[len].next=first[y]; first[y]=len;
  edge[k1].other=k2; edge[k2].other=k1;
}
LL N,M,win[Maxn],lose[Maxn],C[Maxn],D[Maxn];
pair<LL,LL>pr[Maxn]; LL m[Maxn];
 
LL pos[Maxn],pre[Maxn]; queue<LL>Q; LL Dis[Maxn]; LL ST,ED; bool V[Maxn];
bool Spfa()
{
  while(!Q.empty()) Q.pop(); Q.push(ST);
  for(int i=1;i<=ED;i++) Dis[i]=LLONG_MAX/2; Dis[ST]=0;
  memset(V,0,sizeof(V)); V[ST]=1;
  while(!Q.empty())
  {
    LL x=Q.front();
    for(LL k=first[x];k!=-1;k=edge[k].next)
    {
      LL y=edge[k].y;
      if(edge[k].c&&Dis[y]>Dis[x]+edge[k].d)
      {
        Dis[y]=Dis[x]+edge[k].d; pre[y]=k; pos[y]=x;
        if(!V[y])
        {
          V[y]=1; Q.push(y);
        }
      }
    }
    Q.pop(); V[x]=0;
  }
  return Dis[ED]!=(LLONG_MAX/2);
}
 
int main()
{
  scanf("%lld%lld",&N,&M); len=0; memset(first,-1,sizeof(first));
  for(LL i=1;i<=N;i++) scanf("%lld%lld%lld%lld",&win[i],&lose[i],&C[i],&D[i]);
  memset(m,0,sizeof(m)); ST=N+M+1; ED=ST+1;
  for(LL i=1;i<=M;i++)
  {
    scanf("%lld%lld",&pr[i].first,&pr[i].second),m[pr[i].first]++,m[pr[i].second]++;
    ins(ST,i+N,0,1); ins(i+N,pr[i].first,0,1); ins(i+N,pr[i].second,0,1);
  }
   
  LL ans=0;
  for(LL i=1;i<=N;i++)
  {
    LL l=win[i]; LL r=lose[i]+m[i]; ans+=D[i]*(lose[i]+m[i])*(lose[i]+m[i])+C[i]*win[i]*win[i];
    while(l<win[i]+m[i])
    {
      ins(i,ED,C[i]*(2*l+1)-D[i]*(2*r-1),1);
      l++; r--;
    }
  }
   
  while(Spfa())
  {
    LL minf=LLONG_MAX;
    for(LL i=ED;i!=ST;i=pos[i]) minf=min(minf,edge[pre[i]].c);
    for(LL i=ED;i!=ST;i=pos[i])
    {
      edge[pre[i]].c-=minf;
      edge[edge[pre[i]].other].c+=minf;
      ans+=minf*edge[pre[i]].d;
    }
  }
  return printf("%lld\n",ans),0;
}
View Code

 

 

2017.4.10

 

[NOI2009]管道取珠

好蠢啊.... 其实平方 就是把这个游戏 拆成一模一样的一个游戏 在两个游戏中 玩出相同的方案就是了 平方就是一个玩出每种的一次 对应另外一个也玩出这种的一次 那么每人玩出这种游戏都是有ai种 所以就平方了

然后考虑dp F[i][a][b]表示同时玩前面i个球 然后第一个游戏里面的上面玩a个 第二个里面玩b个即可

#include <bits/stdc++.h>
using namespace std;
const int Maxn=510;
const int Mod=1024523;
int F[2][Maxn][Maxn];
char A[Maxn],B[Maxn]; int N,M;
int main()
{
  scanf("%d%d",&N,&M);
  for(int i=N;i>=1;i--) scanf("\n%c",&A[i]);
  for(int i=M;i>=1;i--) scanf("\n%c",&B[i]);
  memset(F,0,sizeof(F)); F[0][0][0]=1; int ST=0;
  for(int i=1;i<=N+M;i++)
  {
    memset(F[1-ST],0,sizeof(F[1-ST]));
    for(int a=0;a<=min(N,i);a++)
      for(int b=0;b<=min(N,i);b++)
      {
        if(a&&b) F[1-ST][a][b]=(F[1-ST][a][b]+(A[a]==A[b])*F[ST][a-1][b-1])%Mod;
        if(a&&b<i) F[1-ST][a][b]=(F[1-ST][a][b]+(A[a]==B[i-b])*F[ST][a-1][b])%Mod;
        if(a<i&&b) F[1-ST][a][b]=(F[1-ST][a][b]+(B[i-a]==A[b])*F[ST][a][b-1])%Mod;
        if(a<i&&b<i) F[1-ST][a][b]=(F[1-ST][a][b]+(B[i-a]==B[i-b])*F[ST][a][b])%Mod;
      }
    ST=1-ST;
  }
  return printf("%d\n",F[ST][N][N]),0;
}
View Code

 

 

墨墨的等式

首先 我们可以先选择一个数p那么对于任意一个B我们都可以化成B%p 记B%p为x

我们可以找出一个能到达的最小的y 使得$y \mod p = x$ 然后用y+kp在B中统计即可

为什么取任意一个都可以呢 这个好像只能意会不可言传 对于每一个y大于p或者小于p是没有关系的 都是一样算 最多在当前一个数枚举k的时候变成在另外一个数的y中

Dijksta_Heap即可算出最小的y

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL Maxn=500010;
struct node{LL x,y,next,d;}edge[Maxn*13]; 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,BMin,BMax,A[Maxn];
LL D[Maxn]; priority_queue<pair<LL,LL>,vector<pair<LL,LL> >,greater<pair<LL,LL> > >Q;
void Dijksta_Heap()
{
  memset(D,126,sizeof(D));
  D[0]=0; while(!Q.empty()) Q.pop(); Q.push(make_pair(0,0));
  while(!Q.empty())
  {
    pair<LL,LL> x=Q.top(); Q.pop();
    if(x.second>D[x.second]) continue;
    for(LL k=first[x.second];k!=-1;k=edge[k].next)
    {
      LL y=edge[k].y;
      if(D[y]>D[x.second]+edge[k].d)
      {
        D[y]=D[x.second]+edge[k].d;
        Q.push(make_pair(D[y],y));
      }
    }
  }
}
int main()
{
  scanf("%lld%lld%lld",&N,&BMin,&BMax); BMin--; len=0; memset(first,-1,sizeof(first));
  LL mn=LLONG_MAX; for(LL i=1;i<=N;i++) scanf("%lld",&A[i]),mn=min(mn,A[i]);
 
  for(LL i=0;i<mn;i++)
    for(LL j=1;j<=N;j++)
      ins(i,(i+A[j])%mn,A[j]);
   
  Dijksta_Heap();
 
  LL ans=0;
  for(LL i=0;i<mn;i++)
  {
    LL l=max(0LL,(BMin-D[i]+mn)/mn);
    LL r=max(0LL,(BMax-D[i]+mn)/mn);
    ans+=r-l;
  }
  return printf("%lld\n",ans),0;
}
View Code

 2017.4.11

 

Tyvj1953 Normal

好劲的题啊 我们应该从考虑某个点贡献这个方向去想 因为期望有线性性 所以的话 每对点的贡献是互不相干的 所以我们就可以把贡献加起来

对于两个点i和j 他们产生的贡献就是 当且仅当两个点之间的路径的点都没有被选 然后选了i表示i作父亲 选了j表示j作父亲 那么概率都是$\frac{1}{dis(i,j)+1}$

所以总的贡献就是$\sum_{i=1}^{N} \sum_{j=1}^{N} \frac{1}{dis(i,j)+1}$

然后变成$2*\sum_{i=1}^{N} D(i)*\frac{1}{i+1} $

我们定义D(i)为距离为i的点对个数 当然最后要加上n个只剩下1个节点的情况

这样的话 这道题就可以完美解决 距离为多少的点对用点分治保证O(N*2logN)

合并的时候 用FFT合并 也是O(N*2logN) (考虑每层只有size[x]个 然后每层合并接近于size[x]*size[x]合并 用FFT优化为 size[x]*log(size[x]) 考虑根节点的size为N 下面的节点均摊log层

所以有O(N*2logN)的好的时间复杂度 调了一个晚上因为 const int pi=cos(-1); .......................

#include <bits/stdc++.h>
using namespace std;
const int Maxn=30010;
const double pi=acos(-1);
struct node{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;}
complex<double>A[Maxn*8],B[Maxn*8]; int Alen,Blen;
int D[Maxn]; bool bo[Maxn];
int size[Maxn];
void Dfs1(int x,int fa)
{
  size[x]=1;
  for(int k=first[x];k!=-1;k=edge[k].next)
  {
    int y=edge[k].y;
    if(y!=fa&&!bo[y]){Dfs1(y,x); size[x]+=size[y];}
  }
}
int MIN;
void Find_root(int &root,int x,int fa,int siz)
{
  int now=siz-size[x];
  for(int k=first[x];k!=-1;k=edge[k].next)
  {
    int y=edge[k].y;
    if(!bo[y]&&y!=fa)
    {
      Find_root(root,y,x,siz);
      now=max(now,size[y]);
    }
  }
  if(now<MIN){root=x; MIN=now;}
}
int G[Maxn],F[Maxn];
void Count(int x,int fa,int d)
{
  G[d]++; Blen=max(Blen,d);
  for(int k=first[x];k!=-1;k=edge[k].next)
  {
    int y=edge[k].y;
    if(y!=fa&&!bo[y]) Count(y,x,d+1);
  }
}
int R[Maxn*8];
void DFT(complex<double> *a,int n,int opt)
{
  for(int i=0;i<n;i++) if(i<R[i]) swap(a[R[i]],a[i]);
  for(int i=1;i<n;i<<=1)
  {
    complex<double>wn(cos(pi/i),sin(pi/i*opt));
    for(int j=0;j<n;j+=(i<<1))
    {
      complex<double>w(1.0,0.0);
      for(int k=0;k<i;k++,w*=wn)
      {
        complex<double>x,y;
        x=a[j+k]; y=a[j+k+i]*w;
        a[j+k]=x+y; a[j+k+i]=x-y;
      }
    }
  }
}
void FFT()
{
  int N,M,L=0;
  M=Alen+Blen; for(N=1;N<=M;N<<=1) L++;
  for(int i=0;i<N;i++) R[i]=(R[i>>1]>>1)|((i&1)<<(L-1));
  DFT(A,N,1); DFT(B,N,1);
  for(int i=0;i<=N;i++) A[i]=A[i]*B[i];
  DFT(A,N,-1);
  for(int i=0;i<=N;i++)
  {
    D[i]+=(int)(A[i].real()/N+0.5);
    A[i].real()=A[i].imag()=0.0;
    B[i].real()=B[i].imag()=0.0;
  }
}
void Dfs(int x)
{
  Dfs1(x,-1);
  MIN=size[x]; Find_root(x,x,-1,size[x]);
  Alen=0; F[0]=1;
  for(int k=first[x];k!=-1;k=edge[k].next)
  {
    int y=edge[k].y;
    if(!bo[y])
    {
      Blen=0;
      Count(y,x,1);
      for(int i=0;i<=Blen;i++) B[i].real()=G[i];
      for(int i=0;i<=Alen;i++) A[i].real()=F[i];
      FFT();
      Alen=max(Alen,Blen);
      for(int i=1;i<=Alen;i++){F[i]+=G[i]; G[i]=0;}
    }
  }
  for(int i=0;i<=Alen;i++) F[i]=0;
  bo[x]=1;
  for(int k=first[x];k!=-1;k=edge[k].next)
  {
    int y=edge[k].y;
    if(!bo[y]) Dfs(y);
  }
}
int main()
{
  int n; scanf("%d",&n); 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);}
  memset(bo,0,sizeof(bo)); Dfs(0);
  double ans=0.0;
  for(int i=1;i<n;i++) ans+=2.0*(double)D[i]*(1.0/(i+1));
  return printf("%.4lf\n",ans+n),0;
}
View Code

 

 

[HAOI2011]向量

我好菜啊 发现影响到x的向量 当合成一下 就会只剩下 +-(2a,0) +-(2b,0) (a,b) (b,a)

同样的 影响到y 的向量 也是 +-(0,2a) +-(0,2b) (a,b) (b,a)

我们发现那些+-(2a,0) +-(2b,0) 和+-(0,2a) +-(0,2b) 是不影响到彼此的 这些向量也可以取很多个 而且取的数量任意即可

而对于剩下的(a,b) (b,a)这些向量也就只有一个 如果有偶数个可以拆成+-(2a,0) +-(2b,0) +-(0,2a) +-(0,2b) 这些

所以分四种情况 (x,y) (x-a,y-b) (x-b,y-a) (x-a-b,y-a-b) 然后判断是否整除即可

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
LL gcd(LL x,LL y){return (y==0)?x:gcd(y,x%y);}
LL d;
bool Check(LL x,LL y){if(!(x%d)&&!(y%d)) return 1; return 0;}
int main()
{
  LL T; scanf("%lld",&T);
  while(T--)
  {
    LL a,b,x,y; scanf("%lld%lld%lld%lld",&a,&b,&x,&y);
    d=gcd(a,b)<<1;
    if(d==0&&(x!=0||y!=0)){printf("N\n"); continue;}
    if(Check(x,y)||Check(x+a,y+b)||Check(x+b,y+a)||Check(x+a+b,y+a+b)) printf("Y\n");
    else printf("N\n");
  }
  return 0;
}
View Code

 

2017.4.12

一场模拟赛,有时间更

Y 4330 JSOI2012 爱之项链   31 58
Y 4331 JSOI2012 越狱老虎桥   18 24
Y 4332 JSOI2012 分零食   65 116
  4333 JSOI2012 智者的考验   15 58

2017.4.13

上午做回比赛 下午学了一下斯坦纳树 看学习笔记

晚上写了一题

[Ctsc2011]幸福路径

yy了一个spfa最长路 wa了 膜POPOQQQ 倍增+floyd

#include <bits/stdc++.h>
using namespace std;
const int Maxn=105;
double F[Maxn][Maxn],G[Maxn][Maxn],A[Maxn],lo; int ST; double temp;
int main()
{
  int N,M; scanf("%d%d",&N,&M);
  for(int i=1;i<=N;i++) scanf("%lf",&A[i]);
  scanf("%d",&ST); scanf("%lf",&lo);

  memset(F,-63,sizeof(F)); for(int i=1;i<=N;i++) F[i][i]=0.0;
  for(int i=1;i<=M;i++){int x,y; scanf("%d%d",&x,&y); F[x][y]=A[y]*lo;}
  temp=lo;
  for(int T=0;T<=70;T++,temp*=temp)
  {
    memset(G,-63,sizeof(G));
    for(int k=1;k<=N;k++)
      for(int i=1;i<=N;i++)
        for(int j=1;j<=N;j++)
          G[i][j]=max(G[i][j],F[i][k]+F[k][j]*temp);
    memcpy(F,G,sizeof(F));
  }
  double maxx=0.0;
  for(int i=1;i<=N;i++) maxx=max(maxx,F[ST][i]+A[ST]);
  return printf("%.1lf\n",maxx),0;
}
View Code

顺便记录一下今天400题

 2017.4.1

 

Calc

好强的题啊 我看到题面就不是很会做了

首先有

$\sum\limits_{1<=i<=N} \sum\limits_{1<=j<=N} 1 [i+j|ij]$

令$d=(i,j) i=ad j=bd (a,b)=1$

$(a+b)d|abd^2$

又$(a,b)=1$ $(a+b,b)=1$ $(a,a+b)=1$

得$(a+b)|d$

我们设$d=(a+b)*t$ 由于j的限制 我们有$b(a+b)t<=N$       $t<=\lfloor \frac{N}{b(a+b)} \rfloor$

然后我们可以推出式子 通过枚举a,b有

$\sum\limits_{1<=a<=\sqrt{N}} \sum\limits_{1<=b<=\sqrt{N}} \lfloor \frac{N}{b*(a+b)} \rfloor [(a,b)==1]$

$=\sum\limits_{d} \mu(d) \sum\limits_{1<=a<=\frac{\sqrt{N}}{d}} \sum\limits_{1<=b<=\frac{\sqrt{N}}{d}} \lfloor \frac{n}{b[(a+b)d]d} \rfloor $

对a进行分块 我们有两种分法 都可以ac

$\frac{\frac{n}{bd^2}}{\frac{n}{bd^2(a+b)}}-b$

$\frac{n}{\frac{n}{b[(a+b)d]d}}/(bd^2)-b$

这样就可以完美解决问题

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int Maxn=47000;
int N,q; int prime[Maxn],pri,mu[Maxn]; bool V[Maxn];
void Prime()
{
  memset(V,1,sizeof(V)); pri=0; memset(mu,0,sizeof(mu)); mu[1]=1;
  for(int i=2;i<=q;i++)
  {
    if(V[i]) prime[++pri]=i,mu[i]=-1;
    for(int j=1;(j<=pri)&&(i*prime[j]<=q);j++)
    {
      V[i*prime[j]]=0; mu[i*prime[j]]=-mu[i];
      if(i%prime[j]==0){mu[i*prime[j]]=0; break;}
    }
  }
}
int main()
{
  scanf("%d",&N); q=(int)sqrt(N); LL ans=0; Prime();
  for(int d=1;d<=q;d++)
  {
    for(int b=1;b<=q/d;b++)
    {
      int lim=N/d/d/b; int nx;
      for(int a=1;((a+b)<=lim)&&(a<b);a=nx+1)
      {
        nx=min(lim,min(b-1,(N/(N/(b*(a+b)*d*d)))/(b*d*d)-b));
        ans+=(nx-a+1)*(N/(b*d*d)/(a+b))*mu[d];
      } 
    }
  }
  return printf("%lld\n",ans),0;
}
View Code 

 

[JLOI2015]管道连接

斯坦纳树 然后森林的话再dp一次就可以了

#include <bits/stdc++.h>
using namespace std;
const int Maxn=4010;
const int inf=1e9;
struct node{int x,y,next,d;}edge[Maxn*4]; 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,M,P; vector<int>Vec[20]; int pos; int Val[Maxn]; int F[Maxn][1030]; int bit[20];
bool V[Maxn]; queue<pair<int,int> >Q;

void Spfa()
{
  while(!Q.empty())
  {
    pair<int,int>x=Q.front(); Q.pop(); V[x.first]=0;
    for(int k=first[x.first];k!=-1;k=edge[k].next)
    {
      int y=edge[k].y;
      if(F[y][x.second]>F[x.first][x.second]+edge[k].d)
      {
        F[y][x.second]=F[x.first][x.second]+edge[k].d;
        if(!V[y])
        {
          V[y]=1;
          Q.push(make_pair(y,x.second));
        }
      }
    }
  }
}

int Col[20]; int G[1030];

int pe[Maxn];

int main()
{
  len=0; memset(first,-1,sizeof(first));
  scanf("%d%d%d",&N,&M,&P); pos=N;
  for(int i=1;i<=M;i++)
  {
    int x,y,d;
    scanf("%d%d%d",&x,&y,&d);
    ins(x,y,d); ins(y,x,d);
  }

  for(int i=0;i<=10;i++) bit[i]=(1<<i);

  memset(F,63,sizeof(F));
  for(int i=1;i<=P;i++)
  {
    int x,y; scanf("%d%d",&x,&y);
    Vec[x].push_back(y);
    F[y][1<<(i-1)]=0; Col[x]|=(1<<(i-1));
  }

  for(int i=1;i<=pos;i++) F[i][0]=0;

  for(int p=1;p<bit[P];p++)
  {
    memset(V,0,sizeof(V));
    for(int i=1;i<=pos;i++)
    {
      for(int s=p;s;s=((s-1)&p))
        F[i][p]=min(F[i][p],F[i][s]+F[i][p-s]);
      if(F[i][p]<inf){Q.push(make_pair(i,p)); V[i]=1;}
    }
    Spfa();
  }

  int ans=0; memset(G,63,sizeof(G));

  for(int p=0;p<bit[P];p++)
  {
    int w=0;
    for(int i=0;i<P;i++) if((p&bit[i])==bit[i]) w|=Col[i+1];
    for(int i=1;i<=pos;i++) G[p]=min(G[p],F[i][w]);
    for(int s=p;s;s=(s-1)&p) G[p]=min(G[p],G[s]+G[p-s]);
  }

  return printf("%d\n",G[bit[P]-1]),0;
}
View Code 

 

2017.4.14

[Zjoi2012]数列(sequence)

发现一个很好玩的东西 就是每次分一个数 再分一次还是只剩下两个数

比如 49

24 25

12 13

.....

然后是交叉的 我们如果有一个记忆化搜索 这道题算是可以很快的解决?

等等 这道题是高精度....................考虑一下怎么记忆化??

我的方法是 把一个高精度变成一个字符串 然后用string类型存 用map hash...

然后还发现一个吐血的事情 这道题的答案也要高精度

那就map<string,node>mp 对应一个结构体 变成数组??

然后居然搞爆了这题......

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <map>
using namespace std;
const int Maxn=110;
struct node
{
  int A[Maxn],len;
  node(){memset(A,0,sizeof(A)); len=0;}
  node(int _p){memset(A,0,sizeof(A)); A[1]=1; len=1;}
  friend node operator +(const node &x,const node &y)
  {
    node z; z.len=max(x.len,y.len);
    for(int i=1;i<=z.len;i++) z.A[i]=x.A[i]+y.A[i];
    for(int i=1;i<=z.len;i++)
      if(z.A[i]>=10)
        z.A[i+1]+=z.A[i]/10,z.A[i]%=10;
    while(z.A[z.len+1])
    {
      z.len++;
      z.A[z.len+1]+=z.A[z.len]/10; z.A[z.len]%=10;
    }
    return z;
  }
}x; char st[Maxn]; map<string,node>mp;
 
node Add(node x)
{
  node z=x;
  z.A[1]++; for(int i=1;i<=z.len;i++) if(z.A[i]>=10) z.A[i+1]+=z.A[i]/10,z.A[i]%=10;
  while(z.A[z.len+1])
  {
    z.len++; z.A[z.len+1]+=z.A[z.len]/10; z.A[z.len]%=10;
  }
  return z;
}
 
node Div(node x)
{
  node z=x;
  for(int i=1;i<=z.len;i++)
  {
    if(z.A[i]&1) z.A[i-1]+=5;
    z.A[i]/=2;
  }
  while(!z.A[z.len]) z.len--;
  return z;
}
 
node Dfs(node x)
{
  string str; str.clear();
  for(int i=1;i<=x.len;i++)
    str.push_back(x.A[i]+'0');
  node k=mp[str];
  if(k.len) return k;
  else
  {
    if(x.A[1]&1) return mp[str]=Dfs(Div(x))+Dfs(Add(Div(x)));
    else return mp[str]=Dfs(Div(x));
  }
}
int main()
{
  int T; scanf("%d",&T);
 
  string str; mp.clear();
  str.clear(); str.push_back('0'); mp[str]=node(1);
  str.clear(); str.push_back('1'); mp[str]=node(1);
 
  while(T--)
  {
    scanf("%s",st+1); int len=strlen(st+1);
    memset(x.A,0,sizeof(x.A)); x.len=0;
    for(int i=len;i>=1;i--) x.A[len-i+1]=st[i]-'0'; x.len=len;
    node ans=Dfs(x);
    for(int i=ans.len;i>=1;i--) printf("%d",ans.A[i]); printf("\n");
  }
  return 0;
}
View Code

 

 

[cqoi2012]局部极小值

这道题是神题好题啊 为什么我现在才做

好像一开始方向就错了................很憋屈

我一开始看到4 7状压 然而我只接触过行列的状压 然后啥都没想出来

后来膜题解才知道是按点状压 $F[i][sta]$表示前i个数 那些局部最小值的点 有无放的状态..

然后考虑转移 从上个数开始的 $F[i][sta]=F[i-1][sta]+(p[sta]-i+1)$

$p[sta]$表示局部最小值为这些点 那些点已经确认是可以填的了 包括那些点 因为局部最小值的点都没用填 附近的点也不能填

考虑如果这个数填在某个局部最小值那里 就有 $F[i][sta]+=F[i-1][sta-j]$

但是还有一个bug 就是有一些不是局部最小值的点填了局部最小值

考虑容斥 如果有一些点可以做局部最小值的话 就Dfs进去搞 想一下??

#include <bits/stdc++.h>
using namespace std;
const int Maxn=10;
const int Mod=12345678;
int F[30][1024]; int dx[9]={1,1,0,-1,-1,-1,0,1}; int dy[9]={0,1,1,1,0,-1,-1,-1};
int N,M; char str[Maxn][Maxn]; int num[Maxn][Maxn];
 
int bit[Maxn]; int cnt[1<<Maxn]; pair<int,int>A[Maxn];
int ans=0;
 
int dp()
{
  int n=0;
  for(int i=1;i<=N;i++) for(int j=1;j<=M;j++)
  {
    if(str[i][j]=='X') num[i][j]=(1<<n),n++;
    else num[i][j]=0;
  }
 
  memset(cnt,0,sizeof(cnt));
  for(int s=0;s<(1<<n);s++)
  {
    for(int i=1;i<=N;i++) for(int j=1;j<=M;j++)
    {
      bool bk=1;
      for(int k=0;k<8;k++)
      {
        int x=dx[k]+i; int y=dy[k]+j;
        if(x<=N&&y<=M&&x>0&&y>0) if((s&num[x][y])!=num[x][y]) bk=false;
      }
      if(bk&&(((s&num[i][j])==num[i][j])||(str[i][j]=='.'))) cnt[s]++;
    }
  }
 
  memset(F,0,sizeof(F));  F[0][0]=1;
  for(int i=1;i<=N*M;i++)
  {
    for(int s=0;s<(1<<n);s++)
    {
      if(cnt[s]>=i)
      {
        F[i][s]=(F[i][s]+F[i-1][s]*(cnt[s]-i+1))%Mod;
        for(int j=0;j<n;j++)
          if((s&bit[j])==bit[j])
            F[i][s]=(F[i][s]+F[i-1][s-bit[j]])%Mod;
      }
    }
  }
  return F[N*M][(1<<n)-1];
}
 
int n=0;
 
void Dfs(int k,int x,int y)
{
  if(x==N+1)
  {
    ans=(ans+dp()*(((k-n)&1)?1:(-1)))%Mod;
    return ;
  }
  if(y==M+1){Dfs(k,x+1,1); return ;}
  bool bk=1;
  for(int kk=0;kk<8;kk++)
  {
    int xx=x+dx[kk]; int yy=y+dy[kk];
    if(xx<=N&&yy<=M&&xx>0&&yy>0) if(str[xx][yy]=='X'){bk=0; break;}
  }
  if(bk)
  {
    if(str[x][y]=='X')
    {
      A[k]=make_pair(x,y);
      Dfs(k+1,x,y+1);
    }
    else
    {
      Dfs(k,x,y+1);
      A[k]=make_pair(x,y);
      str[x][y]='X'; Dfs(k+1,x,y+1); str[x][y]='.';
    }
  }
  else Dfs(k,x,y+1);
}
 
int main()
{
  scanf("%d%d",&N,&M); for(int i=0;i<=8;i++) bit[i]=(1<<i);
  for(int i=1;i<=N;i++) for(int j=1;j<=M;j++) scanf("\n%c",&str[i][j]);
 
  for(int i=1;i<=N;i++) for(int j=1;j<=M;j++)
    if(str[i][j]=='X')
    {
      n++;
      for(int k=0;k<8;k++)
      {
        int xx=i+dx[k]; int yy=j+dy[k];
        if(xx<=N&&yy<=M&&xx>0&&yy>0) if(str[xx][yy]=='X'){printf("0\n"); return 0;}
      }
    }
 
  ans=0;
  Dfs(1,1,1);
  printf("%d\n",(ans+Mod)%Mod);
  return 0;
}
View Code

 

2017.4.15

 

[Noi2012]迷失游乐园

一看题就知道是环套树加期望dp

然后自己太垃圾好像推错了 题解http://www.cnblogs.com/Tunix/p/4561493.html

代码略恶心

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdlib>
#include <cmath>
#include <stack>
#include <vector>
 
using namespace std;
const int Maxn=100010;
struct node{int x,y,d,next,other; bool bo;}edge[Maxn*2]; int len,first[Maxn];
void ins(int x,int y,int d)
{
  len++; int k1=len; edge[len].x=x; edge[len].y=y; edge[len].d=d; edge[len].next=first[x]; first[x]=len;
  len++; int k2=len; edge[len].x=y; edge[len].y=x; edge[len].d=d; edge[len].next=first[y]; first[y]=len;
  edge[k1].other=k2; edge[k2].other=k1;
}
 
int N,M; double F[Maxn],G[Maxn],siz[Maxn]; int root;
 
double Ffa[Maxn];
 
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&&Ffa[y]==1)
    {
      Dfs(y,x);
      F[x]+=F[y]+edge[k].d; siz[x]++;
    }
  }
  if(siz[x]) F[x]=F[x]*(1.0/siz[x]);
  else F[x]=0.0;
}
 
 
void Dfs1(int x,int fa,int d)
{
  if(x!=root)
  {
    if(Ffa[fa]==0)
    {
      if(siz[fa]==1) G[x]=d;
      else G[x]+=(F[fa]*siz[fa]-F[x]-d)/(siz[fa]-1)+d;
    }
    else G[x]=(F[fa]*siz[fa]-F[x]-d+G[fa]*Ffa[fa])/(siz[fa]-1+Ffa[fa])+d;
  }
  for(int k=first[x];k!=-1;k=edge[k].next)
  {
    int y=edge[k].y;
    if(y!=fa&&Ffa[y]==1) Dfs1(y,x,edge[k].d);
  }
}
 
bool vis[Maxn]; bool bo[Maxn*2]; stack<int>S; vector<int>V; int cnt; bool bk=false;
void Dfs2(int x,int fa)
{
  vis[x]=1; S.push(x);
  for(int k=first[x];k!=-1;k=edge[k].next)
  {
    int y=edge[k].y;
    if(!bo[k])
    {
      if(vis[y])
      {
        int i=S.top();
        do
        {
          i=S.top(); V.push_back(i); S.pop();
        }while(i!=y);
        bk=true; return ;
      }
      else
      {
        bo[k]=bo[edge[k].other]=1;
        Dfs2(y,x);
        if(bk) return ;
      }
    }
  }
  S.pop();
}
 
int nexd[Maxn],pred[Maxn],nex[Maxn],pre[Maxn];
 
int main()
{
  len=0; memset(first,-1,sizeof(first));
  scanf("%d%d",&N,&M);
  for(int i=1;i<=M;i++){int x,y,d; scanf("%d%d%d",&x,&y,&d); ins(x,y,d);}
  if(M==N-1)
  {
    memset(F,0,sizeof(F));
    memset(G,0,sizeof(G));
    root=1;
    for(int i=1;i<=N;i++) Ffa[i]=1;
    Ffa[root]=0;
     
    Dfs(root,0);
    Dfs1(root,0,0);
    double ans=0.0;
    for(int i=1;i<=N;i++)
    {
      if(i==root) ans+=F[i];
      else ans+=(F[i]*siz[i]+G[i])/(siz[i]+1);
    }
    printf("%.5lf\n",ans/N);
  }
  else
  {
    memset(vis,0,sizeof(vis));
    memset(bo,0,sizeof(bo)); bk=0;
    Dfs2(1,0);
     
    for(int i=1;i<=N;i++) Ffa[i]=1;
    for(int i=0;i<V.size();i++) Ffa[V[i]]++;
     
    memset(F,0,sizeof(F));
    for(int i=0;i<V.size();i++)
    {
      root=V[i];
      Dfs(root,0);
    }
     
    memset(bo,0,sizeof(bo));
    for(int i=0;i<V.size();i++)
    {
      int x=V[i],y; if(i==0) y=V[V.size()-1]; else y=V[i-1];
      for(int k=first[x];k!=-1;k=edge[k].next)
      {
        if(edge[k].y==y&&!bo[k])
        {
          pred[i]=nexd[((i-1)+V.size())%V.size()]=edge[k].d;
          bo[k]=bo[edge[k].other]=1;
          break;
        }
      }
      nex[i]=(i+1)%V.size(); pre[i]=(i-1+V.size())%V.size();
    }
     
    memset(G,0,sizeof(G));
     
    for(int i=0;i<V.size();i++)
    {
      double p=0.5; double d;
      for(int j=nex[i];j!=i;j=nex[j])
      {
        d=pred[j];
        if(nex[j]==i) G[V[i]]+=(F[V[j]]+d)*p;
        else G[V[i]]+=(F[V[j]]*siz[V[j]]/(siz[V[j]]+1)+d)*p;
        p=p*(1.0/(siz[V[j]]+1));
      }
       
      p=0.5;
      for(int j=pre[i];j!=i;j=pre[j])
      {
        d=nexd[j];
        if(pre[j]==i) G[V[i]]+=(F[V[j]]+d)*p;
        else G[V[i]]+=(F[V[j]]*siz[V[j]]/(siz[V[j]]+1)+d)*p;
        p=p*(1.0/(siz[V[j]]+1));
      }
    }
     
    for(int i=0;i<V.size();i++)
    {
      root=V[i];
      Dfs1(root,0,0);
    }
     
    double ans=0.0;
    for(int i=1;i<=N;i++) ans+=(F[i]*siz[i]+G[i]*Ffa[i])/(siz[i]+Ffa[i]);
     
    printf("%.5lf\n",ans/N);
  }
  return 0;
}
View Code

 

2017.4.16

下午看了一下题 没有写

晚上写了一下最近想写的题

 

[Sdoi2017]数字表格

基础题 然而我基础不算很好

然而化到$\prod\limits_{D} \prod\limits_{d|D} f(d)^{\mu(\frac{D}{d}) \lfloor \frac{n}{D} \rfloor \lfloor \frac{m}{D} \rfloor }$

预处理$\prod\limits_{d|D} f(d)^{\mu(\frac{D}{d})}$

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL Maxn=1000010;
const LL Mod=1e9+7;
LL prime[Maxn]; bool V[Maxn]; LL pri,mu[Maxn]; LL G[Maxn],f[Maxn],n[Maxn],Sum1[Maxn],Sum2[Maxn];
LL Quick_Power(LL x,LL k)
{
  if(k<0) k+=Mod-1;
  LL ans=1;
  while(k)
  {
    if(k&1) ans=(ans*x)%Mod;
    x=(x*x)%Mod; k>>=1;
  }
  return ans;
}
void Pre()
{
  memset(V,1,sizeof(V)); V[1]=0; mu[1]=1; pri=0;
  f[0]=0; f[1]=1; for(LL i=2;i<=1000000;i++) f[i]=(f[i-1]+f[i-2])%Mod;
  for(LL i=2;i<=1000000;i++)
  {
    if(V[i]){prime[++pri]=i; mu[i]=-1;}
    for(LL j=1;(j<=pri)&&(i*prime[j]<=1000000);j++)
    {
      V[i*prime[j]]=0; mu[i*prime[j]]=-mu[i];
      if(i%prime[j]==0){mu[i*prime[j]]=0; break;}
    }
  }
 
  for(LL d=1;d<=1000000;d++) G[d]=1;
 
  for(LL d=1;d<=1000000;d++) n[d]=Quick_Power(f[d],-1);
 
  for(LL d=1;d<=1000000;d++)
    for(LL j=1;(d*j<=1000000);j++)
    {
      if(mu[j]==-1)
        G[d*j]=(G[d*j]*n[d])%Mod;
      if(mu[j]==1)
        G[d*j]=(G[d*j]*f[d])%Mod;
    }
 
  Sum1[0]=1;
  for(LL d=1;d<=1000000;d++)
    Sum1[d]=(Sum1[d-1]*G[d])%Mod;
 
  Sum2[0]=1;
  for(LL d=1;d<=1000000;d++) Sum2[d]=Quick_Power(Sum1[d],-1);
}
 
LL T,N,M;
 
int main()
{
  scanf("%lld",&T); Pre();
  while(T--)
  {
    scanf("%lld%lld",&N,&M); LL nx; LL ans=1;
    for(LL d=1;d<=min(N,M);d=nx+1)
    {
      nx=min(min(N,M),min(N/(N/d),(M/(M/d))));
      ans=(ans*Quick_Power(Sum1[nx]*Sum2[d-1]%Mod,(N/d)*(M/d))%Mod);
    }
    printf("%lld\n",ans);
  }
  return 0;
}
View Code

 

[Violet 6]蒲公英

见clj区间众数

#include <bits/stdc++.h>
using namespace std;
const int Maxn=80010;
int N,M; vector<int>Vec[Maxn]; map<int,int>mp; int tot=0; int Col[Maxn];
int belong[Maxn]; int Block; int F[310][310];
 
int cnt[Maxn]; int A[Maxn];
void Count(int x)
{
  memset(cnt,0,sizeof(cnt)); int maxx=0; int ans=0;
  for(int i=(x-1)*Block+1;i<=N;i++)
  {
    cnt[A[i]]++;
    if((cnt[A[i]]>maxx)||(cnt[A[i]]==maxx&&Col[A[i]]<Col[ans])) maxx=cnt[A[i]],ans=A[i];
    F[x][belong[i]]=ans;
  }
}
 
int Q(int l,int r,int x)
{
  return upper_bound(Vec[x].begin(),Vec[x].end(),r)-lower_bound(Vec[x].begin(),Vec[x].end(),l);
}
 
int Query(int l,int r)
{
  int maxx=0,ans=0,x,cnt=0;
  x=F[belong[l]+1][belong[r]-1];
  if(x){cnt=Q(l,r,x); if(cnt>maxx||(cnt==maxx&&Col[x]<Col[ans])) maxx=cnt,ans=x;}
  for(int i=l;(belong[i]!=(belong[l]+1))&&(i<=r);i++)
  {
    x=A[i]; cnt=Q(l,r,x);
    if(cnt>maxx||(cnt==maxx&&Col[x]<Col[ans])) maxx=cnt,ans=x;
  }
  if(belong[l]!=belong[r])
  {
    for(int i=r;(belong[i]!=(belong[r]-1))&&(i>=l);i--)
    {
      x=A[i]; cnt=Q(l,r,x);
      if(cnt>maxx||(cnt==maxx&&Col[x]<Col[ans])) maxx=cnt,ans=x;
    }
  }
  return ans;
}
 
int main()
{
  scanf("%d%d",&N,&M);
  for(int i=1;i<=N;i++)
  {
    int x; scanf("%d",&x);
    if(mp[x]==0) mp[x]=++tot,Col[tot]=x;
    Vec[mp[x]].push_back(i); A[i]=mp[x];
  }
  Block=(int)ceil(sqrt(N));
  for(int i=1;i<=N;i++) belong[i]=(i-1)/Block+1;
  for(int i=1;i<=belong[N];i++)
    Count(i);
 
  int lastans=0;
  for(int i=1;i<=M;i++)
  {
    int x,y; scanf("%d%d",&x,&y);
    x=(x+lastans-1)%N+1; y=(y+lastans-1)%N+1;
    if(x>y) swap(x,y);
    printf("%d\n",lastans=Col[Query(x,y)]);
  }
  return 0; 
}
View Code

2017.4.17

 

[cqoi2012]编号

很搞笑的一道题 最少有三个不同 可以认为至多有四个相同 也就是说五个以上相同即为不合法 然后发现是7位数 所以我们可以乱搞

按位枚举$F[sta][1][2][3][4][5]$ 表示这五个数的位置是哪五个 分别是什么数 标记了就不合法 $sta$也就是一个组合数..

类似搞一个表格弄一下

#include <bits/stdc++.h>
using namespace std;
const int Maxn=17;
int F[22][Maxn][Maxn][Maxn][Maxn][Maxn]; int K;
int main()
{
  scanf("%d",&K); int ans=0;
  for(int a=0;a<16;a++)
    for(int b=0;b<16;b++)
      for(int c=0;c<16;c++)
        for(int d=0;d<16;d++)
          for(int e=0;e<16;e++)
            for(int f=0;f<16;f++)
              for(int g=0;g<16;g++)
                if((!F[1][a][b][c][d][e])&&(!F[2][a][b][c][d][f])
                 &&(!F[3][a][b][c][d][g])&&(!F[4][a][b][c][e][f])
                 &&(!F[5][a][b][c][e][g])&&(!F[6][a][b][c][f][g])
                 &&(!F[7][a][b][d][e][f])&&(!F[8][a][b][d][e][g])
                 &&(!F[9][a][b][d][f][g])&&(!F[10][a][b][e][f][g])
                 &&(!F[11][a][c][d][e][f])&&(!F[12][a][c][d][e][g])
                 &&(!F[13][a][c][e][f][g])&&(!F[14][a][d][e][f][g])
                 &&(!F[15][b][c][d][e][f])&&(!F[16][b][c][d][e][g])
                 &&(!F[17][b][c][d][f][g])&&(!F[18][b][c][e][f][g])
                 &&(!F[19][b][d][e][f][g])&&(!F[20][a][c][d][f][g])
                 &&(!F[21][c][d][e][f][g]))
                {
                  F[1][a][b][c][d][e]=F[2][a][b][c][d][f]=
                  F[3][a][b][c][d][g]=F[4][a][b][c][e][f]=
                  F[5][a][b][c][e][g]=F[6][a][b][c][f][g]=
                  F[7][a][b][d][e][f]=F[8][a][b][d][e][g]=
                  F[9][a][b][d][f][g]=F[10][a][b][e][f][g]=
                  F[11][a][c][d][e][f]=F[12][a][c][d][e][g]=
                  F[13][a][c][e][f][g]=F[14][a][d][e][f][g]=
                  F[15][b][c][d][e][f]=F[16][b][c][d][e][g]=
                  F[17][b][c][d][f][g]=F[18][b][c][e][f][g]=
                  F[19][b][d][e][f][g]=F[20][a][c][d][f][g]=
                  F[21][c][d][e][f][g]=1;
                  ans++; if(ans==K){printf("%x%x%x%x%x%x%x\n",a,b,c,d,e,f,g); return 0;}
                }
  return 0;
}
View Code

 

[Violet 2]After 17

很快就可以推出$(\sum\limits_{i=1}{n} x[i])^2-(\sum\limits_{i=1}{n} x[i]^2) +(\sum\limits_{i=1}{n} y[i])^2-(\sum\limits_{i=1}{n} y[i]^2)$最小 当然要除二

$x$和$y$可以分开搞 可以证明一定是取极限的 对于前一部分 如果我们知道这个结论 就可以用背包搞出来了 是不是?

然后开搞

#include <bits/stdc++.h>
using namespace std;
const int Maxn=210;
int N; pair<int,int>pr[Maxn]; bool F[Maxn*Maxn*2],G[Maxn*Maxn*2];
int main()
{
  scanf("%d",&N); for(int i=1;i<=N;i++) scanf("%d%d",&pr[i].first,&pr[i].second);
 
  memset(F,0,sizeof(F)); memset(G,0,sizeof(G));
  F[40000]=1;
  for(int i=1;i<=N;i++)
  {
    memcpy(G,F,sizeof(F));
    memset(F,0,sizeof(F));
    for(int j=0;j<=80000;j++)
      if(G[j])
        F[j-pr[i].first]=F[j+pr[i].first]=1;
  }
 
  double ansx=0.0; int minx=INT_MAX;
  for(int i=1;i<=N;i++) ansx-=pr[i].first*pr[i].first;
  for(int i=0;i<=80000;i++) if(F[i]) minx=min(minx,abs(i-40000)); ansx+=minx*minx;
  ansx/=2.0;
 
 
  memset(F,0,sizeof(F)); memset(G,0,sizeof(G));
  F[40000]=1;
  for(int i=1;i<=N;i++)
  {
    memcpy(G,F,sizeof(F));
    memset(F,0,sizeof(F));
    for(int j=0;j<=80000;j++)
      if(G[j])
        F[j-pr[i].second]=F[j+pr[i].second]=1;
  }
 
  double ansy=0.0; minx=INT_MAX;
  for(int i=1;i<=N;i++) ansy-=pr[i].second*pr[i].second;
  for(int i=0;i<=80000;i++) if(F[i]) minx=min(minx,abs(i-40000)); ansy+=minx*minx;
  ansy/=2.0;
 
  return printf("%.2lf\n",ansx+ansy),0;
}
View Code

 

[HNOI2012]与非

原来想想出这道题 发现自己太弱了

然而这个运算 可以通过自己玩一下 变成 ^ & | ~ 四个运算

然后也可发现 在每个数中 两个位置的数字保持相同的 依然相同 然后这两个数就一定不变 否则的话 所有可能都会有

所以找出不变的位置 用并查集搞在一起 然后就类dp这样子就行了

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL Maxn=1010;
LL fa[Maxn]; LL Find(LL x){return (fa[x]==x)?x:Find(fa[x]);} LL Sum[Maxn],s[Maxn];
LL N,K,L,R; LL A[Maxn],bit[Maxn];
 
LL Query(LL r)
{
  LL ans=1LL;
  for(LL i=K-1LL;i>=0;i--)
  {
    if(Find(i)!=i) continue;
    if(Sum[i]<=r)
    {
      r-=Sum[i];
      ans+=1LL<<(s[i]-1LL);
    }
  }
  return ans;
}
 
int main()
{
  scanf("%lld%lld%lld%lld",&N,&K,&L,&R);
  for(LL i=0LL;i<K;i++) fa[i]=i;
  for(LL i=0LL;i<K;i++) bit[i]=(1LL<<i),Sum[i]=(1LL<<i);
  for(LL i=1LL;i<=N;i++) scanf("%lld",&A[i]);
  for(LL i=0LL;i<K;i++)
    for(LL j=i+1LL;j<K;j++)
    {
      bool ok=1;
      for(LL k=1LL;k<=N;k++)
      {
        LL x=(A[k]&bit[i]); LL y=(A[k]&bit[j]);
        if((x&&y)||(!x&&!y)) continue;
        else{ok=0; break;}
      }
      if(ok)
      {
        LL xx=Find(i); LL yy=Find(j);
        if(xx>yy) swap(xx,yy);
        if(xx!=yy){fa[xx]=yy; Sum[yy]+=Sum[xx];}
      }
    }
 
  s[0]=Find(0)==0;
  for(LL i=1;i<K;i++) s[i]=s[i-1]+(Find(i)==i);
  LL ansL=0LL; if(L) ansL=Query(L-1LL);
  LL ansR=0LL; ansR=Query(R);
 
  return printf("%lld\n",ansR-ansL),0;
}
View Code

2017.4.18

考试爆炸预示oi再见

2017.4.19

早上back回比赛

下午做了道恶心题

 

a + b Problem

常见的线段树优化构图 但是这题用主席树优化构图 最小割模型很可见 拆点乱搞 不会看popoQQQ

最大化转最小化 负数加权再mod

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
 
const int Maxn=500010;
const LL inf=1e15;
 
struct EDGE{int x,y,next,other; LL c;}edge[Maxn]; int len,first[Maxn];
void ins(int x,int y,LL 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;
}
 
queue<int>Q; int dep[Maxn]; 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();
    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;
}
 
LL Dfs(int x,LL flow)
{
  LL delta=0;
  if(x==ED) return flow;
  for(int k=first[x];k!=-1;k=edge[k].next)
  {
    int y=edge[k].y;
    if(flow>delta&&edge[k].c&&dep[y]==dep[x]+1)
    {
      LL 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;
}
 
struct node{int a,b,w,l,r,p;}q[5010];
 
int lc[Maxn],rc[Maxn],tot,rt[5010]; int N;
 
void Link(int &u,int L,int R,int k,int id)
{
  if(!u) u=++tot;
  if(L==R)
  {
    ins(id,u+N*2+2,inf);
    return ;
  }
  int mid=(L+R)>>1;
  if(k<=mid)
  {
    int now=lc[u]; Link(lc[u],L,mid,k,id);
    if(lc[u]!=now) ins(lc[u]+N*2+2,u+N*2+2,inf);
  }
  else
  {
    int now=rc[u]; Link(rc[u],mid+1,R,k,id);
    if(rc[u]!=now) ins(rc[u]+N*2+2,u+N*2+2,inf);
  }
}
 
void Merge(int &u1,int u2)
{
  if(!u2) return ;
  if(!u1){u1=u2; return ;}
  int now; ins(N*2+2+u2,N*2+2+u1,inf);
  now=lc[u1]; Merge(lc[u1],lc[u2]); if(lc[u1]!=now) ins(N*2+2+lc[u1],N*2+2+u1,inf);
  now=rc[u1]; Merge(rc[u1],rc[u2]); if(rc[u1]!=now) ins(N*2+2+rc[u1],N*2+2+u1,inf);
}
 
void Query(int u,int L,int R,int l,int r,int id)
{
  if(!u) return ;
  if(L==l&&R==r)
  {
    ins(u+N*2+2,id,inf);
    return ;
  }
  int mid=(L+R)>>1;
  if(r<=mid) Query(lc[u],L,mid,l,r,id);
  else if(l>mid) Query(rc[u],mid+1,R,l,r,id);
  else
  {
    Query(lc[u],L,mid,l,mid,id);
    Query(rc[u],mid+1,R,mid+1,r,id);
  }
}
 
map<int,int>mp; vector<int>V;
 
int main()
{
  scanf("%d",&N); len=tot=0; memset(first,-1,sizeof(first)); memset(rt,0,sizeof(rt));
  for(int i=1;i<=N;i++)
  {
    scanf("%d%d%d%d%d%d",&q[i].a,&q[i].b,&q[i].w,&q[i].l,&q[i].r,&q[i].p);
    V.push_back(q[i].a); V.push_back(q[i].l); V.push_back(q[i].r);
  }
  sort(V.begin(),V.end());
  int len=unique(V.begin(),V.end())-(V.begin()+1);
  for(int i=0;i<=len;i++) mp[V[i]]=i;
 
  ST=N*2+1; ED=N*2+2;
  for(int i=1;i<=N;i++) ins(ST,i*2,(LL)(1e10)-q[i].b),ins(i*2,ED,(LL)(1e10)-q[i].w),ins((i-1)*2+1,i*2,q[i].p);
 
  for(int i=1;i<=N;i++)
  {
    Query(rt[i-1],0,len,mp[q[i].l],mp[q[i].r],(i-1)*2+1);
    Link(rt[i],0,len,mp[q[i].a],i*2);
    Merge(rt[i],rt[i-1]);
  }
 
  LL ans=0,delta;
  while(Bfs())
  {
    if(delta=Dfs(ST,inf)) ans+=delta;
  }
  /*
  for(LL k=first[ST];k!=-1;k=edge[k].next) printf("%lld %lld\n",edge[k].y>>1,edge[k].c);
 
  printf("\n");
  for(LL k=first[ED];k!=-1;k=edge[k].next)
    printf("%lld %lld\n",edge[edge[k].other].x>>1,edge[edge[k].other].c);
 
  printf("\n");
  for(LL i=1;i<=len;i++) if((edge[i].x<=2*N)&&(edge[i].y<=2*N)&&(edge[i].x+1==edge[i].y)) printf("%lld %lld %lld\n",edge[i].x,edge[i].y,edge[i].c);
  */
  //for(LL i=1;i<=30;i+=2) printf("%lld %lld %lld\n",edge[i].x,edge[i].y,edge[i].c);
 
  ans=(ans%(LL)(1e10)-(LL)(1e10));
 
  return printf("%lld\n",-ans),0;
}
View Code

晚上写了又一道恶心题

 

Arietta

一开始yy贪心 然后换思路 主席树套最大流

很开心的码了一发wa

发现合并的时候 子节点和另外的子节点有边 考虑怎么处理

沙茶的我跑去问wph

wph大神:建多一倍点

大概就这样

#include <bits/stdc++.h>
using namespace std;
struct EDGE{int x,y,next,c,other;}edge[1400000]; int len,first[300010];
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;
}
queue<int>Q; int dep[300010]; 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]&&edge[k].c)
      {
        dep[y]=dep[x]+1;
        Q.push(y);
      }
    }
    Q.pop();
  }
  return dep[ED]>0;
}
int Dfs(int x,int flow)
{
  int delta=0;
  if(x==ED) return flow;
  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));
      edge[k].c-=minf; edge[edge[k].other].c+=minf;
      delta+=minf;
    }
  }
  if(delta==0) dep[x]=0;
  return delta;
}
int N,M; int A[300010]; vector<int>G[300010];
 
struct node
{
  int l,r,d;
  node(){}
  node(int _l,int _r,int _d)
  {
    l=_l; r=_r; d=_d;
  }
};
vector<node>T[300010];
 
int lc[300010],rc[300010]; int tot,rt[300010];
 
void Link(int &u,int L,int R,int k)
{
  if(!u) u=++tot;
  if(L==R){ins(u,ED,1); return ;}
  int mid=(L+R)>>1;
  if(k<=mid)
  {
    int now=lc[u]; Link(lc[u],L,mid,k);
    if(lc[u]!=now) ins(u,lc[u],INT_MAX);
  }
  else
  {
    int now=rc[u]; Link(rc[u],mid+1,R,k);
    if(rc[u]!=now) ins(u,rc[u],INT_MAX);
  }
}
 
void Merge(int &u1,int u2)
{
  if(!u2) return ;
  if(!u1){u1=u2; return ;}
  tot++;
  ins(tot,u1,INT_MAX); ins(tot,u2,INT_MAX);
  
  lc[tot]=lc[u1]; rc[tot]=rc[u1]; u1=tot;
 
  int now;
  now=lc[u1]; Merge(lc[u1],lc[u2]); if(lc[u1]) ins(u1,lc[u1],INT_MAX);
  now=rc[u1]; Merge(rc[u1],rc[u2]); if(rc[u1]) ins(u1,rc[u1],INT_MAX);
}
 
void Query(int u,int L,int R,int l,int r,int id)
{
  if(!u) return ;
  if(L==l&&R==r)
  {
    ins(id,u,INT_MAX);
    return ;
  }
  int mid=(L+R)>>1;
  if(r<=mid) Query(lc[u],L,mid,l,r,id);
  else if(l>mid) Query(rc[u],mid+1,R,l,r,id);
  else
  {
    Query(lc[u],L,mid,l,mid,id);
    Query(rc[u],mid+1,R,mid+1,r,id);
  }
}
 
void Dfs1(int x)
{
  Link(rt[x],1,N,A[x]);
  for(int i=0;i<G[x].size();i++)
  {
    int y=G[x][i];
    Dfs1(y);
    Merge(rt[x],rt[y]);
  }
  for(int i=0;i<T[x].size();i++)
  {
    tot++;
    ins(ST,tot,T[x][i].d);
    Query(rt[x],1,N,T[x][i].l,T[x][i].r,tot);
  }
}
 
int main()
{
  scanf("%d%d",&N,&M); len=0; memset(first,-1,sizeof(first));
  for(int i=2;i<=N;i++){int x; scanf("%d",&x); G[x].push_back(i);}
  for(int i=1;i<=N;i++) scanf("%d",&A[i]);
  for(int i=1;i<=M;i++)
  {
    int l,r,d,x; scanf("%d%d%d%d",&l,&r,&x,&d);
    T[x].push_back(node(l,r,d));
  }
  ST=300000; ED=300001; tot=0; Dfs1(1);
  int ans=0; int delta;
  while(Bfs())
  {
    if(delta=Dfs(ST,INT_MAX)) ans+=delta;
  }
  return printf("%d\n",ans),0;
}
View Code

 2017.4.20

今天好有动力啊!

然而调了一早上表示很无语

 

[PA2014]Fiolki

首先建成一棵树 然后沉淀就是lca 然后按照深度 倒入的时间戳 和反应的时间戳排序即可

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const LL Maxn=500010;
LL N,M,K; LL tot=0; LL A[200010]; LL fa[200010][25];
LL dep[200010]; LL ru[200010]; LL T[200010];

struct node
{
  LL x,y,d,t1,t2;
  node(){}
  node(LL _x,LL _y,LL _d,LL _t1,LL _t2){x=_x; y=_y; d=_d; t1=_t1; t2=_t2;}
}Q[Maxn];

bool Cmp(const node &x,const node &y)
{
  if(x.d!=y.d) return x.d>y.d;
  if(x.t1!=y.t1) return x.t1<y.t1;
  return x.t2<y.t2;
}

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

void Dfs(LL x)
{
  for(LL k=first[x];k!=-1;k=edge[k].next)
  {
    LL y=edge[k].y;
    dep[y]=dep[x]+1; T[y]=edge[k].t; fa[y][0]=x;
    Dfs(y);
  }
}

pair<LL,LL> LCA(LL x,LL y)
{
  if(dep[x]<dep[y]) swap(x,y);
  LL deep=dep[x]-dep[y]; LL last,p;
  for(LL i=20;i>=0;i--) if(deep>=(1<<i))
  {
    deep-=(1<<i); last=x; p=i;
    x=fa[x][i];
  }
  if(x==y)
  {
    for(LL i=p-1;i>=0;i--) last=fa[last][i];
    return make_pair(x,T[last]);
  }
  for(LL i=20;i>=0;i--)
  {
    if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
  }
  return make_pair(fa[x][0],max(T[x],T[y]));
}

int main()
{
  scanf("%lld%lld%lld",&N,&M,&K);
  for(LL i=1;i<=N;i++) scanf("%lld",&A[i]);

  memset(ru,0,sizeof(ru)); len=0; memset(first,-1,sizeof(first));
  for(LL i=1;i<=M;i++){LL x,y; scanf("%lld%lld",&x,&y); ins(y,x,i); ru[x]++;}
  
  memset(dep,0,sizeof(dep));
  for(LL i=1;i<=N;i++) if(ru[i]==0){dep[i]=1; Dfs(i);}

  for(LL j=1;j<=20;j++)
    for(LL i=1;i<=N;i++)
      fa[i][j]=fa[fa[i][j-1]][j-1];


  for(LL i=1;i<=K;i++)
  {
    LL x,y; scanf("%lld%lld",&x,&y); pair<LL,LL> p=LCA(x,y);
    if(p.first!=0) Q[++tot]=node(x,y,dep[p.first],p.second,i);
  }
  sort(Q+1,Q+tot+1,Cmp); LL ans=0;
  for(LL i=1;i<=tot;i++)
  {
    LL minf=min(A[Q[i].x],A[Q[i].y]);
    A[Q[i].x]-=minf; A[Q[i].y]-=minf; ans+=minf*2;
  }
  return printf("%lld\n",ans),0;
}
View Code 

 

JSOI2012 智者的考验

智者的考验 果然我不是智者

首先就是很快看到格子很少才6个 颜色的情况也就16种 首先按钮有2^5=32种 然后就是取反 就是按了按钮1+2=3+4+5

然后我才意识到线段树可以求区间前缀种类数目这个什么鬼东西 颜色情况只有十六种 对于一个区间可以处理前缀为每种颜色情况的数量

然后卡时过

#include <bits/stdc++.h>
using namespace std;

const int Maxn=1000010;

int r,c; int bit[10][10]; int ED; int F[100],Hash[100]; int Flen; int bottom[10];
void Dfs(int sta,int k)
{
  if(k==r*c+1)
  {
    if(F[sta]==0){F[sta]=++Flen; Hash[Flen]=sta;}
    return ;
  }
  else
  {
    Dfs(sta,k+1);
    Dfs(sta^bottom[k],k+1);
  }
}

int rt,tot; int lc[Maxn*2],rc[Maxn*2],lazy[Maxn*2];
struct node{int num[17],C;}P[Maxn*2];

node update(node u1,node u2)
{
  node u;
  u.C=F[Hash[u1.C]^Hash[u2.C]];
  int lsta=Hash[u1.C];
  for(int i=1;i<=Flen;i++){u.num[i]=0; u.num[i]+=u1.num[i]+u2.num[F[Hash[i]^lsta]];}
  return u;
}

void Push_down(int u,int L,int R)
{
  if(lazy[u])
  {
    int id=lazy[u];
    int mid=(L+R)>>1;

    if(lc[u])
    {
      P[lc[u]].C=F[((mid-L+1)&1)*bottom[id]];
      for(int i=1;i<=Flen;i++) P[lc[u]].num[i]=0;
      P[lc[u]].num[F[bottom[id]]]=(mid-L+2)>>1;
      P[lc[u]].num[F[0]]=(mid-L+1)>>1;
      lazy[lc[u]]=id;
    }
    if(rc[u])
    {
      P[rc[u]].C=F[((R-(mid+1)+1)&1)*bottom[id]];
      for(int i=1;i<=Flen;i++) P[rc[u]].num[i]=0;
      P[rc[u]].num[F[bottom[id]]]=(R-(mid+1)+2)>>1;
      P[rc[u]].num[F[0]]=(R-(mid+1)+1)>>1;
      lazy[rc[u]]=id;
    }
    lazy[u]=0;
  }
}

void Link(int &u,int L,int R,int k,int id)
{
  if(!u) u=++tot;
  if(L==R)
  {
    P[u].num[P[u].C]=0;
    P[u].C=F[bottom[id]];
    P[u].num[F[bottom[id]]]=1;
    return ;
  }
  Push_down(u,L,R);
  int mid=(L+R)>>1;
  if(k<=mid) Link(lc[u],L,mid,k,id);
  else Link(rc[u],mid+1,R,k,id);
  P[u]=update(P[lc[u]],P[rc[u]]);
}

void Change(int u,int L,int R,int l,int r,int id)
{
  if(L==l&&R==r)
  {
    P[u].C=F[((R-L+1)&1)*bottom[id]];
    for(int i=1;i<=Flen;i++) P[u].num[i]=0;
    P[u].num[F[bottom[id]]]=(R-L+1+1)>>1;
    P[u].num[F[0]]=(R-L+1)>>1;
    lazy[u]=id;
    return ;
  }
  Push_down(u,L,R);
  int mid=(L+R)>>1;
  if(r<=mid) Change(lc[u],L,mid,l,r,id);
  else if(l>mid) Change(rc[u],mid+1,R,l,r,id);
  else
  {
    Change(lc[u],L,mid,l,mid,id);
    Change(rc[u],mid+1,R,mid+1,r,id);
  }
  P[u]=update(P[lc[u]],P[rc[u]]);
}

inline node Query1(int u,int L,int R,int l,int r)
{
  if(L==l&&R==r) return P[u];
  int mid=(L+R)>>1;
  Push_down(u,L,R);
  if(r<=mid) return Query1(lc[u],L,mid,l,r);
  else if(l>mid) return Query1(rc[u],mid+1,R,l,r);
  else
  {
    return update(Query1(lc[u],L,mid,l,mid),Query1(rc[u],mid+1,R,mid+1,r));
  }
}

inline int Query2(int u,int L,int R,int l,int r)
{
  if(l>r) return 0;
  if(L==l&&R==r) return Hash[P[u].C];
  int mid=(L+R)>>1;
  Push_down(u,L,R);
  if(r<=mid) return Query2(lc[u],L,mid,l,r);
  else if(l>mid) return Query2(rc[u],mid+1,R,l,r);
  else
  {
    return Query2(lc[u],L,mid,l,mid)^Query2(rc[u],mid+1,R,mid+1,r);
  }
}

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()
{
  r=Read(); c=Read();
  for(int i=1;i<=r;i++) for(int j=1;j<=c;j++)
  {
    bit[i][j]=(1<<((i-1)*c+j-1));
    int x=Read(); if(x==1) ED|=bit[i][j];
  }

  for(int i=1;i<=r;i++) for(int j=1;j<=c;j++) bottom[i]|=bit[i][j];
  for(int j=1;j<=c;j++) for(int i=1;i<=r;i++) bottom[r+j]|=bit[i][j];

  Flen=0; memset(F,0,sizeof(F));
  Dfs(0,1);

  int N,M; N=Read(); M=Read();
  P[0].C=F[0]; for(int i=1;i<=Flen;i++) P[0].num[i]=0;

  rt=tot=0;
  for(int i=1;i<=N;i++) Link(rt,1,N,i,1);
  for(int i=1;i<=M;i++)
  {
    int t=Read();
    if(t==0)
    {
      int d=Read(),x=Read();
      Link(rt,1,N,d,x);
    }
    if(t==1)
    {
      int l=Read(),r=Read();
      printf("%d\n",Query1(rt,1,N,l,r).num[F[ED^Query2(rt,1,N,1,l-1)]]);
    }
    if(t==2)
    {
      int l=Read(),r=Read(),id=Read();
      Change(rt,1,N,l,r,id);
    }
  }
  return 0;
}
/*
2 3
0 0 1
1 1 0
7 4
1 1 7
0 2 3
0 3 4
1 1 7
*/
View Code

 

 

2017.4.21

[Thu Summer Camp 2015]异或运算

很搞笑的一道题 发现m比较多 所以用m建可持久化Trie树(建法跟线段树差不多) 然后l到r区间 枚举答案来跑

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int Maxn=300010;
int X[Maxn],Y[Maxn]; int lc[Maxn*50],rc[Maxn*50],C[Maxn*50]; int N,M; int rt[Maxn],tot;
void Link(int &u,int L,int R,int k)
{
  if(!u) u=++tot; C[u]++;
  if(L==R) return ;
  int mid=((LL)L+R+1)/2;
  if(k<mid) Link(lc[u],L,mid-1,k);
  else Link(rc[u],mid,R,k);
 
}
 
void Merge(int &u1,int u2)
{
  if(!u2) return ;
  if(!u1){u1=u2; return ;}
  C[u1]+=C[u2];
  Merge(lc[u1],lc[u2]);
  Merge(rc[u1],rc[u2]);
}
 
int bit[Maxn]; int u1[Maxn],u2[Maxn];
int Query(int L,int R,int k,int p)
{
  if(p==-1) return 0;
  int C1=0;
  for(int i=L;i<=R;i++)
  {
    if(X[i]&bit[p]) C1+=C[lc[u2[i]]]-C[lc[u1[i]]];
    else C1+=C[rc[u2[i]]]-C[rc[u1[i]]];
  }
  if(k<=C1)
  {
    for(int i=L;i<=R;i++)
    {
      if(X[i]&bit[p]) u2[i]=lc[u2[i]],u1[i]=lc[u1[i]];
      else u2[i]=rc[u2[i]],u1[i]=rc[u1[i]];
    }
    return Query(L,R,k,p-1)+(1<<p);
  }
  else
  {
    for(int i=L;i<=R;i++)
    {
      if(X[i]&bit[p]) u2[i]=rc[u2[i]],u1[i]=rc[u1[i]];
      else u2[i]=lc[u2[i]],u1[i]=lc[u1[i]];
    }
    return Query(L,R,k-C1,p-1);
  }
}
 
int main()
{
  scanf("%d%d",&N,&M);
  tot=0; memset(C,0,sizeof(C)); memset(rt,0,sizeof(rt));
  for(int i=1;i<=N;i++) scanf("%d",&X[i]);
 
  for(int i=0;i<=30;i++) bit[i]=(1<<i);
 
  for(int i=1;i<=M;i++)
  {
    scanf("%d",&Y[i]);
    Link(rt[i],1,INT_MAX,Y[i]);
    Merge(rt[i],rt[i-1]);
  }
  int p; scanf("%d",&p);
  for(int i=1;i<=p;i++)
  {
    int u,d,l,r,k; scanf("%d%d%d%d%d",&u,&d,&l,&r,&k);
    for(int j=u;j<=d;j++) u1[j]=rt[l-1],u2[j]=rt[r];
    printf("%d\n",Query(u,d,k,30));
  }
  return 0;
}
View Code

 

[Thu Summer Camp 2015]解密运算

表示这道题我也很迷 看了题解大概是末尾的第几个对应的开头 和开头的第几个对应末尾是相同的性质?

画图理解一下吧 真的没办法说

#include <bits/stdc++.h>
using namespace std;
struct node{int x,t;}Q[200010];
bool Cmp(const node &x,const node &y){if(x.x!=y.x) return x.x<y.x; else return x.t<y.t;}
int main()
{
  int N,M; scanf("%d%d",&N,&M); for(int i=0;i<=N;i++) scanf("%d",&Q[i].x),Q[i].t=i;
  sort(Q,Q+N+1,Cmp); int nx=Q[0].t;
  for(int i=1;i<=N;i++)
  {
    printf("%d ",Q[nx].x);
    nx=Q[nx].t;
  }
  return 0;
}
View Code

 

[Thu Summer Camp 2015]平方运算

这道题好神啊 首先转化模型那是必须的 转化成在图上走有环有链 然后发现环长lcm不超过60 于是就可以维护了

每个点用一个vector把一个环给塞进去 对于两个环 就可以合并 因为只需要统计答案 所以我们可以这样合并

对着代码理解一些吧..

#include <bits/stdc++.h>
using namespace std;
const int Maxn=100010;
int N,M,P; int A[Maxn];
 
int next[Maxn]; bool incir[Maxn]; bool V[Maxn];
stack<int>s;
 
int rt,tot; int lc[Maxn*4],rc[Maxn*4],S[Maxn*4],ST[Maxn*4],lazy[Maxn*4]; vector<int>G[Maxn*4]; bool Cir[Maxn*4];
 
inline int gcd(int x,int y){return (y==0)?x:gcd(y,x%y);}
 
void init(int u,int num)
{
  if(incir[num])
  {
    Cir[u]=1; S[u]=num; G[u].clear();
    G[u].push_back(num); ST[u]=0;
    int t=next[num];
    while(t!=num)
    {
      G[u].push_back(t);
      t=next[t];
    }
  }
  else S[u]=num;
}
 
void update(int u)
{
  Cir[u]=Cir[lc[u]]&Cir[rc[u]];
  S[u]=S[lc[u]]+S[rc[u]];
  if(Cir[u])
  {
    int k=G[lc[u]].size()*G[rc[u]].size()/gcd(G[lc[u]].size(),G[rc[u]].size());
    int tx=ST[lc[u]]; int ty=ST[rc[u]]; G[u].clear();
    for(int i=0;i<k;i++)
    {
      G[u].push_back(G[lc[u]][tx]+G[rc[u]][ty]); ++tx; ++ty;
      tx=(tx%G[lc[u]].size()); ty=(ty%G[rc[u]].size());
    }ST[u]=0;
    S[u]=G[u][ST[u]];
  }
}
 
void Push_down(int u)
{
  if(lazy[u])
  {
    ST[lc[u]]=(ST[lc[u]]+lazy[u])%G[lc[u]].size();
    ST[rc[u]]=(ST[rc[u]]+lazy[u])%G[rc[u]].size();
    lazy[lc[u]]+=lazy[u]; lazy[rc[u]]+=lazy[u];
    S[lc[u]]=G[lc[u]][ST[lc[u]]];
    S[rc[u]]=G[rc[u]][ST[rc[u]]];
 
    lazy[u]=0;
  }
}
 
void Link(int &u,int L,int R,int k)
{
  if(!u) u=++tot;
  if(L==R){init(u,A[L]); return ;}
  int mid=(L+R)>>1;
  if(k<=mid) Link(lc[u],L,mid,k);
  else Link(rc[u],mid+1,R,k);
  update(u);
}
 
void Modify(int u,int L,int R,int l,int r)
{
  if(L==l&&R==r&&Cir[u])
  {
    ST[u]=(ST[u]+1)%G[u].size(); S[u]=G[u][ST[u]];
    lazy[u]++;
    return ;
  }
  if(L==R){init(u,next[S[u]]); return ;}
  Push_down(u);
  int mid=(L+R)>>1;
  if(r<=mid) Modify(lc[u],L,mid,l,r);
  else if(l>mid) Modify(rc[u],mid+1,R,l,r);
  else
  {
    Modify(lc[u],L,mid,l,mid);
    Modify(rc[u],mid+1,R,mid+1,r);
  }
  update(u);
}
 
int Query(int u,int L,int R,int l,int r)
{
  if(L==l&&R==r) return S[u];
  int mid=(L+R)>>1;
  Push_down(u);
  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 Query(lc[u],L,mid,l,mid)+Query(rc[u],mid+1,R,mid+1,r);
}
 
int main()
{
  scanf("%d%d%d",&N,&M,&P);
 
  for(int i=1;i<=P;i++) next[i]=i*i%P; memset(V,0,sizeof(V));
 
  for(int i=0;i<P;i++)
  {
    int now=i;
    while(V[now]==0){V[now]=1; s.push(now); now=next[now];}
    int j=s.top(); int cnt=0;
    do
    {
      j=s.top(); incir[j]=1; V[j]=0;cnt++;  s.pop();
    }while(j!=now);
    while(!s.empty()) V[s.top()]=0,s.pop();
  }
 
  rt=tot=0;
  for(int i=1;i<=N;i++)
    {scanf("%d",&A[i]); Link(rt,1,N,i);}
 
  for(int i=1;i<=M;i++)
  {
    int opt,l,r; scanf("%d%d%d",&opt,&l,&r);
    if(opt==0) Modify(rt,1,N,l,r);
    else printf("%d\n",Query(rt,1,N,l,r));
  }
 
  return 0;
}
 
/*
3 3 11
1 2 3
1 1 3
0 1 3
1 1 3
*/
View Code

 

踩气球

MlogM个区间定标 然后用D数组记录剩下点标数量 为0就统计答案

#include <bits/stdc++.h>
using namespace std;
const int Maxn=100010;
int N,M; int A[Maxn]; int lc[Maxn*4],rc[Maxn*4],C[Maxn*4],D[Maxn*4],tot,rt; vector<int>V[Maxn*4];
void Link(int &u,int L,int R,int k,int c)
{
  if(!u) u=++tot;
  if(L==R)
  {
    C[u]+=c;
    if(C[u]==0)
    {
      for(int i=0;i<V[u].size();i++)
      {
        int y=V[u][i];
        D[V[u][i]]--; if(D[V[u][i]]==0) tot++;
      }
      V[u].clear();
    }
    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);
  C[u]=C[lc[u]]+C[rc[u]];
  if(C[u]==0)
  {
    for(int i=0;i<V[u].size();i++){D[V[u][i]]--; if(D[V[u][i]]==0) tot++;}
    V[u].clear();
  }
}
 
void Query(int u,int L,int R,int l,int r,int id)
{
  if(L==l&&R==r){V[u].push_back(id); D[id]++; return ;}
  int mid=(L+R)>>1;
  if(r<=mid) Query(lc[u],L,mid,l,r,id);
  else if(l>mid) Query(rc[u],mid+1,R,l,r,id);
  else
  {
    Query(lc[u],L,mid,l,mid,id);
    Query(rc[u],mid+1,R,mid+1,r,id);
  }
}
 
int main()
{
  scanf("%d%d",&N,&M);
  tot=rt=0; for(int i=1;i<=N;i++) scanf("%d",&A[i]),Link(rt,1,N,i,A[i]);
  memset(D,0,sizeof(D));
  for(int i=1;i<=M;i++)
  {
    int l,r; scanf("%d%d",&l,&r);
    Query(rt,1,N,l,r,i);
  }int K; scanf("%d",&K); int lastans=0; tot=0;
  for(int i=1;i<=K;i++)
  {
    int x; scanf("%d",&x); x=(x+lastans-1)%N+1;
    Link(rt,1,N,x,-1); printf("%d\n",tot); lastans=tot;
  }
  return 0;
}
 
/*
3 3 11
1 2 3
1 1 3
0 1 3
1 1 3
*/
View Code

 

 2017.4.22

抵制克苏恩

期望题 记住打脸的时候还要乘上之前的概率

#include <bits/stdc++.h>
using namespace std;
const int Maxn=110;
int T,K,A,B,C; double F[Maxn][8][8][8]; double P[Maxn][8][8][8];
int main()
{
  scanf("%d",&T);
  while(T--)
  {
    scanf("%d%d%d%d",&K,&A,&B,&C);
    memset(P,0.0,sizeof(P)); memset(F,0,sizeof(F)); P[0][A][B][C]=1.0;
    for(int i=0;i<K;i++)
    {
    for(int a=0;a<=7;a++)
      for(int b=0;b<=7;b++)
        for(int c=0;c<=7;c++)
          if(P[i][a][b][c])
          {
            F[i+1][a][b][c]+=(F[i][a][b][c]+(1.0*P[i][a][b][c]))*(1.0/(a+b+c+1));
            P[i+1][a][b][c]+=P[i][a][b][c]*(1.0/(a+b+c+1));
            if(a) F[i+1][a-1][b][c]+=(F[i][a][b][c])*(1.0/(a+b+c+1)*a),P[i+1][a-1][b][c]+=P[i][a][b][c]*(1.0/(a+b+c+1)*a);
            if(a+b+c+1<=7)
            {
              if(b) F[i+1][a+1][b-1][c+1]+=(F[i][a][b][c])*(1.0/(a+b+c+1)*b),P[i+1][a+1][b-1][c+1]+=P[i][a][b][c]*(1.0/(a+b+c+1)*b);
              if(c) F[i+1][a][b+1][c]+=(F[i][a][b][c])*(1.0/(a+b+c+1)*c),P[i+1][a][b+1][c]+=P[i][a][b][c]*(1.0/(a+b+c+1)*c);
            }
            else
            {
              if(b) F[i+1][a+1][b-1][c]+=(F[i][a][b][c])*(1.0/(a+b+c+1)*b),P[i+1][a+1][b-1][c]+=P[i][a][b][c]*(1.0/(a+b+c+1)*b);
              if(c) F[i+1][a][b+1][c-1]+=(F[i][a][b][c])*(1.0/(a+b+c+1)*c),P[i+1][a][b+1][c-1]+=P[i][a][b][c]*(1.0/(a+b+c+1)*c);
            }
          }
    }
    double ans=0.0;
    for(int a=0;a<=7;a++)
      for(int b=0;b<=7;b++)
        for(int c=0;c<=7;c++)
          if(P[K][a][b][c])
            ans+=F[K][a][b][c];
    printf("%.2lf\n",ans);
  }
  return 0;
}
View Code 

 

Rhl的游戏

好题 很推崇这题

首先一个个按格子来建方程显然会超时 我们考虑将第一行按和不按设为$xi$ 然后第一行建出来之后 后面都建出来了 后面的一行建出来的方式是通过使上一行合法

然后对于最后一行 我们新建一行在最下面表示一定要使最后一行合法 然后的话 最后一行肯定不能按是不是 所以所有的项异或起来一定等于0作为条件

那些坏的点也是一样

#include <bits/stdc++.h>
using namespace std;
const int Maxn=300;
bitset<Maxn>F[Maxn][Maxn],A[Maxn*2]; int len; int id[Maxn]; int N,M,K; char st[Maxn][Maxn];
void Gauss()
{
  memset(id,0,sizeof(id)); int k=1;
  for(int i=1;i<=M;i++)
  {
    if(A[k][i]==0)
    {
      for(int j=k+1;j<=len;j++)
        if(A[j][i]){swap(A[j],A[k]); break;}
    }
    if(A[k][i])
    {
      for(int j=k+1;j<=len;j++) if(A[j][i]) A[j]^=A[k];
      id[i]=k++;
    }
  }
}
 
int ans[Maxn];
int main()
{
  int Tcase; scanf("%d",&Tcase);
  for(int T=1;T<=Tcase;T++)
  {
    scanf("%d%d%d",&N,&M,&K);
    for(int i=1;i<=N;i++) for(int j=1;j<=M;j++) scanf("\n%c",&st[i][j]);
    for(int i=1;i<=N;i++) for(int j=1;j<=M;j++) F[i][j].reset();
    for(int j=1;j<=M;j++) F[1][j][j]=1;
    for(int i=2;i<=N;i++)
      for(int j=1;j<=M;j++)
      {
        if(i>2) F[i][j]^=F[i-2][j];
        if(j>1) F[i][j]^=F[i-1][j-1]; if(j<M) F[i][j]^=F[i-1][j+1]; F[i][j]^=F[i-1][j];
        F[i][j][M+1]=F[i][j][M+1]^(st[i-1][j]=='B');
      }
 
    len=0; A[len].reset();
    for(int i=1;i<=M;i++)
    {
      len++; A[len].reset();
      if(N>1) A[len]^=F[N-1][i]; if(i>1) A[len]^=F[N][i-1]; if(i<M) A[len]^=F[N][i+1]; A[len]^=F[N][i];
      A[len][M+1]=A[len][M+1]^(st[N][i]=='B');
    }
    for(int i=1;i<=K;i++)
    {
      int x,y; scanf("%d%d",&x,&y);
      len++; A[len].reset(); A[len]=F[x][y];
    }
     
    Gauss(); memset(ans,0,sizeof(ans));
 
    for(int i=M;i>=1;i--)
    {
      if (id[i])
      {
        int k=id[i];
        ans[i]=A[k][M+1];
        for(int j=M;j>i;j--) if(A[k][j]) ans[i]^=ans[j];
      }
    }
 
    bool bk=1;
    for(int i=1;i<=len;i++)
    {
      int s=0;
      for(int j=1;j<=M;j++) if(A[i][j]) s^=ans[j];
      if(s!=A[i][M+1]){bk=0; break;} 
    }
 
    printf("Case #%d:\n",T);
    if(bk) printf("YES\n");
    else printf("NO\n");
  }
  return 0;
}
View Code

2017.4.23

昨天晚上调节情绪 没有多做题 然后看了场以前的电影 心里满是正能量 是的!

[Baltic2013]tracks

首先这道题 找出突破口 什么才会增加数量 画画图可知 只有包围的情况才会+1

然后就用流水搞啊搞啊 发现好像是好似是有向图搞 然后搞了很久发现wa了

然后搞着搞着发现用无向图最长路就好了 然后也是搞啊搞 不同的联通块连了很多条相同的边

发现hash也很慢 然后堆优化也很慢 所以用spfa搞发现快一点

然后搞完之后发现最大的点跑了4.8s wph只要跑3.6s... T了

自己看着时间YY了一下 大概优化到4.2s就可以过

然后把多建的边整理一下..发现优化到4.5s

发现是spfa  加了个双向队列 然后能不清空就不清空 加上循环展开 手写队列 加法提前 建边的时候不建出起点....

然后搞啊搞搞到了4.1-4.2s 感觉很可行 然后一交

39s卡时AC !!!!

事实说明:有梦想了不起坚持才是硬道理

#include <bits/stdc++.h>
using namespace std;
const int Maxn=4010;
int N,M; char str[Maxn][Maxn];
int dx[5]={1,0,-1,0}; int dy[5]={0,1,0,-1};
struct node
{
  int x,y;
  node(){}
  node(int _x,int _y){x=_x; y=_y;}
};
node Q[Maxn*Maxn]; int head,tail; int num[Maxn][Maxn],cnt;
  
struct EDGE{int y,next;}edge[Maxn*Maxn*2]; int len,first[Maxn*Maxn];
  
int q[Maxn*Maxn*2]; int F[Maxn*Maxn]; bool bo[Maxn*Maxn];
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]);
  
  int maxx=0; cnt=0;
  for(int i=1;i<=N;i++) for(int j=1;j<=M;j++) if(str[i][j]!='.') if(num[i][j]==0)
  {
    ++cnt; num[i][j]=cnt; head=tail=1; Q[1]=node(i,j);
    while(head<=tail)
    {
      node x=Q[head]; int xx,yy;
        
      xx=x.x+dx[0]; yy=x.y+dy[0];
      if((str[xx][yy]==str[x.x][x.y])&&(num[xx][yy]==0)) num[xx][yy]=cnt,Q[++tail]=node(xx,yy);
  
      xx=x.x+dx[1]; yy=x.y+dy[1];
      if((str[xx][yy]==str[x.x][x.y])&&(num[xx][yy]==0)) num[xx][yy]=cnt,Q[++tail]=node(xx,yy);
  
      xx=x.x+dx[2]; yy=x.y+dy[2];
      if((str[xx][yy]==str[x.x][x.y])&&(num[xx][yy]==0)) num[xx][yy]=cnt,Q[++tail]=node(xx,yy);
  
      xx=x.x+dx[3]; yy=x.y+dy[3];
      if((str[xx][yy]==str[x.x][x.y])&&(num[xx][yy]==0)) num[xx][yy]=cnt,Q[++tail]=node(xx,yy);
  
      ++head;
    }
  }
  
  len=0;
  for(int i=1;i<=N;i++) for(int j=1;j<=M;j++) if(num[i][j]!=0)
  {
    int xx,yy;
    xx=i+dx[0]; yy=j+dy[0];
    if((num[xx][yy]!=0)&&(num[xx][yy]!=num[i][j]))
    {
      ++len; edge[len].y=num[i][j]; edge[len].next=first[num[xx][yy]]; first[num[xx][yy]]=len;
      ++len; edge[len].y=num[xx][yy]; edge[len].next=first[num[i][j]]; first[num[i][j]]=len;
    }
  
    xx=i+dx[1]; yy=j+dy[1];
    if((num[xx][yy]!=0)&&(num[xx][yy]!=num[i][j]))
    {
      ++len; edge[len].y=num[i][j]; edge[len].next=first[num[xx][yy]]; first[num[xx][yy]]=len;
      ++len; edge[len].y=num[xx][yy]; edge[len].next=first[num[i][j]]; first[num[i][j]]=len;
    }
  }
  
  head=tail=Maxn*Maxn; q[head]=1; memset(F,63,sizeof(F)); F[1]=1; bo[1]=1;
  while(head<=tail)
  {
    int x=q[head]; bo[x]=0; ++head;
    for(int k=first[x];k;k=edge[k].next)
    {
      int y=edge[k].y;
      if(F[y]>F[x]+1)
      {
        F[y]=F[x]+1;
        if(!bo[y])
        {
          bo[y]=1;
          if(F[y]<F[q[head]]) q[--head]=y;
          else q[++tail]=y;
        }
      }
    }
  }
  
  for(int i=1;i<=cnt;i++) maxx=max(maxx,F[i]);
  return printf("%d\n",maxx),0;
}
View Code

 

Lmc的游戏

一开始感觉这题不可做 二分一下吧

然后感觉好像又不是很用 感觉二分也是为了构造 那么可不可以直接贪心构造出最优解

答案是可以的 首先考虑尽量大 那么就是从大到小尽量少放 发现max只有一个子树有用 min全都要填满才行

尽量小反过来同理

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int Maxn=200010;
struct node{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 N; int dep[Maxn]; int F[Maxn]; int ans=0;
void Dfs(int x)
{
  int tot=0; for(int k=first[x];k!=-1;k=edge[k].next) tot++;
  if(tot==0){F[x]=1; ans++; return ;}
  if(dep[x]&1) F[x]=INT_MAX; else F[x]=0;
  for(int k=first[x];k!=-1;k=edge[k].next)
  {
    int y=edge[k].y; dep[y]=dep[x]+1;
    if(dep[x]&1){Dfs(y); F[x]=min(F[x],F[y]);}
    else{Dfs(y); F[x]+=F[y];}
  }
}
int main()
{
  scanf("%d",&N); LL root=N*(N+1)/2; 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); root-=y;}
  memset(dep,0,sizeof(dep)); int rt=(int)root;
  dep[root]=1; ans=0; Dfs(root); printf("%d ",ans-F[root]+1);
 
  memset(dep,0,sizeof(dep)); rt=(int)root;
  dep[root]=0; ans=0; Dfs(root); printf("%d\n",F[root]);
  return 0;
}
/*
5
1 2
1 3
2 4
2 5
*/
View Code

 

2017.4.24

比赛天 

 

2017.4.25

[Apio2015]巴厘岛的雕塑

好搞笑的一道题 首先自己很容易想到$F[i][j]=\min (F[i-1][k]|(sum[j]-sum[k]))$

感觉很对 呵呵

其实是错误的 局部最小值不代表整体的最小值

然后膜题解 按位dp $F[i][j]$表示前i个数分成j组 且满足之前的位数 这个位是否可以能填0

然后转移就是

if(F[k][j-1]&&((((Sum[i]-Sum[k])|ans)>>p)==(ans>>p))&&(((Sum[i]-Sum[k])&bit[p])==0))
              F[i][j]=1;

ans就是之前位的答案 显然要小于之前位的答案

对于a=1 的 就可以优化一维 同理$G[i]$表示前i个数合法至少要用多少组

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int Maxn=2010;
int N,A,B; LL Y[Maxn],Sum[Maxn]; bool F[110][110]; LL bit[Maxn]; LL G[Maxn];
int main()
{
  scanf("%d%d%d",&N,&A,&B);
  for(int i=1;i<=N;i++) scanf("%lld",&Y[i]),Sum[i]=Sum[i-1]+Y[i];
  for(int i=0;i<=45;i++) bit[i]=((LL)(1)<<i);
  if(A==1)
  {
    LL ans=0;
    for(int p=45;p>=0;p--)
    {
      memset(G,63,sizeof(G)); G[0]=0;
      for(int i=1;i<=N;i++)
      {
        for(int k=0;k<i;k++)
          if(((((Sum[i]-Sum[k])|ans)>>p)==(ans>>p))&&(((Sum[i]-Sum[k])&bit[p])==0))
            G[i]=min(G[k]+1,G[i]);
      }
      if(G[N]>B) ans|=bit[p];
    }
    printf("%lld\n",ans);
  }
  else
  {
    LL ans=0;
    for(int p=45;p>=0;p--)
    {
      memset(F,0,sizeof(F)); F[0][0]=1;
      for(int i=1;i<=N;i++)
      {
        for(int j=1;j<=B;j++)
        {
          for(int k=0;k<i;k++)
            if(F[k][j-1]&&((((Sum[i]-Sum[k])|ans)>>p)==(ans>>p))&&(((Sum[i]-Sum[k])&bit[p])==0))
              F[i][j]=1;
        }
      }
      bool bk=1;
      for(int j=A;j<=B;j++) if(F[N][j]){bk=0; break;}
      if(bk) ans|=bit[p];
    }
    printf("%lld\n",ans);
  }
  return 0;
}
View Code

 

 

[Cqoi2017]小Q的表格

好题!

首先这个表格有两个式子 第一个是满足表格对称性 第二个是一个关于欧几里得的东西

我们看第二个$b×F(a,a+b)=(a+b)*F(a,b)$ 可以变成 $F(a,a+b)/(a+b)=F(a,b)/b$ 然后很容易发现公约数相同的 表格里面的数是和横纵坐标成倍数关系

然后我们推公式 很明显和公约数有关 构成一个公约数的一个最小数对就是自己本身

所以我们很快推出:

$\sum\limits_{i=1}^{k} \sum\limits_{j=1}^{k} F(gcd(i,j) ) \frac{ij}{gcd(i,j)^2} $

$\sum\limits_{d} \sum\limits_{i=1}^{\lfloor \frac{k}{d} \rfloor} \sum\limits_{j=1}^{\lfloor \frac{k}{d} \rfloor} ij \sum\limits_{D|(i,j)} \mu(D)$

发现后面的一部分可以线性筛出来 $\sum\limits_{i=1}^{\lfloor \frac{k}{d} \rfloor} \sum\limits_{j=1}^{\lfloor \frac{k}{d} \rfloor} ij \sum\limits_{D|(i,j)} \mu(D)$

这个东西相当于$\Sigma ij [(i,j)=1]$

发现对于每一个$i$ 与$i$互质的数$j$ 的和 就是 $\frac{n \phi(n)}{2}$

假设后面部分对于$\frac{k}{d}$答案为$G(\frac{k}{d})$ 我们就有

$\sum\limits_{d} F(d)*G(\frac{k}{d})$

首先第一个$F(d)$是待修改的 第二个 $F(d)$要求前缀和

我们可以想到用树状数组 但是这样并不能均摊复杂度

改的时候是$O(MlogN)$ 询问是$O(M logN \sqrt N)$

考虑在分块的时候 询问比较多 我们可以考虑再用分块来优化询问 将询问改成$O(1)$ 改的复杂度改成 $O(\sqrt N)$

然后就可以AC了 总的复杂度为

改的时候是$O(M\sqrt N)$ 询问是$O(M \sqrt N)$

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL Maxn=4*1e6+10;
const LL Mod=1e9+7; LL M,N; LL prime[Maxn],phi[Maxn],G[Maxn],pri; bool V[Maxn];
void Pre()
{
  memset(V,1,sizeof(V)); V[1]=0; phi[1]=1; pri=0;
  for(LL i=2;i<=N;i++)
  {
    if(V[i]) prime[++pri]=i,phi[i]=i-1;
    for(LL j=1;(j<=pri)&&(i*prime[j]<=N);j++)
    {
      V[i*prime[j]]=0;
      if(i%prime[j]==0){phi[i*prime[j]]=phi[i]*prime[j]; break;}
      else phi[i*prime[j]]=phi[i]*phi[prime[j]];
    }
  }

  for(LL i=1;i<=N;i++) G[i]=(G[i-1]+i*i%Mod*phi[i]%Mod)%Mod;
}

LL gcd(LL x,LL y){return (y==0)?x:gcd(y,x%y);}

LL belong[Maxn],Sum1[Maxn],A[Maxn],Sum2[Maxn],last[Maxn]; LL q;

void Change(LL x,LL k)
{
  for(LL i=belong[x];i<=belong[N];i++) Sum2[i]=(Sum2[i]-(A[x]-k)+Mod)%Mod; A[x]=k;
  for(LL i=x;belong[i]==belong[x];i++)
  {
    if(belong[i-1]==belong[i]) Sum1[i]=Sum1[i-1];
    else Sum1[i]=0;
    Sum1[i]=(Sum1[i]+A[i])%Mod;
  }
}

LL Query(LL l,LL r)
{
  LL ans=0;
  if(belong[l]+1<belong[r]) ans=(ans+Sum2[belong[r]-1]-Sum2[belong[l]])%Mod;
  if(belong[l]==belong[r]) ans=(ans+Sum1[r]-Sum1[l]+A[l]+Mod)%Mod;
  else ans=(ans+Sum1[last[belong[l]]]-Sum1[l]+A[l]+Sum1[r]+Mod)%Mod;
  return ans;
}

int main()
{
  scanf("%lld%lld",&M,&N); Pre();

  q=(LL)ceil(sqrt(N));
  for(LL i=1;i<=N;i++){belong[i]=(i-1)/q; last[belong[i]]=i;}
  for(LL i=1;i<=N;i++)
  {
    A[i]=i*i%Mod; if(belong[i]==belong[i-1]) Sum1[i]=(Sum1[i-1]+A[i])%Mod;
    else Sum1[i]=A[i];
    Sum2[belong[i]]=(Sum2[belong[i]]+A[i])%Mod;
  }
  for(LL i=1;i<=belong[N];i++) Sum2[i]=(Sum2[i]+Sum2[i-1])%Mod; 

  while(M--)
  {
    LL a,b,x,k; scanf("%lld%lld%lld%lld",&a,&b,&x,&k);
    LL d=gcd(a,b); Change(d,x/(a/d)/(b/d));
    LL nx,ans=0; for(d=1;d<=k;d=nx+1)
    {
      nx=min(k,(k/(k/d)));
      ans=(ans+(Query(d,nx))*G[k/d]%Mod)%Mod;
    }
    printf("%lld\n",(ans+Mod)%Mod);
  }
  return 0;
}
/*
3 3
1 1 1 2
2 2 4 3
1 2 4 2
*/
View Code 

 

2017.4.26

[Sdoi2017]新生舞会

下午脑残切了道算是模板题的题吧....... 0/1分数规划 我们有:

$\frac{\Sigma A}{\Sigma B}>k$

$\Sigma A - k\Sigma B>0$

很明显这是要最大费用 我们把上面不等式取反做最小费用即可

PS:要是GD有这样的签到题就好了囧

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <climits>
using namespace std;
const int Maxn=110;
const double eps=1e-9;
int N; double A[Maxn][Maxn],B[Maxn][Maxn];
struct node{int x,y,next,c,other; double d;}edge[Maxn*Maxn*10]; int len,first[Maxn*2];
void ins(int x,int y,double d,int c)
{
  len++; int k1=len; edge[len].x=x; edge[len].y=y; edge[len].d=d; 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].d=-d; edge[len].c=0; edge[len].next=first[y]; first[y]=len;
  edge[k1].other=k2; edge[k2].other=k1;
}

int ST,ED;
void Rebuild(double k)
{
  ST=N*2+1; ED=ST+1; len=0; memset(first,-1,sizeof(first));
  for(int i=1;i<=N;i++) ins(ST,i,0,1),ins(i+N,ED,0,1);
  for(int i=1;i<=N;i++) for(int j=1;j<=N;j++)
    ins(i,j+N,k*B[i][j]-A[i][j],1);
}

int pos[Maxn*2],pre[Maxn*2]; double D[Maxn*2]; bool V[Maxn*2]; queue<int>Q;
bool Spfa()
{
  memset(V,0,sizeof(V)); V[ST]=1;
  while(!Q.empty()) Q.pop(); Q.push(ST);
  for(int i=1;i<=ED;i++) D[i]=99999999.0; D[ST]=0.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(D[y]>D[x]+edge[k].d&&edge[k].c)
      {
        D[y]=D[x]+edge[k].d; pos[y]=x; pre[y]=k;
        if(!V[y])
        {
          V[y]=1;
          Q.push(y);
        }
      }
    }
    Q.pop(); V[x]=0;
  }
  return D[ED]<99999999.0;
}
bool Judge()
{
  double ans=0; int delta=0;
  while(Spfa())
  {
    int minf=INT_MAX;
    for(int i=ED;i!=ST;i=pos[i]) minf=min(minf,edge[pre[i]].c);
    for(int i=ED;i!=ST;i=pos[i])
    {
      ans+=edge[pre[i]].d*minf;
      edge[pre[i]].c-=minf;
      edge[edge[pre[i]].other].c+=minf;
    }
  }
  return ans<0;
}

int main()
{
  scanf("%d",&N); for(int i=1;i<=N;i++) for(int j=1;j<=N;j++) scanf("%lf",&A[i][j]);
  for(int i=1;i<=N;i++) for(int j=1;j<=N;j++) scanf("%lf",&B[i][j]);
  double L=0; double R=1e7; double ans=0.0;
  while(R-L>=eps)
  {
    double mid=(L+R)/2.0;
    Rebuild(mid);
    if(Judge()) L=mid,ans=mid;
    else R=mid;
  }
  return printf("%.6lf\n",ans),0;
}
/*
3
19 17 16
25 24 23
35 36 31
9 5 6
3 4 2
7 8 9
*/
View Code 

 

[Sdoi2017]硬币游戏

好神啊 当作复习了exkmp....

首先AC自动机每次跳fail 建出无向图有40 ftps

对于一个没有到达一个满足的串$N$ 设概率为$P[N]$ 对于两个串$A=TTH$ $B=HTT$ 以$A$串结尾的概率为$P[A]$ 以$B$串结尾的概率为$P[B]$

有$N+TTH$一定到达A 可能先到达B

那么有

$N+TTH=A + BH + BTH$

$\frac{1}{2^3}P[N]=P[A]+\frac{3}{4}P[B]$

列出一系列方程 最后一个 就是 所有串结尾的概率加起来等于1

注意高斯消元的精度问题即可

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdlib>
#include <cmath>
  
using namespace std;
const int Maxn=310;
int N,M;
struct node
{
  char str[Maxn]; int extend[Maxn];
  void Exkmp()
  {
    extend[1]=M; for(int i=1;i<=M;i++) if(str[i]!=str[i+1]){extend[2]=i-1; break;}
    int id=2,mx=2+extend[2]-1;
    for(int i=3;i<=M;i++)
    {
      if(i<=mx) extend[i]=min(extend[i-id+1],mx-i+1);
      while(str[i+extend[i]]==str[extend[i]+1]) extend[i]++;
      if(i+extend[i]-1>mx){mx=i+extend[i]-1; id=i;}
    }
  }
}P[Maxn];
long double A[Maxn][Maxn]; long double p[Maxn]; long double ans[Maxn]; int id[Maxn];
int extend[Maxn];
long double Count(node x,node y)
{
  memset(extend,0,sizeof(extend));
  for(int i=1;i<=M+1;i++)
  {
    if(i==M+1){extend[1]=M; break;}
    if(x.str[i]!=y.str[i]){extend[1]=i-1; break;}
  }
  int id=1,mx=extend[1];
  for(int i=2;i<=M;i++)
  {
    if(i<=mx) extend[i]=min(x.extend[i-id+1],mx-i+1);
    while(y.str[i+extend[i]]==x.str[extend[i]+1]) extend[i]++;
    if(i+extend[i]-1>mx){mx=i+extend[i]-1; id=i;}
  }
  
  long double s=0.0;
  for(int i=1;i<=M;i++) if(i+extend[i]-1==M) s+=p[i-1];
  return s;
}
  
void Gauss()
{
  int k=1; memset(id,0,sizeof(id));
  for(int i=1;i<=N+1;i++)
  {
    for(int j=k+1;j<=N;j++)
      if(fabs(A[j][i])>fabs(A[k][i]))
      {
        for(int p=1;p<=N+2;p++) swap(A[j][p],A[k][p]);
      }
    id[i]=k;
    for(int j=k+1;j<=N+1;j++)
    {
      long double kk=A[j][i]/A[k][i];
      for(int p=i;p<=N+2;p++)
        A[j][p]=A[j][p]-A[k][p]*kk;
    }k++;
  }
  
  for(int i=N+1;i>=1;i--)
  {
    int k=id[i];
    ans[i]=A[k][N+2];
    for(int j=N+1;j>i;j--) ans[i]=ans[i]-ans[j]*A[k][j];
    ans[i]=ans[i]/A[k][i];
  }
}
  
int main()
{
  scanf("%d%d",&N,&M);
  for(int i=1;i<=N;i++){scanf("%s",P[i].str+1); P[i].Exkmp();}
    
  p[0]=1.0; for(int i=1;i<=M;i++) p[i]=p[i-1]*0.5;
  
  for(int i=1;i<=N;i++)
  {
    for(int j=1;j<=N;j++)
    {
      A[i][j]=Count(P[i],P[j]);
    }
    A[i][N+1]=-p[M]; A[i][N+2]=0.0;
  }
  
  for(int i=1;i<=N;i++) A[N+1][i]=1.0; A[N+1][N+2]=1.0;
  
  Gauss();
  for(int i=1;i<=N;i++) printf("%.10lf\n",(double)ans[i]);
  return 0;
}
View Code

 

2017.4.27

[Sdoi2017]树点涂色

好题好题 好劲的题

这道题可以分成 到根路径染色 子树权值最大 询问路径权值

发现有子树找最大的操作 一般都要用到dfs序 可是又有维护染色 这个是最麻烦的 发现如果单单树剖的话 一条路径染色之后会影响很多段dfs序 这样我们很难维护

cys:考虑用LCT 每一条重链都是一种颜色 每次就相当于维护路径上轻链的数目

好劲啊......

然后按照这个思路去推理 路径染色 直接Access就行 然后用一个线段树维护DFS序 路径权值 只要找到LCA搞一下即可

但是有一个问题 在Access的时候 我们怎么维护这个线段树

原来的重链 现在不是重链了 就是多了条轻链 子树要+1

现在的重链 少了条轻链 子树要-1

之前的轻链和现在的轻链 好像不管它事 子树不改变

然后就可以AC了 自认为还挺好写

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdlib>
using namespace std;
const int Maxn=100010;
struct node{int x,y,next;}edge[Maxn*2]; int len,first[Maxn]; int N,M;
void ins(int x,int y){len++; edge[len].x=x; edge[len].y=y; edge[len].next=first[x]; first[x]=len;}
int dfn[Maxn],id; int ST[Maxn],ED[Maxn];
 
int lc[Maxn*4],rc[Maxn*4],C[Maxn*4],lazy[Maxn*4]; int rt,tot=0;
 
void Push_down(int u)
{
  if(lazy[u])
  {
    lazy[lc[u]]+=lazy[u]; lazy[rc[u]]+=lazy[u];
    C[lc[u]]+=lazy[u]; C[rc[u]]+=lazy[u];
    lazy[u]=0;
  }
}
 
void Link(int &u,int L,int R,int l,int r,int c)
{
  if(!u) u=++tot;
  if(L==l&&R==r){lazy[u]+=c; C[u]+=c; return ;}
  Push_down(u);
  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);
  }
  C[u]=max(C[lc[u]],C[rc[u]]);
}
 
int Query(int u,int L,int R,int l,int r)
{
  if(L==l&&R==r) return C[u];
  Push_down(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 max(Query(lc[u],L,mid,l,mid),Query(rc[u],mid+1,R,mid+1,r));
}
 
struct Tree
{
  int fa,son[2];
}tr[Maxn]; int root;
 
bool Is_root(int u){if((tr[tr[u].fa].son[1]==u)||(tr[tr[u].fa].son[0]==u)) return 0; return 1;}
void Rotate(int u)
{
  int y=tr[u].fa; int z=tr[y].fa;
  int a=tr[y].son[1]==u; int b=tr[z].son[1]==y;
  int g=tr[u].son[a^1];
  if(!Is_root(y)) tr[z].son[b]=u; tr[u].fa=z; 
  tr[u].son[a^1]=y; tr[y].fa=u; 
  tr[y].son[a]=g; if(g) tr[g].fa=y; 
}
 
void Splay(int u)
{
  while(!Is_root(u))
  {
    int y=tr[u].fa; int z=tr[y].fa;
    if(Is_root(y)) Rotate(u);
    else
    {
      if((tr[y].son[1]==u)&&(tr[z].son[1]==y)) Rotate(y);
      else Rotate(u); Rotate(u);
    }
  }
}
 
void Access(int u)
{
  int last=0;
  while(u)
  {
    Splay(u);
  
    int y=last; while(tr[y].son[0]) y=tr[y].son[0];
    if(y) Link(rt,1,N,ST[y],ED[y],-1);
 
    y=tr[u].son[1]; while(tr[y].son[0]) y=tr[y].son[0];
    if(y) Link(rt,1,N,ST[y],ED[y],1);
 
    tr[u].son[1]=last;
 
    last=u; u=tr[u].fa;
  }
}
 
int fa[Maxn][25],dep[Maxn];
 
void Dfs(int x,int f)
{
  dfn[x]=++id; ST[x]=id;
  for(int k=first[x];k!=-1;k=edge[k].next)
  {
    int y=edge[k].y;
    if(y!=f)
    {
      tr[y].fa=x; fa[y][0]=x; dep[y]=dep[x]+1;
      Dfs(y,x);
    }
  }
  ED[x]=id; Link(rt,1,N,ST[x],ED[x],1);
}
 
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,&M); 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=tot=0; for(int i=1;i<=N;i++) Link(rt,1,N,i,i,0); dep[1]=1; Dfs(1,0);
  for(int i=1;i<=N;i++) for(int j=1;j<=20;j++) fa[i][j]=fa[fa[i][j-1]][j-1];
 
  for(int i=1;i<=M;i++)
  {
    int opt,x,y; scanf("%d",&opt);
    if(opt==1){scanf("%d",&x); Access(x);}
    else if(opt==2)
    {
      scanf("%d%d",&x,&y); int p=LCA(x,y);
      printf("%d\n",Query(rt,1,N,dfn[x],dfn[x])+Query(rt,1,N,dfn[y],dfn[y])-2*Query(rt,1,N,dfn[p],dfn[p])+1);
    }
    else if(opt==3)
    {
      scanf("%d",&x); printf("%d\n",Query(rt,1,N,ST[x],ED[x]));
    }
  }
  return 0;
}
View Code 

 

[Noi2015]品酒大会

出发的前一天才切品酒..

和差异那道题差不多 但是我当时做差异那道题是用set做的 品酒我用并查集做 对于两个一开始没有合并的集合点对个数就是两个集合的大小的乘积

然后从大到小做 记录集合最大最小值即可

用set做是从小到大做 不推荐

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdlib>
#include <cmath>
#include <climits>
#include <vector>
using namespace std;
typedef long long LL;
const LL Maxn=300010;
int N; char str[Maxn];

int rank[Maxn],sa[Maxn],fir[Maxn],sec[Maxn],A[Maxn],R[Maxn],height[Maxn]; LL Val[Maxn];
void Rsort(int *use,int *in,int *out,int num,int size)
{
  for(int i=1;i<=size;i++) R[i]=0;
  for(int i=1;i<=num;i++) R[use[in[i]]]++;
  for(int i=1;i<=size;i++) R[i]+=R[i-1];
  for(int i=num;i>=1;i--) out[R[use[in[i]]]--]=in[i];
}
void SA()
{
  for(int i=1;i<=N;i++) rank[i]=i;
  Rsort(A,rank,sa,N,300);
  rank[sa[1]]=1; for(int i=2;i<=N;i++) rank[sa[i]]=rank[sa[i-1]]+(A[sa[i]]!=A[sa[i-1]]);
  int p=0; int cnt=1;
  while(cnt<N)
  {
    for(int i=1;i<=N;i++)
    {
      sa[i]=i; fir[i]=rank[i]; sec[i]=((i+(1<<p))<=N)?rank[i+(1<<p)]:0;
    }
    Rsort(sec,sa,rank,N,N);
    Rsort(fir,rank,sa,N,N);
    rank[sa[1]]=1; for(int i=2;i<=N;i++) rank[sa[i]]=rank[sa[i-1]]+((fir[sa[i]]!=fir[sa[i-1]])||(sec[sa[i]]!=sec[sa[i-1]]));
    cnt=rank[sa[N]]; p++;
  }
}
void Height()
{
  int j=0;
  for(int i=1;i<=N;i++)
  {
    if(j>0) j--;
    if(rank[i]!=1)
    {
      int pos=sa[rank[i]-1];
      while(A[pos+j]==A[i+j]) j++;
    }
    height[rank[i]]=j;
  }
}

int fa[Maxn]; LL siz[Maxn]; LL mx[Maxn],mn[Maxn]; int Find(int x){return (x==fa[x])?x:fa[x]=Find(fa[x]);}
vector<int>V[Maxn];

LL ansA[Maxn]; LL ansB[Maxn];
int main()
{
  scanf("%d",&N); for(int i=1;i<=N;i++) scanf("\n%c",&str[i]),A[i]=str[i]-'a'+1;
  for(int i=1;i<=N;i++) scanf("%lld",&Val[i]);
  SA();
  Height();

  for(int i=1;i<=N;i++) fa[i]=i,siz[i]=1,mx[i]=mn[i]=Val[sa[i]]; LL s=0;
  for(int i=2;i<=N;i++) V[height[i]].push_back(i); LL MX=LLONG_MIN;
  for(int r=N-1;r>=0;r--)
  {
    for(int i=0;i<V[r].size();i++)
    {
      int xx=Find(V[r][i]-1); int yy=Find(V[r][i]);
      s+=siz[xx]*siz[yy]; fa[yy]=xx;
      siz[xx]+=siz[yy]; MX=max(MX,max(mx[xx]*mx[yy],mn[xx]*mn[yy]));
      mx[xx]=max(mx[xx],mx[yy]);
      mn[xx]=min(mn[xx],mn[yy]);
    }
    ansA[r]=s; ansB[r]=(s==0)?0:MX;
  }
  for(int r=0;r<N;r++) printf("%lld %lld\n",ansA[r],ansB[r]);
  return 0;
}
View Code 

 

[Hnoi2017]礼物

FFT反串搞一下找最大 不想多说 不会问我

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

using namespace std;
typedef long long LL;
const LL Maxn=50010;
const double pi=acos(-1);
struct node
{
  double r,i;
  node(){}
  node(double _r,double _i){r=_r; i=_i;}
  friend node operator +(const node &x,const node &y){return node(x.r+y.r,x.i+y.i);}
  friend node operator -(const node &x,const node &y){return node(x.r-y.r,x.i-y.i);}
  friend node operator *(const node &x,const node &y){return node(x.r*y.r-x.i*y.i,x.r*y.i+y.r*x.i);}
}A[Maxn*16],B[Maxn*16]; LL S[Maxn];



LL N,M; LL X[Maxn],Y[Maxn]; LL Minx;
LL Solve(LL m)
{
  LL ans=N*m*m;
  for(LL i=1;i<=N;i++) ans+=X[i]*X[i]+Y[i]*Y[i];
  for(LL i=1;i<=N;i++) ans+=2*m*(X[i]-Y[i]);
  return ans;
}


LL R[Maxn*16];

void DFT(node *a,LL n,LL opt)
{
  for(LL i=0;i<n;i++) if(R[i]>i) swap(a[i],a[R[i]]);
  for(LL i=1;i<n;i<<=1)
  {
    node wn=node(cos(pi/i),sin(pi/i*opt));
    for(LL j=0;j<n;j+=(i<<1))
    {
      node w=node(1.0,0.0);
      for(LL k=0;k<i;k++,w=w*wn)
      {
        node x=a[j+k]; node y=a[j+k+i]*w;
        a[j+k]=x+y; a[j+k+i]=x-y;
      }
    }
  }
}

void FFT()
{
  LL n,m,l=0; m=3*N; for(n=1;n<=m;n<<=1) l++;
  for(LL i=0;i<n;i++) R[i]=(R[i>>1]>>1)|((i&1)<<(l-1));
  DFT(A,n,1); DFT(B,n,1);
  for(LL i=0;i<n;i++) A[i]=A[i]*B[i];
  DFT(A,n,-1);
  
  for(LL i=N+2;i<=2*N+1;i++) S[i-N-1]=(LL)((A[i].r/n+0.5));
}

int main()
{
  scanf("%lld%lld",&N,&M); Minx=LLONG_MAX;
  for(LL i=1;i<=N;i++) scanf("%lld",&X[i]);
  for(LL i=1;i<=N;i++) scanf("%lld",&Y[i]);

  for(LL i=1;i<=N;i++) A[i].r=X[i],A[i].i=0.0;

  for(LL i=1;i<=N;i++) B[N-i+1].r=Y[i],B[i].i=0.0;
  for(LL i=1;i<=N;i++) B[2*N-i+1].r=Y[i],B[i].i=0.0;
  
  FFT();

  LL Maxx=0; for(LL i=1;i<=N;i++) Maxx=max(Maxx,S[i]);

  for(LL i=-M;i<=M;i++)
    Minx=min(Minx,Solve(i)-Maxx*2);
  return printf("%lld\n",Minx),0;
}
View Code

 2017.4.28

下午就出发了 早上无脑学了个三分技巧 具体见学习教程

 

最后的最后

游记我懒的补了 睡觉起床打题睡觉

省排名54 进了第三天值得高兴 继续加油吧 !

posted @ 2017-03-14 17:24  wohenshuai  阅读(844)  评论(5编辑  收藏  举报