APIO2019解题报告

「APIO 2019」奇怪装置

题目描述

有无限个二元组,每个二元组为\(((t+\left\lfloor\frac{t}{B} \right\rfloor)\%A,t \% B)\),给出一些区间,问他们之中有多少本质不同的二元组。

题解

考虑朴素做法,区间求并AC

考虑如果每个二元组为\((t\%A,t \% B)\)的话,那么它显然是有一个\(\frac{A*B}{(A,B)}\)的循环节的。

然后我们考虑所有在\(\%B\)意义下同余的所有数\(\%A\)意义下的结果。

他们形成了一个每节长度为\(B\)的一个环。

现在每个数又多了一个偏移量,相当于变成了\(B+1\)

那么循环节就变成了\(\frac{A*B}{(A,B+1)}\)

#include<bits/stdc++.h>
#define N 1000009
using namespace std;
typedef long long ll;
ll n,A,B,ans;
int tot;
inline ll rd(){
  ll x=0;char c=getchar();bool f=0;
  while(!isdigit(c)){if(c=='-')f=1;c=getchar();}
  while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
  return f?-x:x;
}
inline ll gcd(ll x,ll y){return y?gcd(y,x%y):x;}
struct node{
  ll l,r;
  inline bool operator <(const node &b)const{
    return l<b.l;
  }
}a[N<<1];
int main(){
  n=rd();A=rd();B=rd();
  ll G=A/gcd(A,B+1)*B;
  if(G/B!=A/gcd(A,B+1))G=9e18;
  ll l,r;
  for(int i=1;i<=n;++i){
    l=rd();r=rd();
    if(r-l+1>=G){cout<<G;return 0;}
    l%=G;r%=G;
    if(l>r){
      a[++tot]=node{l,G-1};
      a[++tot]=node{0,r};
    }
    else a[++tot]=node{l,r};
  }
  sort(a+1,a+tot+1);
  ll p=0;
  for(int i=1;i<=tot;++i){
    p=max(p,a[i].l);
    ans+=max(0ll,a[i].r-p+1);
    p=max(p,a[i].r+1);
  }
  cout<<ans;
  return 0;
}

「APIO 2019」桥梁

题目描述

无向图,边有边权,每次可以修改一条边的边权,或者询问从一个点出发走边权不小于w的边能够到达的点数。

题解

考虑朴素做法,定期重构AC

话说这不就是\([HNOI2016]\)最小公倍数吗?

然而那道题\(KD-tree\)分治能过,这题不行。

我们可以对所有操作分块,每个块内把边权不会动的边拿出来排序,会动的按照操作时间排序,每次用指针卡不会动的边,会动的暴力做就行了,做完一个快就就算所有修改。

代码

