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;
}
APIO2019解题报告的更多相关文章
- CH Round #56 - 国庆节欢乐赛解题报告
最近CH上的比赛很多,在此会全部写出解题报告,与大家交流一下解题方法与技巧. T1 魔幻森林 描述 Cortana来到了一片魔幻森林,这片森林可以被视作一个N*M的矩阵,矩阵中的每个位置上都长着一棵树 ...
- 二模13day1解题报告
二模13day1解题报告 T1.发射站(station) N个发射站,每个发射站有高度hi,发射信号强度vi,每个发射站的信号只会被左和右第一个比他高的收到.现在求收到信号最强的发射站. 我用了时间复 ...
- BZOJ 1051 最受欢迎的牛 解题报告
题目直接摆在这里! 1051: [HAOI2006]受欢迎的牛 Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 4438 Solved: 2353[S ...
- 习题:codevs 2822 爱在心中 解题报告
这次的解题报告是有关tarjan算法的一道思维量比较大的题目(真的是原创文章,希望管理员不要再把文章移出首页). 这道题蒟蒻以前做过,但是今天由于要复习tarjan算法,于是就看到codevs分类强联 ...
- 习题:codevs 1035 火车停留解题报告
本蒟蒻又来写解题报告了.这次的题目是codevs 1035 火车停留. 题目大意就是给m个火车的到达时间.停留时间和车载货物的价值,车站有n个车道,而火车停留一次车站就会从车载货物价值中获得1%的利润 ...
- 习题: codevs 2492 上帝造题的七分钟2 解题报告
这道题是受到大犇MagHSK的启发我才得以想出来的,蒟蒻觉得自己的代码跟MagHSK大犇的代码完全比不上,所以这里蒟蒻就套用了MagHSK大犇的代码(大家可以关注下我的博客,友情链接就是大犇MagHS ...
- 习题:codevs 1519 过路费 解题报告
今天拿了这道题目练练手,感觉自己代码能力又增强了不少: 我的思路跟别人可能不一样. 首先我们很容易就能看出,我们需要的边就是最小生成树算法kruskal算法求出来的边,其余的边都可以删掉,于是就有了这 ...
- NOIP2016提高组解题报告
NOIP2016提高组解题报告 更正:NOIP day1 T2天天爱跑步 解题思路见代码. NOIP2016代码整合
- LeetCode 解题报告索引
最近在准备找工作的算法题,刷刷LeetCode,以下是我的解题报告索引,每一题几乎都有详细的说明,供各位码农参考.根据我自己做的进度持续更新中...... ...
随机推荐
- ThinkPHP目录下面php文件 Access denied. 的问题
对于这种拒绝访问的报错,从我遇到过的问题总结来讲,可以从几个方向入手: 1. 文件权限. 最容易想到的也是这个 使用命令chmod -R 777 目录名 2. 环境配置. 这个我也是有遇到过的 ...
- k线图的分形
蜡烛图上的分形指标,作为一种特殊的K线组合形态,通过对价格的一系列的高低点的描述,辅助识别出市场潜在的突破和反转点,预判后期走势. 顶分形:相邻的五根K线,若中间那根K线最高价为这五根K线的最高价,则 ...
- 华南理工大学 “三七互娱杯” C HRY and Abaas
https://ac.nowcoder.com/acm/contest/874/C 题目大意是两人俄罗斯轮盘赌 n个位置 有m个子弹 已知哪些位置上有子弹 子弹打出 游戏结束 求第i次扣动扳机游戏才结 ...
- Kubernetes服务部署解决方案
学习了K8S的基础知识,我们的目的就是解决我们服务的迁移,那么接下去通过几个案例来感受一下K8s部署带来的便捷与效率. 环境准备: 3个节点,然后我这边也安装了 Ingress. 部署wordpres ...
- SparkStreaming DStream转换
1.无状态转换操作 (1)无状态转化操作就是把简单的RDD转化操作应用到每个批次上,也就是转换DStream中的每一个RDD. 部分无状态转化操作: (2)尽管这些函数韩起来像作用在整个流上一样,但事 ...
- Java编程思想学习录(连载之:内部类)
内部类基本概念 可将一个类的定义置于另一个类定义的内部 内部类允许将逻辑相关的类组织在一起,并控制位于内部的类的可见性 甚至可将内部类定义于一个方法或者任意作用域内! 当然,内部类 ≠ 组合 内部类拥 ...
- 图论 test solution
图论 test solution T1:潜伏 题目背景 小悠回家之后,跟着母亲看了很多抗日神剧,其中不乏一些谍战片. 题目描述 解放前夕,北平城内潜伏着若干名地下党员,他们居住在城市的不同位置.现在身 ...
- RabbitMq学习4-发布/订阅(Publish/Subscribe)
一.发布/订阅 分发一个消息给多个消费者(consumers).这种模式被称为“发布/订阅”. 为了描述这种模式,我们将会构建一个简单的日志系统.它包括两个程序——第一个程序负责发送日志消息,第二个程 ...
- 02: CI(持续集成)/CD(持续交付/持续部署)
1.1 持续集成.持续交付 介绍 参考博客:https://www.cnblogs.com/cay83/p/8856231.html 1.传统交付 1. 传统软件的开发与交付的周期都很漫长,从需求 ...
- 剑指offer 分行从上到下打印二叉树
题目: 从上到下按层打印二叉树,同一层的节点按照从左到右的顺序打印,每一层打印到一行. /* struct TreeNode { int val; struct TreeNode *left; str ...