省选测试5
A. 点点的圈圈
可以想到建出一颗树然后树形 \(dp\)
关键在于建树
根据题目给出的无交的条件,可以知道在圆的相对位置不变
用扫描线从左扫到右
再把圆拆成上半圆和下半圆
可以根据 \((x-x_i)^2+(y-y_i)^2=r_i^2\) 和是哪半圆 \(O(1)\) 计算出给定 \(x\) 时 \(y\) 的值
于是用 \(set\) 维护一下圆就行,以 \(y\) 值递增排序
在插入时找到下半圆的前驱
如果没有前驱则说明是一个根
前驱是下半圆说明前驱是他的父亲
前驱是上半圆说明他和前驱是同一个父亲
最后再 \(dp\) 一遍就行
Code
#include<bits/stdc++.h>
#define int long long
#define rint signed
#define inf 0x3f3f3f3f3f3f3f3f
using namespace std;
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
int n,x,p,rt;
int f[100010],fa[100010];
int head[100010],ver[100010],to[100010],tot;
struct Circle{int x,y,r,w;}L[100010];
inline int q2(int x){return x*x;}
inline void add(int x,int y){
//printf("%lld -> %lld\n",x,y);
ver[++tot]=y;to[tot]=head[x];head[x]=tot;
}
inline double calcy(int id,int op){
double w=sqrt(1.0*q2(L[id].r)-1.0*q2(L[id].x-x));
return L[id].y+op*w;
}
void dfs(int x){
int sum=0;
for(int i=head[x];i;i=to[i]){
int y=ver[i];
dfs(y);sum+=f[y];
}
f[x]=max(sum,L[x].w);
}
struct SL{
int id,pos,op;//1加0删
inline bool operator<(const SL &b)const{
if(pos!=b.pos) return pos<b.pos;
return op>b.op;
}
}LL[200010];
struct node{
int id,op;//1上-1下
inline bool operator<(const node &b)const{
double y=calcy(id,op),by=calcy(b.id,b.op);
if(y!=by) return y<by;
if(op!=b.op) return op>b.op;
return id<b.id;
}
};
set<node>S;
set<node>::iterator iter;
signed main(){
#ifdef LOCAL
freopen("in","r",stdin);
freopen("out","w",stdout);
#endif
n=read();rt=n+1;
for(int i=1;i<=n;i++) L[i].x=read(),L[i].y=read(),L[i].r=read(),L[i].w=read();
for(int i=1;i<=n;i++){
p++;LL[p].id=i;LL[p].pos=L[i].x-L[i].r+1;LL[p].op=1;
p++;LL[p].id=i;LL[p].pos=L[i].x+L[i].r-1;LL[p].op=0;
}
sort(LL+1,LL+1+p);
for(int i=1;i<=p;i++){
x=LL[i].pos;
if(LL[i].op==0){
S.erase(S.find((node){LL[i].id,1}));
S.erase(S.find((node){LL[i].id,-1}));
}else{
S.insert((node){LL[i].id,1});
S.insert((node){LL[i].id,-1});
iter=S.find((node){LL[i].id,-1});
if(iter==S.begin()){
add(rt,LL[i].id);fa[LL[i].id]=rt;
continue;
}
iter--;
if(iter->op==1){
add(fa[iter->id],LL[i].id);fa[LL[i].id]=fa[iter->id];
}else{
add(iter->id,LL[i].id);fa[LL[i].id]=iter->id;
}
}
}
dfs(rt);
printf("%lld\n",f[rt]);
return 0;
}
B. 点点的计算
打出表,发现每一行的值都是杨辉三角乘上了一个系数
根据组合数同行递推式和乘上的系数,简单意会可得第 \(n\) 行的前 \(k\) 个的 \(LCM\)
就是 \(N...N-K+1\) 的 \(LCM\)
那么考虑如何求出 \(LCM\)
可以构造一个数组 \(\prod\limits_{i=n}^{n-k+1}D_{n,i}=LCM(n...n-k+1)\)
那么假设已经构造好了 \(D_{n-1}\) 那么如何转移到 \(D_n\)
如果直接在 \(D_{n,n}\) 处插入 \(n\) 那么显然会多乘
于是找到他的每个质因数出现的位置,在那些位置都除掉相应的次数就行
可以用线段树维护,再可持久化一下就能在线了
Code
#include<bits/stdc++.h>
//#define int long long
#define rint signed
#define lson t[x].ls
#define rson t[x].rs
#define mod 1000000007
#define inf 0x3f3f3f3f3f3f3f3f
using namespace std;
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
int q,n,K,A,B,MOD,lst;
int prime[100010],id[100010],g[100010],cnt;
int C[200010],D[200010];
rint rt[100010],tot;
bool is[100010];
vector<pair<int,int>> vec[10010];
struct Seg{rint ls,rs;int prod;}t[10900000];
inline void init(int N){
for(int i=2;i<=N;i++){
if(!is[i]){prime[++cnt]=i;id[i]=cnt;g[i]=i;}
for(int j=1;j<=cnt&&i*prime[j]<=N;j++){
is[i*prime[j]]=1,g[i*prime[j]]=prime[j];
if(i%prime[j]==0) break;
}
}
}
inline int qpow(int x,int k){
int res=1,base=x;
while(k){if(k&1) res=1ll*res*base%mod;base=1ll*base*base%mod;k>>=1;}
return res;
}
void build(rint &x,int l,int r){
t[x=++tot].prod=1;
if(l==r) return ;
int mid=(l+r)>>1;
build(lson,l,mid);
build(rson,mid+1,r);
}
void upd(rint &x,int l,int r,int pos,int k){
rint pre=x;x=++tot;t[x]=t[pre];t[x].prod=1ll*t[pre].prod*k%mod;
//printf("x : %d l : %lld r : %lld pos : %lld k : %lld\n",x,l,r,pos,k);
//printf("t[pre].prod : %lld t[x].prod : %lld\n",t[pre].prod,t[x].prod);
if(l==r) return ;
int mid=(l+r)>>1;
if(pos<=mid) upd(lson,l,mid,pos,k);
else upd(rson,mid+1,r,pos,k);
}
int query(rint x,int l,int r,int L,int R){
if(L<=l&&r<=R) return t[x].prod;
int mid=(l+r)>>1,res=1;
if(L<=mid) res=1ll*res*query(lson,l,mid,L,R)%mod;
if(R>mid) res=1ll*res*query(rson,mid+1,r,L,R)%mod;
return res;
}
void print(rint x,int l,int r){
printf("x : %d l : %lld r : %lld prod : %lld\n",x,l,r,t[x].prod);
if(l==r) return ;
int mid=(l+r)>>1;
print(lson,l,mid);
print(rson,mid+1,r);
}
signed main(){
#ifdef LOCAL
freopen("in","r",stdin);
freopen("out","w",stdout);
#endif
q=read()-1;
n=read(),K=read();
A=read(),B=read(),MOD=read();init(100000);
for(int i=1;i<=q;i++) C[i]=read();
for(int i=1;i<=q;i++) D[i]=read();
t[0].prod=1;
for(int i=1,x,p,k,kkk;i<=100000;i++){
x=i;rt[i]=rt[i-1];
while(x!=1){
p=g[x];k=0;
while(x%p==0) x/=p,k++;kkk=k;
upd(rt[i],1,100000,i,qpow(p,k));
for(int j=(int)vec[id[p]].size()-1,pos,kk;j>=0;j--){
pos=vec[id[p]][j].first,kk=vec[id[p]][j].second;
if(kk>=k){
upd(rt[i],1,100000,pos,qpow(qpow(p,k),mod-2));
vec[id[p]][j].second-=k;if(!vec[id[p]][j].second) vec[id[p]].pop_back();
break;
}else{
k-=kk;
upd(rt[i],1,100000,pos,qpow(qpow(p,kk),mod-2));
vec[id[p]].pop_back();
}
}
vec[id[p]].push_back(make_pair(i,kkk));
}
}
printf("%d\n",lst=query(rt[n],1,100000,n-K+1,n));
for(int i=1;i<=q;i++){
n=1ll*(1ll*A*lst+C[i])%MOD+1;
K=1ll*(1ll*B*lst+D[i])%n+1;
printf("%d\n",lst=query(rt[n],1,100000,n-K+1,n));
}
return 0;
}
C. 点点的最大流
树的情况是平凡的,那考虑把仙人掌边双缩点后变成树
对于每一个边双,都有两条路从进来的点走到出去的点
而且其中一定会走边权最小的边,那么我们把最小的边断掉
给剩下的每条边都加上这个边权,就相当于把两条路的流量都加起来了
用 \(LCT\) 维护,修改的时候分类讨论一下就行了
Code
#include<bits/stdc++.h>
#define int long long
#define rint signed
#define lson t[x].son[0]
#define rson t[x].son[1]
#define inf 0x3f3f3f3f3f3f3f3f
using namespace std;
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
int n,m,q;
int head[300010],ver[600010],to[600010],tot;
int stk[300010],p;
int dfn[300010],low[300010],bl[300010],clo,col;
set<pair<int,int>> S[300010];
set<pair<int,int>>::iterator iter;
bool vis[300010];
struct node{int fa,son[2],mn,val,atag;bool rev;}t[300010];
struct Edge{int x,y,w;}e[300010];
inline void add(int x,int y){ver[++tot]=y;to[tot]=head[x];head[x]=tot;}
inline bool isrt(int x){return (t[t[x].fa].son[0]!=x)&&(t[t[x].fa].son[1]!=x);}
inline void pushup(int x){t[x].mn=min({t[lson].mn,t[rson].mn,t[x].val});}
inline void pushrev(int x){swap(lson,rson);t[x].rev^=1;}
inline void pushadd(int x,int k){t[x].atag+=k,t[x].val+=k,t[x].mn+=k;}
inline void pushdown(int x){
if(t[x].rev) pushrev(lson),pushrev(rson),t[x].rev=0;
if(t[x].atag) pushadd(lson,t[x].atag),pushadd(rson,t[x].atag),t[x].atag=0;
}
inline void rotate(int x){
int y=t[x].fa;
int z=t[y].fa;
int k=t[y].son[1]==x;
if(!isrt(y)) t[z].son[t[z].son[1]==y]=x;
t[y].son[k]=t[x].son[k^1];
t[t[x].son[k^1]].fa=y;
t[x].son[k^1]=y;
t[y].fa=x;t[x].fa=z;
pushup(y);pushup(x);
}
inline void splay(int x){
int y=x,z=0;stk[++z]=x;
while(!isrt(y)) stk[++z]=y=t[y].fa;
while(z) pushdown(stk[z--]);
while(!isrt(x)){
y=t[x].fa;z=t[y].fa;
if(!isrt(y)) (t[z].son[0]==y)^(t[y].son[1]==x)?rotate(x):rotate(y);
rotate(x);
}
pushup(x);
}
inline void access(int x){for(int y=0;x;x=t[y=x].fa) splay(x),t[x].son[1]=y,pushup(x);}
inline void makert(int x){access(x);splay(x);pushrev(x);}
inline int findrt(int x){access(x);splay(x);while(lson) x=lson;splay(x);return x;}
inline void split(int x,int y){makert(x);access(y);splay(y);}
inline void link(int x,int y){makert(x);t[x].fa=y;}
inline void cut(int x,int y){makert(x);access(y);splay(y);t[x].fa=t[y].son[0]=0;pushup(y);}
void tarjan(int x,int fa){
dfn[x]=low[x]=++clo;stk[++p]=x;vis[x]=1;
for(int i=head[x];i;i=to[i]){
int y=ver[i];
if(y==fa) continue;
if(!dfn[y]){
tarjan(y,x);
low[x]=min(low[x],low[y]);
}else if(vis[y]) low[x]=min(low[x],dfn[y]);
}
if(dfn[x]==low[x]){
int k;col++;
do{
k=stk[p--];
S[col].insert(make_pair(t[k].val,k));
bl[k]=col;
vis[k]=0;
}while(k!=x);
}
}
signed main(){
#ifdef LOCAL
freopen("in","r",stdin);
freopen("out","w",stdout);
#endif
n=read(),m=read();
for(int i=0;i<=n;i++) t[i].val=t[i].mn=inf;
for(int i=n+1;i<=n+m;i++){
e[i].x=read(),e[i].y=read(),e[i].w=read();
t[i].val=t[i].mn=e[i].w;
add(e[i].x,i);add(i,e[i].x);
add(e[i].y,i);add(i,e[i].y);
}
tarjan(1,0);
for(int i=1,v,x;i<=col;i++){
if(S[i].size()==1){
x=(*S[i].begin()).second;
if(x>n) link(x,e[x].x),link(x,e[x].y);
}else{
iter=++S[i].begin();
while(iter!=S[i].end()){
x=(*iter).second;
if(x>n) link(x,e[x].x),link(x,e[x].y);
iter++;
}
x=(*S[i].begin()).second;
split(e[x].x,e[x].y);
t[e[x].y].val+=(*S[i].begin()).first;
t[e[x].y].atag+=(*S[i].begin()).first;
}
}
q=read();
for(int i=1,op,x,y,a,b,xx,aa,bb;i<=q;i++){
op=read(),x=read(),y=read();
if(op==1){
x+=n;
if(S[bl[x]].size()==1){
split(x,x);
t[x].val=t[x].mn=y;
}else if((*S[bl[x]].begin()).second==x){
a=e[x].x,b=e[x].y;
split(a,b);
t[b].val-=((*S[bl[x]].begin()).first);
t[b].atag-=((*S[bl[x]].begin()).first);
//t[b].mn-=((*S[bl[x]].begin()).first);
splay(x);t[x].val=y;
splay(b);
S[bl[x]].erase(S[bl[x]].begin());
S[bl[x]].insert(make_pair(y,x));
if((*S[bl[x]].begin()).second==x){
t[b].val+=y;
t[b].atag+=y;
//t[b].mn+=y;
}else{
xx=(*S[bl[x]].begin()).second;
aa=e[xx].x,bb=e[xx].y;
cut(aa,xx);cut(xx,bb);
link(x,a);link(x,b);
split(aa,bb);
t[bb].val+=(*S[bl[x]].begin()).first;
//t[bb].mn+=(*S[bl[x]].begin()).first;
t[bb].atag+=(*S[bl[x]].begin()).first;
}
}else{
xx=(*S[bl[x]].begin()).second;
aa=e[xx].x,bb=e[xx].y;a=e[x].x,b=e[x].y;
split(aa,bb);
t[bb].val-=(*S[bl[x]].begin()).first;
t[bb].atag-=(*S[bl[x]].begin()).first;
//t[bb].mn-=(*S[bl[x]].begin()).first;
splay(x);
S[bl[x]].erase(make_pair(t[x].val,x));
S[bl[x]].insert(make_pair(y,x));
t[x].val=y;
splay(bb);
if((*S[bl[x]].begin()).second==xx){
//t[bb].mn+=(*S[bl[x]].begin()).first;
t[bb].atag+=(*S[bl[x]].begin()).first;
t[bb].val+=(*S[bl[x]].begin()).first;
}else{
cut(a,x);cut(x,b);
link(aa,xx);link(bb,xx);
split(a,b);
t[b].val+=y;
//t[b].mn+=y;
t[b].atag+=y;
}
}
}else{
split(x,y);printf("%lld\n",t[y].mn);
}
}
return 0;
}