更高更妙の数据结构专练
为什么更高更妙呢?因为时间复杂度一般有除了 \(\log\) 以外的东西。
A. [SHOI2006] 作业
直接根号分治,对于 \(Y\le\sqrt V\) 的情况直接记录每个集合的答案,否则记录每个集合的数,绕环找后继即可。时间复杂度可以做到 \(O(n\sqrt V)\),不过 \(O(n\sqrt V\log n)\) 也能过。
#include<bits/stdc++.h>
using namespace std;
const int N=3e5+5,k=300;
int n,nx[N],fl[1005],as[1005];
multiset<int>st;
inline void chg(int x,int l,int r,int v){
if(fl[x]){
for(int i=x*k;i>x*k-k;i--) nx[i]=fl[x];
fl[x]=0;
}
for(int i=l;i<=r;i++) nx[i]=v;
}
inline void add(int l,int r,int v){
if((l-1)/k==(r-1)/k){
chg((l-1)/k+1,l,r,v);
return;
}
chg((l-1)/k+1,l,(l-1)/k*k+k,v);
chg((r-1)/k+1,(r-1)/k*k+1,r,v);
for(int i=(l-1)/k+2;i<=(r-1)/k;i++) fl[i]=v;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n;
st.insert(0),st.insert(3e5+1);
for(int i=1;i<=1000;i++) as[i]=1e9;
for(int i=1;i<=3e5;i++) nx[i]=1e9;
while(n--){
char c;
int x;
cin>>c>>x;
if(c=='B'){
if(x>1000){
int ans=(fl[1]?fl[1]:nx[1]);
for(int i=x;i<=3e5;i+=x)
ans=min(ans,(fl[(i-1)/k+1]?fl[(i-1)/k+1]:nx[i])-i);
cout<<ans<<"\n";
}
else cout<<as[x]<<"\n";
}
else{
int num=*--st.lower_bound(x);
st.insert(x),add(num+1,x,x);
for(int i=1;i<=1000;i++) as[i]=min(as[i],x%i);
}
}
return 0;
}
B. [CF1882G2] Magic Triples (Hard Version)
由于上一题,所以下意识想到直接对 \(b\) 做根号分治。对于 \(b\le\sqrt[3]V\) 是容易的,时间复杂度为 \(O(n\sqrt[3]V)\);但是发现解决 \(\sqrt[3]V<b\le\sqrt V\) 这一部分的时间复杂度为 \(O(\min(n\sqrt[2]V,V^\frac 56))\),由于是多测,所以这个时间复杂度肯定是过不了的。考虑对于 \(n\) 较小的情况,写 \(O(n^2)\) 的暴力,这样就可以迷惑地 \(A\) 了。
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int T,n,a[N];
long long ans;
struct hash_map{
static const int modd=1145141;
struct lxl{
int u,v,nx;
}e[N];
int cnt,h[modd];
inline void clear(){
while(cnt) h[e[cnt--].u%modd]=0;
}
inline void add(int x,int v){
int hx=x%modd;
for(int i=h[hx];i;i=e[i].nx)
if(e[i].u==x){
e[i].v+=v;
return;
}
e[++cnt]={x,v,h[hx]},h[hx]=cnt;
}
inline int num(int x){
int hx=x%modd;
for(int i=h[hx];i;i=e[i].nx)
if(e[i].u==x) return e[i].v;
return 0;
}
}mp;
inline void solve(){
cin>>n;
mp.clear();
for(int i=1;i<=n;i++)
cin>>a[i],mp.add(a[i],1);
sort(a+1,a+n+1),ans=0;
n=unique(a+1,a+n+1)-a-1;
for(int i=1;i<=n;i++)
ans+=1ll*mp.num(a[i])*(mp.num(a[i])-1)*(mp.num(a[i])-2);
if(n<=5000){
for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++)
if(a[j]%a[i]==0&&1e9/a[j]*a[i]>=a[j])
ans+=1ll*mp.num(a[i])*mp.num(a[j])*mp.num(a[j]/a[i]*a[j]);
cout<<ans<<"\n";
return;
}
for(int i=2;i<=32000;i++)
for(int j=1;j<=n&&a[j]<=1e9/i/i;j++)
ans+=1ll*mp.num(a[j])*mp.num(a[j]*i)*mp.num(a[j]*i*i);
cout<<ans<<"\n";
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>T;
while(T--) solve();
return 0;
}
C. [Ynoi Easy Round 2015] 此时此刻的光辉
之前做过。
显然的想法是直接质因数分解,然后由于 \(\le V=10^9\) 的数最多只有 \(10\) 个质因子,所以只有 \(10n\) 个质数需要考虑。离散化后跑莫队就可以拿到 \(O(10n\sqrt n)\) 的时间复杂度了。由于质因数分解时间太久了,考虑将 \(\le 1000\) 的质数全部拉出来计算前缀和,这样就只需要维护 \(>1000\) 的质数了。由于只剩两个,所以分解质因数速度大大提高。
上古码风,懒得改了。
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5,p=19260817;
template<class P,class Q>
class HashTable{
static const int mod=1e6+7,L=mod+10;
int head[L],nxt[N*2],siz;P val[N*2];Q sav[N*2];
public:
inline void clear() {
for(int i=1;i<=siz;i++){
head[val[i]%mod]=0,nxt[i]=val[i]=0;
Q tmp;swap(sav[i],tmp);
}siz=0;
}inline void insert(P y){
val[++siz]=y;
nxt[siz]=head[y%mod];
head[y%mod]=siz;
}inline Q &operator[](const P y) {
int x=y%mod;
for(int i=head[x];i;i=nxt[i])
if(val[i]==y)
return sav[i];
insert(y);
return sav[siz];
}inline bool count(const P y) {
int x=y%mod;
for(int i=head[x];i;i=nxt[i])
if(val[i]==y) return 1;
return 0;
}
};HashTable<int,int>mp;
int n,m,a[N],pc[205],vs[1005],tg[205],tp,as;
vector<int>cc[N];vector<pair<int,int> >pr[N];
struct que{int l,r,id;}qu[N];int ans[N],inv[N*40];
int cmp(que x,que y){
if(x.l/300!=y.l/300) return x.l/300<y.l/300;
return (x.l/300%2?x.r>y.r:x.r<y.r);
}namespace rho{
int mr[8]={2,3,5,7,11,13,17,19};
int qpow(int x,int y,int c){
int re=1;
while(y){
if(y&1) re=1ll*re*x%c;
x=1ll*x*x%c,y>>=1;
}return re;
}int check(int x,int md){
int c=md-1,mid=qpow(x,c,md);
if(mid!=1) return 0;
while(!(c&1)&&mid==1)
c>>=1,mid=qpow(x,c,md);
return (mid==1||mid==md-1);
}int miller_rabin(int x){
if(x<2) return 0;
if(x<=19){
for(int i=0;i<8;i++)
if(mr[i]==x) return 1;
return 0;
}for(int i=0;i<8;i++)
if(!check(mr[i],x)) return 0;
return 1;
}int gcd(int x,int y){
return (!y)?x:gcd(y,x%y);
}int pr(int x){
int s=0,t=0,val=1;
int c=rand()%(x-1)+1;
for(int gl=1;;gl*=2,s=t,val=1){
for(int st=1;st<=gl;st++){
t=(1ll*t*t+c)%x;
val=1ll*val*abs(t-s)%x;
if(st%127==0){
int d=gcd(val,x);
if(d>1) return d;
}
}int d=gcd(val,x);
if(d>1) return d;
}
}void pol_rho(int x,vector<int>&ve){
if(x<2) return;
if(miller_rabin(x))
return ve.push_back(x),void();
int p=x;while(p>=x) p=pr(x);
while(x%p==0) x/=p,pol_rho(p,ve);
pol_rho(x,ve);
}
}void add(int x){
for(auto [y,cc]:pr[x]) tg[y]+=cc,as=1ll*as*inv[tg[y]-cc+1]%p*(tg[y]+1)%p;
for(auto y:cc[x]) mp[y]++,as=1ll*as*inv[mp[y]]%p*(mp[y]+1)%p;
}void del(int x){
for(auto [y,cc]:pr[x]) as=1ll*as*inv[tg[y]+1]%p*(tg[y]-cc+1)%p,tg[y]-=cc;
for(auto y:cc[x]) as=1ll*as*inv[mp[y]+1]%p*mp[y]%p,mp[y]--;
}signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0),cin>>n>>m,inv[1]=1;
for(int i=2;i<=4e6;i++) inv[i]=1ll*(p-p/i)*inv[p%i]%p;
for(int i=2;i<=1e3;i++) if(!vs[i]){
for(int j=i*2;j<=1e3;j+=i) vs[j]=1;pc[++tp]=i;
}for(int i=1;i<=n;i++){
int a;cin>>a;
for(int j=1;j<=tp&&a>1;j++){
if(a%pc[j]) continue;int num=0;
while(a%pc[j]==0) num++,a/=pc[j];
pr[i].push_back({j,num});
}rho::pol_rho(a,cc[i]);
}for(int i=1;i<=m;i++) cin>>qu[i].l>>qu[i].r,qu[i].id=i;
sort(qu+1,qu+m+1,cmp),as=1;
for(int i=1,l=1,r=0;i<=m;i++){
while(r<qu[i].r) add(++r);
while(l>qu[i].l) add(--l);
while(r>qu[i].r) del(r--);
while(l<qu[i].l) del(l++);
ans[qu[i].id]=as;
}for(int i=1;i<=m;i++) cout<<ans[i]<<"\n";
return 0;
}
D. [Ynoi2013] 无力回天 NOI2017
首先求并用容斥原理搞成求交。
有两种思路。第一种是计算出每个集合都有哪些数,然后求交。可以使用 \(bitset\) 随意优化到 \(O(\frac{d^2}w)\),但是 \(A\) 不了。其中 \(d\) 表示出现的元素种类数。
第二种是直接计算 \(dp_{x,y}\) 表示他们的交有多大。每次给 \(x\) 加入一个数时考虑 \(y\) 有没有,有的话 \(dp_{x,y}\) 加一。时间复杂度 \(O(n^2)\),但是对于每个数,若它出现 \(c\) 次,时间复杂度就为 \(O(c^2)\)。
我们发现一个希望每个元素出现次数尽量小,一个希望元素尽量少,考虑根号分治,将时间复杂度转化为 \(O(n\sqrt{\frac nw})\),佐以卡常即可通过。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
char buf[1<<20],*p1,*p2;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?0:*p1++)
template<typename T>
inline void read(T &x){
x=0;
char c=getchar();
bool fl=0;
while(c>57||c<48) fl=(c=='-'),c=getchar();
while(c>=48&&c<=57)
x=(x<<1)+(x<<3)+c-48,c=getchar();
x=(fl?-x:x);
}
template<typename T>
inline void write(T x){
if(x<0) x=-x,putchar('-');
if(x>9) write(x/10);
putchar(x%10+48);
}
const int N=1e6+5,M=3505;
int n,cnt,df[M],num[N],vs[N],nm[N];
int anna[N],*c[N],*ed;
bitset<M>bt[N];
struct que{
int opt,x,y;
}qu[N];
struct hash_map{
static const int modd=5074157;
struct lxl{
int x,y;
int v,nx;
}e[N];
int cnt,h[modd];
inline void mk(int x,int y){
int hx=(1ll*x*n+y)%modd;
e[++cnt]={x,y,0,h[hx]},h[hx]=cnt;
}
inline void add(int x,int y){
int hx=(1ll*x*n+y)%modd;
for(int i=h[hx];i;i=e[i].nx)
if(e[i].x==x&&e[i].y==y){
e[i].v++;
return;
}
return;
}
inline int num(int x,int y){
int hx=(1ll*x*n+y)%modd;
for(int i=h[hx];i;i=e[i].nx)
if(e[i].x==x&&e[i].y==y) return e[i].v;
return 0;
}
}mp;
int main(){
read(n);
for(int i=1;i<=n;i++){
read(qu[i].opt),read(qu[i].x),read(qu[i].y);
if(qu[i].opt==1) num[qu[i].y]++;
else{
if(qu[i].x>qu[i].y) swap(qu[i].x,qu[i].y);
mp.mk(qu[i].x,qu[i].y);
}
}
ed=anna;
for(int i=1;i<=n;i++){
if(num[i]>100) df[num[i]=++cnt]=i,vs[i]=1;
else c[i]=ed,ed+=num[i],num[i]=0;
}
for(int i=1;i<=n;i++){
if(qu[i].opt==1){
if(!vs[qu[i].y]){
c[qu[i].y][++num[qu[i].y]]=qu[i].x;
for(int j=1;j<=num[qu[i].y];j++){
int x=c[qu[i].y][j],y=qu[i].x;
if(x>y) swap(x,y);
mp.add(x,y);
}
}
else bt[qu[i].x][num[qu[i].y]]=1;
nm[qu[i].x]++;
}
else{
int ans=(bt[qu[i].x]&bt[qu[i].y]).count();
write(nm[qu[i].x]+nm[qu[i].y]-ans-mp.num(qu[i].x,qu[i].y));
putchar('\n');
}
}
return 0;
}
E. [Cnoi2019] 数字游戏
考虑到四元组过于麻烦,必定要通过两个数据结构分别解决。那就只有树套树或莫队+分块能解决这类问题。本题使用后者。
实际上这道题莫队维护值域会更方便。发现莫队将本题转化为对于一个 \(0/1\) 序列做单点修改和区间查联通块大小的二次方之和。使用线段树一类的 \(O(\log n)-O(\log n)\) 的算法显然比较容易。考虑分块似乎并不方便,于是改用类块状链表状物进行计算。快速查询一个链表大小则用小常数的可撤销并查集即可。时间复杂度 \(O(n\sqrt{n\log n})\)。
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int n,q,a[N],ps[N],kl=500;
long long ans[N],num[N];
int fa[N],sz[N],fl[N],tp;
struct rt{
int id;
long long nm;
int xc,yc;
}st[N];
inline long long numd(int x){
return x*(x+1ll)/2;
}
inline int find(int x){
return x==fa[x]?x:find(fa[x]);
}
inline void unite(int x,long long &re,int fl=0){
int y=find(x-1);
x=find(x);
long long flg=re;
re-=numd(sz[x])+numd(sz[y]);
if(sz[x]<sz[y]) swap(x,y);
if(fl) st[++tp]={0,0,x,y};
sz[x]+=sz[y],fa[y]=x;
re+=numd(sz[x]);
}
struct que{
int l,r,x,y,id;
}qu[N];
inline bool cmp(que x,que y){
return x.x/kl!=y.x/kl?x.x<y.x:x.y<y.y;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>q;
for(int i=1;i<=n;i++) cin>>a[i],ps[a[i]]=i;
for(int i=1;i<=q;i++)
cin>>qu[i].l>>qu[i].r>>qu[i].x>>qu[i].y,qu[i].id=i;
sort(qu+1,qu+q+1,cmp);
for(int j=1;j<=n;j++) sz[fa[j]=j]=1,fl[j]=0;
for(int i=1,cc=0,r=kl-1;i<=q;i++){
if(cc!=qu[i].x/kl){
cc=qu[i].x/kl,r=cc*kl+kl-1;
for(int j=0;j<=500;j++) num[j]=0;
for(int j=1;j<=n;j++) sz[fa[j]=j]=1,fl[j]=0;
}
if(qu[i].x/kl==qu[i].y/kl){
for(int j=qu[i].x;j<=qu[i].y;j++)
if(ps[j]>=qu[i].l&&ps[j]<=qu[i].r){
fl[ps[j]]=1,ans[qu[i].id]++;
if(fl[ps[j]-1]) unite(ps[j],ans[qu[i].id]);
if(fl[ps[j]+1]) unite(ps[j]+1,ans[qu[i].id]);
}
for(int j=qu[i].x;j<=qu[i].y;j++)
fl[ps[j]]=0,sz[fa[ps[j]]=ps[j]]=1;
continue;
}
while(r<qu[i].y){
num[ps[++r]/kl]++,fl[ps[r]]=1;
if(ps[r]%kl&&fl[ps[r]-1]) unite(ps[r],num[ps[r]/kl]);
if((ps[r]+1)%kl&&fl[ps[r]+1]) unite(ps[r]+1,num[ps[r]/kl]);
}
for(int j=qu[i].x;j<cc*kl+kl;j++){
st[++tp]={ps[j],num[ps[j]/kl],0,0};
fl[ps[j]]=1,num[ps[j]/kl]++;
if(ps[j]%kl&&fl[ps[j]-1]) unite(ps[j],num[ps[j]/kl],1);
if((ps[j]+1)%kl&&fl[ps[j]+1]) unite(ps[j]+1,num[ps[j]/kl],1);
}
int numc=fl[qu[i].l];
if(qu[i].l/kl==qu[i].r/kl){
for(int j=qu[i].l+1;j<=qu[i].r;j++){
if(fl[j]) numc++;
else ans[qu[i].id]+=numd(numc),numc=0;
}
ans[qu[i].id]+=numd(numc);
}
else{
for(int j=qu[i].l+1;j<qu[i].l/kl*kl+kl;j++){
if(fl[j]) numc++;
else ans[qu[i].id]+=numd(numc),numc=0;
}
for(int j=qu[i].l/kl+1;j<qu[i].r/kl;j++){
if(sz[find(j*kl)]*fl[j*kl]==kl) numc+=kl;
else{
ans[qu[i].id]+=num[j]-numd(fl[j*kl]*sz[find(j*kl)]);
ans[qu[i].id]+=numd(numc+fl[j*kl]*sz[find(j*kl)]);
ans[qu[i].id]-=numd(numc=fl[j*kl+kl-1]*sz[find(j*kl+kl-1)]);
}
}
for(int j=qu[i].r/kl*kl;j<=qu[i].r;j++){
if(fl[j]) numc++;
else ans[qu[i].id]+=numd(numc),numc=0;
}
ans[qu[i].id]+=numd(numc);
}
while(tp){
num[st[tp].id/kl]=st[tp].nm;
sz[st[tp].xc]-=sz[st[tp].yc];
fa[st[tp].yc]=st[tp].yc;
fl[st[tp--].id]=0;
}
}
for(int i=1;i<=q;i++) cout<<ans[i]<<"\n";
return 0;
}
F. [Ynoi2013] 文化课
我当时为什么会点开这道题……
考虑乘法操作连成一个联通块,所以线段树维护的信息和上一道题差不多一个样。假如没有修改,就是和上一道题的线段树差不多;假如只修改符号,就对于每个区间记录全乘和全加的值;假如同时修改数字序列,那就对每个区间维护一个多项式意义的信息,修改时带入计算新信息即可。
由于经典结论,长为 \(n\) 的算式组成的多项式项数为 \(O(\sqrt n)\)。多项式合并的时候使用归并排序,时间复杂度就可以做到 \(O(n\sqrt n)\)。由于要用快速幂所以时间复杂度多一个 \(\log\)(不过好像可以被优化掉)。
#include<bits/stdc++.h>
#define int long long
using namespace std;
char buf[1<<20],*p1,*p2;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?0:*p1++)
template<typename T>
inline void read(T &x){
x=0;
char c=getchar();
bool fl=0;
while(c>57||c<48) fl=(c=='-'),c=getchar();
while(c>=48&&c<=57)
x=(x<<1)+(x<<3)+c-48,c=getchar();
x=(fl?-x:x);
}
template<typename T>
inline void write(T x){
if(x>9) write(x/10);
putchar(x%10+48);
}
const int N=1e5+5,p=1e9+7;
inline int qpow(int x,int y){
int re=1;
while(y){
if(y&1) re=re*x%p;
x=x*x%p,y>>=1;
}
return re;
}
int n,m,ac[N],pc[N],lv[N<<1];
int tms[N<<1],sum[N<<1],fla[N<<1],flp[N<<1];
struct seg{
int fl,pr,sf,ans;
}sg[N<<1];
struct dat{
int a,b;
};
struct segdx{
int fl,pr,sf;
vector<dat>ans;
}sgd[N<<1];
inline seg operator+(seg x,seg y){
if(x.fl&&y.fl)
return {1,x.pr*y.pr%p,x.sf*y.sf%p,0};
if(x.fl) return {0,x.pr*y.pr%p,y.sf,y.ans};
if(y.fl) return {0,x.pr,x.sf*y.sf%p,x.ans};
return {0,x.pr,y.sf,(x.ans+y.ans+x.sf*y.pr)%p};
}
inline segdx operator+(segdx x,segdx y){
if(x.fl&&y.fl)
return {1,x.pr+y.pr,x.sf+y.sf,{}};
if(x.fl) return {0,x.pr+y.pr,y.sf,y.ans};
if(y.fl) return {0,x.pr,x.sf+y.sf,x.ans};
segdx re={0,x.pr,y.sf,{}};
int lid=0,rid=0,fl=0;
while(lid<x.ans.size()||rid<y.ans.size()||!fl){
dat ad=(lid==x.ans.size()?(dat){(int)1e18,0}:x.ans[lid]);
int flx=1,fly=0,flf=0;
if(rid<y.ans.size()){
if(ad.a>y.ans[rid].a) ad=y.ans[rid],flx=0,fly=1;
else if(ad.a==y.ans[rid].a) ad.b+=y.ans[rid].b,fly=1;
}
if(!fl){
if(ad.a>x.sf+y.pr) ad={x.sf+y.pr,1},flx=fly=0,flf=1;
else if(ad.a==x.sf+y.pr) ad.b++,flf=1;
}
re.ans.push_back(ad),lid+=flx,rid+=fly,fl+=flf;
}
return re;
}
inline seg trans(int x,segdx c){
int re=0;
for(dat y:c.ans) re=(re+y.b*qpow(x,y.a))%p;
return {c.fl,qpow(x,c.pr),qpow(x,c.sf),re};
}
inline void push_up(int x,int mid){
sum[x]=(sum[mid<<1]+sum[mid<<1|1])%p;
tms[x]=tms[mid<<1]*tms[mid<<1|1]%p;
sg[x]=sg[mid<<1]+sg[mid<<1|1];
sgd[x]=sgd[mid<<1]+sgd[mid<<1|1];
lv[x]=lv[mid<<1];
}
inline void build(int x,int l,int r){
if(l==r){
sum[x]=tms[x]=lv[x]=ac[l];
sg[x]={pc[l],ac[l],(pc[l]?ac[l]:1),0};
sgd[x]={pc[l],1,flp[x]=pc[l],{}};
return;
}
int mid=(l+r)>>1;
build(mid<<1,l,mid);
build(mid<<1|1,mid+1,r);
flp[x]=-1,push_up(x,mid);
}
inline void downa(int x,int v,int len){
lv[x]=fla[x]=v,sg[x]=trans(v,sgd[x]);
tms[x]=qpow(v,len),sum[x]=len*v%p;
}
inline void downp(int x,int v,int len){
if(!v){
if(len<2) sgd[x]={0,1,0,{}};
else sgd[x]={0,1,0,{{1,len-1}}};
flp[x]=v,sg[x]={0,lv[x],1,(sum[x]-lv[x]+p)%p};
}
else{
flp[x]=v,sgd[x]={1,len,len,{}};
sg[x]={1,tms[x],tms[x],0};
}
}
inline void push_down(int x,int l,int mid,int r){
if(fla[x]) downa(mid<<1,fla[x],mid-l+1),downa(mid<<1|1,fla[x],r-mid),fla[x]=0;
if(flp[x]>=0) downp(mid<<1,flp[x],mid-l+1),downp(mid<<1|1,flp[x],r-mid),flp[x]=-1;
}
inline void chga(int x,int l,int r,int L,int R,int v){
if(L<=l&&r<=R) return downa(x,v,r-l+1);
int mid=(l+r)>>1;
push_down(x,l,mid,r);
if(L<=mid) chga(mid<<1,l,mid,L,R,v);
if(R>mid) chga(mid<<1|1,mid+1,r,L,R,v);
push_up(x,mid);
}
inline void chgp(int x,int l,int r,int L,int R,int v){
if(L<=l&&r<=R) return downp(x,v,r-l+1);
int mid=(l+r)>>1;
push_down(x,l,mid,r);
if(L<=mid) chgp(mid<<1,l,mid,L,R,v);
if(R>mid) chgp(mid<<1|1,mid+1,r,L,R,v);
push_up(x,mid);
}
inline void answ(int x,int l,int r,int L,int R,seg &c){
if(L<=l&&r<=R){
c=c+sg[x];
return;
}
int mid=(l+r)>>1;
push_down(x,l,mid,r);
if(L<=mid) answ(mid<<1,l,mid,L,R,c);
if(R>mid) answ(mid<<1|1,mid+1,r,L,R,c);
}
inline int num(int x,int l,int r,int k){
if(flp[x]>=0) return !flp[x];
int mid=(l+r)>>1;
if(k<=mid) return num(mid<<1,l,mid,k);
return num(mid<<1|1,mid+1,r,k);
}
signed main(){
read(n),read(m);
for(int i=1;i<=n;i++) read(ac[i]),ac[i]%=p;
for(int i=1;i<n;i++) read(pc[i]);
build(1,1,n);
while(m--){
int op,l,r,x;
read(op),read(l),read(r);
if(op<2) read(x),chga(1,1,n,l,r,x%p);
else if(op==2) read(x),chgp(1,1,n,l,r,x);
else{
seg c={0,0,1,0};
answ(1,1,n,l,r,c);
write((c.ans+c.sf-num(1,1,n,r))%p);
putchar('\n');
}
}
return 0;
}
G. [KTSC2020 R1] 穿越
\(A,B\) 固定的话这题顶多就是个水蓝,直接线段树优化 \(dp\) 即可。考虑到一个可能的答案一定能被表示成 \(xA+yB\) 的形式(其中 \(x,y\) 为正整数),那么假如我们可以知道所有可能的 \((x,y)\),我们就只需要暴力便利这些点即可。容易发现这些点只有在左下凸壳的点才有用,问题转化为求解左下凸壳。我们直接把凸壳当成原先线段树中的变量,那么原先 \(O(n\log n)\) 线段树优化 \(dp\) 就需要再乘上凸壳的点数。由于整点凸壳舍去共线点后点数量级为 \(O(n^\frac 23)\),所以预处理时间复杂度为 \(O(n^\frac 53\log n)\)。查询时可以将凸壳拎出来跑二分,总时间复杂度即为 \(O((n^\frac 53+q)\log n)\)。
#include<vector>
void init(int N, int M,std::vector<int>Y1,std::vector<int>Y2);
long long minimize(int A,int B);
#include<bits/stdc++.h>
#define conh vector<node>
using namespace std;
const int N=10005;
int n,ad[N<<3];
struct node{
int x,y;
};
inline bool operator==(node x,node y){
return x.x==y.x&&x.y==y.y;
}
inline bool check(node x,node y,node z){
return 1ll*(y.y-x.y)*(z.x-y.x)>=1ll*(z.y-y.y)*(y.x-x.x);
}
conh con[N<<3],flg[N<<3],tmp;
inline void addc(node ad){
int num=tmp.size();
if(num&&(tmp[num-1].y<=ad.y||tmp[num-1].x==ad.x)) return;
while(num>1&&check(tmp[num-2],tmp[num-1],ad))
tmp.pop_back(),num--;
tmp.push_back(ad);
}
inline void merge(conh &x,conh &y,conh &z){
conh().swap(tmp);
for(int i=0,j=0;i<x.size()||j<y.size();){
if(i==x.size()) addc(y[j++]);
else if(j==y.size()) addc(x[i++]);
else if(x[i].x<y[j].x||(x[i].x==y[j].x&&x[i].y<y[j].y)) addc(x[i++]);
else addc(y[j++]);
}
swap(z,tmp);
}
inline void add(conh &cc,int xad,int yad){
for(int i=cc.size()-1;~i;i--) cc[i].x+=xad,cc[i].y+=yad;
}
inline void build(int x,int l,int r){
con[x].push_back({0,0});
if(l==r) return;
int mid=(l+r)>>1;
build(x<<1,l,mid);
build(x<<1|1,mid+1,r);
}
inline void downf(int x,conh &v){
merge(flg[x],v,flg[x]),merge(con[x],v,con[x]);
}
inline void downa(int x,int v){
add(flg[x],0,v),add(con[x],0,v),ad[x]+=v;
}
inline void push_down(int x){
if(ad[x]) downa(x<<1,ad[x]),downa(x<<1|1,ad[x]),ad[x]=0;
if(flg[x].size()) downf(x<<1,flg[x]),downf(x<<1|1,flg[x]),conh().swap(flg[x]);
}
inline void chg(int x,int l,int r,int L,int R){
if(L<=l&&r<=R) return downa(x,1);
int mid=(l+r)>>1;
push_down(x);
if(L<=mid) chg(x<<1,l,mid,L,R);
if(R>mid) chg(x<<1|1,mid+1,r,L,R);
merge(con[x<<1],con[x<<1|1],con[x]);
}
inline long long answ(node x,int A,int B){
return 1ll*x.x*A+1ll*x.y*B;
}
int k,e[N<<1];
unordered_map<int,int>mp;
void init(int N,int M,vector<int>Y1,vector<int>Y2){
e[++k]=1;
for(int i:Y1) e[++k]=i+1;
for(int i:Y2) e[++k]=i+2;
sort(e+1,e+k+1),k=unique(e+1,e+k+1)-e-1;
for(int i=1;i<=k;i++) mp[e[i]]=i;
if(e[k]==M+1) k--;
build(1,1,k);
for(int i=0;i<N;i++){
conh().swap(con[k<<2]);
for(node x:con[1]) con[k<<2].push_back(x);
add(con[k<<2],1,0),downf(1,con[k<<2]);
chg(1,1,k,mp[Y1[i]+1],mp[Y2[i]+2]-1);
}
}
long long minimize(int A,int B){
int l=1,r=con[1].size()-1,as=0;
while(l<=r){
int mid=(l+r)>>1;
if(answ(con[1][mid-1],A,B)<=answ(con[1][mid],A,B)) r=mid-1;
else l=mid+1,as=mid;
}
return answ(con[1][as],A,B);
}
//test begin
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
int n,m,q;
vector<int>ga,gb;
cin>>n>>m>>q;
for(int i=1,x,y;i<=n;i++)
cin>>x>>y,ga.push_back(x),gb.push_back(y);
init(n,m,ga,gb);
while(q--){
int a,b;
cin>>a>>b;
cout<<minimize(a,b)<<"\n";
}
return 0;
}
//test end
L. [ABC369G] As far as possible
典中典了,直接把所有加权长链扔到大根堆里一个一个加就行了,时间复杂度 \(O(n\log n)\)。
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int n,sn[N];
long long len[N],ans;
priority_queue<long long>q;
struct edge{
int to,cs;
};
vector<edge>g[N];
inline void dfs(int x,int fa){
for(edge y:g[x]) if(y.to!=fa){
dfs(y.to,x);
if(len[x]<len[y.to]+y.cs){
q.push(len[x]),sn[x]=y.to;
len[x]=len[y.to]+y.cs;
}
else q.push(len[y.to]+y.cs);
}
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n;
for(int i=1,u,v,w;i<n;i++){
cin>>u>>v>>w;
g[u].push_back({v,w});
g[v].push_back({u,w});
}
dfs(1,0);
q.push(len[1]);
for(int i=1;i<=n;i++){
if(q.size()) ans+=q.top(),q.pop();
cout<<ans*2<<"\n";
}
return 0;
}

浙公网安备 33010602011771号