题解 P5444 【[APIO2019]奇怪装置】
显然这一类问题都有循环节。
发现这个题目让我们求的点对
我们设
不妨设
代入上式
根据高斯函数的性质,原式化为
根据整除的性质,或者提取最大公约数,得
所以只要满足这个式子,就可以满足对于任意
而且这个式子是强的,即不存在不满足这个式子依旧可以满足条件的
所以我们得到了我们想要的循环节
此时
设最小循环节为
然后我们开始对每一个给定的区间
情况1:
如果
情况2:
设
如果
所以我们可以取到这
发现这正好是一个数轴线段覆盖问题。如果我们给所有点对进行编号
情况3:
如果
最后整个数轴被覆盖的长度就是答案了。
实现
关于线段覆盖,我写了
算法1:
动态开点线段树。
亲测只有
算法2:
区间覆盖...珂朵莉树...岂不美哉?
loj 上过了(跑了最后一页),但是 luogu 不开 O2 过不去...
算法3:
由于懒得写 set ,我想到了一种类似贪心的方法。
所有要覆盖的线段按照左端点从小到大排序,然后从左向右扫,找到每一个连通块。
如果当前可以扩展到的右端点比这个线段的左端点小,说明已经有一个连通块了!答案加上连通块的长度即可。
这比上面两个快多了。
看不懂的可以看代码,包看包懂(滑稽)
代码如下(算法
#include<bits/stdc++.h>
#define int long long
#define ll long long
using namespace std;
#ifdef Fading
#define gc getchar
#endif
#ifndef Fading
inline char gc(){
static char now[1<<16],*S,*T;
if (T==S){T=(S=now)+fread(now,1,1<<16,stdin);if (T==S) return EOF;}
return *S++;
}
#endif
inline ll read(){
register ll x=0,f=1;char ch=gc();
while (!isdigit(ch)){if(ch=='-') f=-1LL;ch=gc();}
while (isdigit(ch)){x=(x<<3LL)+(x<<1LL)+ch-'0';ch=gc();}
return (f==1LL)?x:-x;
}
struct node{
int l,r;
}g[2000001];
int tot;
inline bool cmp(node a,node b){
return a.l<b.l;
}
signed main(){
int n=read(),A=read(),B=read();
int G=A/__gcd(A,B+1)*B;
while (n--){
int l=read(),r=read();
if (r-l+1>=G){
printf("%lld\n",G);return 0;
}else{
l=l%G,r=r%G;
if (l>r){
g[++tot].l=0;g[tot].r=r;
g[++tot].l=l;g[tot].r=G-1;
}else{
g[++tot].l=l;g[tot].r=r;
}
}
}
sort(g+1,g+1+tot,cmp);
int ans=0,nowL=g[1].l,nowR=g[1].r;
g[tot+1].l=G+1;//新加入一个哨兵线段,把最后一个连通块算入总答案。
for (int i=2;i<=tot+1;i++){
if (nowR<g[i].l){//前面的线段已经形成了连通块
ans+=nowR-nowL+1;nowL=g[i].l,nowR=g[i].r;
}else{
nowR=max(g[i].r,nowR);//更新可以扩展到的右端点,把这个线段加入连通块。
}
}
printf("%lld\n",ans);
return 0;
}