#include<bits/stdc++.h>
#define N 100002
using namespace std;
typedef long long ll;
int n,m,n1,q;
int top,top1,top2,top3,be[N],f[N],dep[N],ans[N];
int size[N],pos[N];
bool vis[N],vi[N];
inline ll rd(){
  ll x=0;char c=getchar();bool f=0;
  while(!isdigit(c)){if(c=='-')f=1;c=getchar();}
  while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
  return f?-x:x;
}
struct edge{
  int u,v,w,id;
  inline bool operator <(const edge &b)const{
    return w>b.w;
  }
}b[N],st1[N];
struct Q{
  int opt,u,v,id,tim;
  inline bool operator<(const Q &b)const{
    return v>b.v;
  }
}c[N],st2[N],st3[N];
struct node{
  int u,v,val;
}st[N];
int find(int x){return f[x]==x?x:find(f[x]);}
inline void add(int u,int v,bool tag){
  int xx=find(u),yy=find(v);
  if(xx==yy)return;
  if(dep[xx]>dep[yy])swap(xx,yy);
  if(tag)st[++top]=node{xx,yy,dep[yy]};
  f[xx]=yy;size[yy]+=size[xx];dep[yy]=max(dep[yy],dep[xx]+1);
}
inline void del(){
  node x=st[top];top--;
  dep[x.v]=x.val;f[x.u]=x.u;size[x.v]-=size[x.u];
}
inline int query(int x){return size[find(x)];}
int main(){
  n=rd();m=rd();
  int n1=max(20,(int)sqrt(max(1,m)*log2(n)));
  for(int i=1;i<=m;++i){
    b[i].u=rd();b[i].v=rd();b[i].w=rd();b[i].id=i;
  }
  sort(b+1,b+m+1);
  int dd=0;
  for(int i=1;i<=m;++i)pos[b[i].id]=i;
  q=rd();
  for(int i=1;i<=q;++i){
    c[i].opt=rd();c[i].u=rd();c[i].v=rd();
    c[i].tim=i;
    if(c[i].opt==2)c[i].id=++dd;
  }
  for(int i=1;i<=q;++i)be[i]=(i-1)/n1+1;
  for(int i=1;i<=be[q];++i){
    int l=(i-1)*n1+1,r=min(q,i*n1);
    top1=top2=top3=0;
    for(int j=l;j<=r;++j)
      if(c[j].opt==1)vi[c[j].u]=1,st3[++top3]=c[j];
      else st2[++top2]=c[j];
    sort(st2+1,st2+top2+1);
    for(int j=1;j<=n;++j)f[j]=j,size[j]=dep[j]=1;
    for(int j=1;j<=m;++j)if(!vi[b[j].id])st1[++top1]=b[j];
    int p=1;
    for(int j=1;j<=top2;++j){
      while(st1[p].w>=st2[j].v&&p<=top1)add(st1[p].u,st1[p].v,0),p++;
      for(int k=1;k<=top3;++k)if(st3[k].tim<st2[j].tim)vis[st3[k].u]=1;else break;
      for(int k=top3;k>=1;--k){
        if(st3[k].tim>st2[j].tim){
          if(!vis[st3[k].u]&&b[pos[st3[k].u]].w>=st2[j].v)add(b[pos[st3[k].u]].u,b[pos[st3[k].u]].v,1);
          continue;
        }
        if(vis[st3[k].u]&&st3[k].v>=st2[j].v)add(b[pos[st3[k].u]].u,b[pos[st3[k].u]].v,1);
        vis[st3[k].u]=0;
      }
      ans[st2[j].id]=query(st2[j].u);
      while(top)del();
    }
    for(int j=l;j<=r;++j){
      vi[c[j].u]=0;
      if(c[j].opt==1)b[pos[c[j].u]].w=c[j].v;
    }
    sort(b+1,b+m+1);
    for(int j=1;j<=m;++j)pos[b[j].id]=j;

  }
  for(int i=1;i<=dd;++i)printf("%d\n",ans[i]);
  return 0;
}

「APIO 2019」路灯

题目描述

有一个序列,相邻两个元素之间有一个桥,每次改变一座桥的联通状态,或者询问两个点有多少时刻是联通的。

题解

考虑朴素做法,三维偏序AC

不过这个三维偏序确实比较明显,直接打个带时间戳的标记就行了。

代码

