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