#include<bits/stdc++.h>
#define N 300009
#define ls tr[cnt].l
#define rs tr[cnt].r
using namespace std;
typedef long long ll;
char S[N];
bool nw[N];
int tot,tott,nwtim,n,q,rot;
set<int>s;
set<int>::iterator it;
inline ll rd(){
  ll x=0;char c=getchar();bool f=0;
  while(!isdigit(c)){if(c=='-')f=1;c=getchar();}
  while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
  return f?-x:x;
}
int tag;
struct point{
  int x[2];
  inline bool operator <(const point &b)const{
    if(x[tag]!=b.x[tag])return x[tag]<b.x[tag];
    else return x[tag^1]<b.x[tag^1];
  }
  inline bool operator ==(const point &b)const{
    return x[tag]==b.x[tag]&&x[tag^1]==b.x[tag^1];
  }
}b[N];
inline bool jiao(int l1,int r1,int l2,int r2){
  if(l1>l2)swap(l1,l2),swap(r1,r2);
  return (l2>=l1&&l2<=r1)||(r1>=l2&&r1<=r2);
}
inline bool In(int x,int l1,int r1){
  return x>=l1&&x<=r1;
}
struct matrix{
  point mi,mx;
};
struct seg{
  int l,r;
  point a,mx,mi;
  int la,sum;
}tr[N];
struct Q{
  int opt,x,y;
}a[N];
inline void pushdown(int cnt){
  if(ls)tr[ls].sum+=tr[cnt].la,tr[ls].la+=tr[cnt].la;
  if(rs)tr[rs].sum+=tr[cnt].la,tr[rs].la+=tr[cnt].la;
  tr[cnt].la=0;
}
inline void pushup(int cnt){
  tr[cnt].mi=tr[cnt].mx=tr[cnt].a;
  for(int i=0;i<2;++i){
    if(ls){
      tr[cnt].mi.x[i]=min(tr[cnt].mi.x[i],tr[ls].mi.x[i]);
      tr[cnt].mx.x[i]=max(tr[cnt].mx.x[i],tr[ls].mx.x[i]);
    }
    if(rs){
      tr[cnt].mi.x[i]=min(tr[cnt].mi.x[i],tr[rs].mi.x[i]);
      tr[cnt].mx.x[i]=max(tr[cnt].mx.x[i],tr[rs].mx.x[i]);
    }
  }
}
void build(int &cnt,int l,int r,int tg){
  if(l>r)return;
  if(!cnt)cnt=++tott;
  int mid=(l+r)>>1;
  tag=tg;
  nth_element(b+l,b+mid,b+r+1);
  tr[cnt].a=b[mid];
  build(ls,l,mid-1,tg^1);
  build(rs,mid+1,r,tg^1);
  pushup(cnt);
}
inline bool pd1(matrix x,seg now){
  if(x.mx.x[0]>=now.mx.x[0]&&x.mx.x[1]>=now.mx.x[1]&&x.mi.x[0]<=now.mi.x[0]&&x.mi.x[1]<=now.mi.x[1])
    return 1;
  return 0;
}
inline bool pd2(matrix x,seg now){
  if(pd1(x,now))return 1;
  if(!jiao(now.mi.x[0],now.mx.x[0],x.mi.x[0],x.mx.x[0]))return 0;
  if(!jiao(now.mi.x[1],now.mx.x[1],x.mi.x[1],x.mx.x[1]))return 0;
  return 1;
}
void upd(int cnt,matrix x,int y){
  if(pd1(x,tr[cnt])){
    tr[cnt].sum+=y;
    tr[cnt].la+=y;
    return;
  }
  if(In(tr[cnt].a.x[0],x.mi.x[0],x.mx.x[0])&&In(tr[cnt].a.x[1],x.mi.x[1],x.mx.x[1]))tr[cnt].sum+=y;
  if(ls&&pd2(x,tr[ls]))upd(ls,x,y);
  if(rs&&pd2(x,tr[rs]))upd(rs,x,y);
}
void query(int cnt,point x,int tg,int op){
  tag=tg;
  if(x==tr[cnt].a){
    printf("%d\n",tr[cnt].sum+nwtim*op);
    return;
  }
  pushdown(cnt);
  if(x<tr[cnt].a)query(ls,x,tg^1,op);
  else query(rs,x,tg^1,op);
}
void check(int cnt){
  printf("%d %d %d %d\n",cnt,tr[cnt].a.x[0],tr[cnt].a.x[1],tr[cnt].sum);
  pushdown(cnt);
  if(ls)check(ls);
  if(rs)check(rs);
}
int main(){
  n=rd()+1;q=rd();
  scanf("%s",S+1);
  for(int i=1;i<=n;++i)nw[i]=S[i]-'0';
  for(int i=1;i<=q;++i){
    scanf("%s",S);
    if(S[0]=='t'){a[i].opt=1;a[i].x=rd();}
    else {a[i].x=rd();a[i].y=rd();b[++tot]=point{a[i].x,a[i].y};}
  }
  sort(b+1,b+tot+1);
  tot=unique(b+1,b+tot+1)-b-1;
  build(rot,1,tot,0);
  matrix x;
  s.insert(0);s.insert(n);
  for(int i=1;i<n;++i)if(!nw[i])s.insert(i);
  for(int i=1;i<=q;++i){
    nwtim=i;
    if(a[i].opt){
      if(nw[a[i].x]){
        nw[a[i].x]=0;
        s.insert(a[i].x);
        it=s.lower_bound(a[i].x);
        --it;
        x.mi.x[0]=*it+1;x.mx.x[0]=a[i].x;
        ++it;++it;
        x.mi.x[1]=a[i].x+1;x.mx.x[1]=*it;
        upd(rot,x,nwtim);
      }
      else{
        nw[a[i].x]=1;
        it=s.lower_bound(a[i].x);
        --it;
        x.mi.x[0]=*it+1;x.mx.x[0]=a[i].x;
        ++it;++it;
        x.mi.x[1]=a[i].x+1;x.mx.x[1]=*it;
        upd(rot,x,-nwtim);
        --it;
        s.erase(it);
      }
    }
    else{
      it=s.lower_bound(a[i].x);
      if(*it<a[i].y)query(rot,point{a[i].x,a[i].y},0,0);
      else query(rot,point{a[i].x,a[i].y},0,1);
    }
    //check(rot);puts("");
  }
  return 0;
}
posted @ 2019-07-08 14:45  comld  阅读(502)  评论(0编辑  收藏  举报