1
我们考虑这样一个问题:
有一个序列,被划分为了\(k\)个集合,每个集合里的元素在序列上不一定连续,每次进行区间操作,求有多少个集合满足集合里的和小于某个定值
\(1.\) 折半警报器
如果对于每个集合\(id\),其中元素都恰有\(2\)个,那我们考虑将每个元素的阈值设为整体阈值的\(\frac 1 2\),每次区间加变成区间减,假设此二元素分别为\(a,b\),阈值为\(s\),那么有
\(max(a,b)+min(a,b)\le2max(a,b)\)
那么如果\(max(a,b)\le \frac 1 2 s\Rightarrow a+b\le s\)所以,如果\(a,b\le \frac s 2\)则此时一定不超阈值,如果出现\(max(a,b)>\frac s 2\)我们暴力取出\(a,b\)进行修改
\(2.\)二进制警报器
我们设\(t\)为满足\(2^t\le \lceil \frac{s}{k}\rceil\)的最大整数,则我们将原本为\(\frac s 2\)的阈值改为\(2^t\),如果存在某个\(x>2^t\),我们对\(s\)进行修改,如果此时\(2^t>\lceil \frac{s'}{k}\rceil,t\rightarrow t-1\),直到\(t=1\)或\(s\le0\)
复杂度\(O(n\log n\log V),V\)为值域
例题:U676773 污染
题意简述:
存在一个\(n\)个节点的有根树,每个节点属于一个集合\(S\),对于每个集合,有一个阈值\(k\),每次对整个子树内的所有节点加某个值,问每次操作后有多少个集合的权值和大于阈值
\(\text{Solution}:\)
因为是对子树进行操作,显然的可以想到用\(dfs\)序将子树问题转为区间问题
先考虑如果每个集合中都只有两个元素,此时我们使用折半警报器,将阈值设为\(\frac s 2\),每次区间加操作改为区间减,维护区间最小值,当最小值$ \le 0\(时,我们取出两个元素,计算其污染值之和,设其为\)k\(,我们更新\)S\rightarrow S-k=S'\(,将两点阈值重置为\)\frac {S'} 2$
对于每次取出两个点的复杂度是\(O(\log n)\)的,对于每个集合,每次操作至少会让\(S\rightarrow \frac S 2(\)因为警报器触发时一定有一个\(\frac S 2\)被归\(0)\)所以整体复杂度为 \(O(k\log S\log n)\)
现在每个集合有\(t\)个元素,我们考虑延续上方的做法,将阈值设为\(\frac S t\),每次修改复杂度变为\(O(t\log n)\),每次\(S\rightarrow \frac {t-1} t S\),时间复杂度\(O(\sum t\log_{1-\frac 1 t}S\log n)\)近似于\(O(n^2\log S\log n)\),显然无法接受(甚至不如\(O(qn)\)的暴力)
我们考虑优化,我们发现复杂度瓶颈在于\(\frac S t\)比较小,所以警报器的触发非常频繁,对\(S\)的减小幅度也很小,而且每次警报器触发都要\(O(t\log n)\)的处理所有点,我们可以想到,如果将阈值减小,每次如果\(\frac {S'} t\)仍大于阈值,我们就不必取出所有点,只更新\(S\)并重置该点的阈值,当\(\frac{S'} t\)小于阈值再取出所有点修改即可
我们设阈值序列为
\(
d_0 > d_1 > d_2 > \cdots > d_m = 1
\)
其中 \(d_0\) 是初始阈值,\(d_j\) 是第\(j\)次全组扫描后更新的阈值。我们要分析该小组的总时间复杂度,再对所有小组求和。
\(\bold{1. 单个小组的代价}\)
记该小组有 \(k\) 个监测点,初始剩余阈值 \(r_j=s\)。
阶段 \(j\):阈值为 \(d_j\)
进入该阶段时,剩余阈值为 \(r_j\),满足
\(
k\,d_j \le r_j < k\,d_{j-1}
\)
因为上一次切换时,\(d_{j-1}\) 不满足 \(k d_{j-1} \le r\),所以 \(r < k d_{j-1}\);而新阈值 \(d_j\) 必须满足 \(k d_j \le r\),否则会继续调整。
在该阶段内,每次触发至少结算 \(d_j\) 的污染。因此该阶段最多触发
次。
每次触发需要一次单点查询和单点赋值,代价 \(O(\log n)\)。
全组扫描
每次从 \(d_{j-1}\) 切换到 \(d_j\) 时,都会执行一次全组扫描,遍历该小组所有 \(k\) 个监测点,每个点做一次查询和赋值,代价 \(O(k \log n)\)。
共有 \(m\) 次切换(加上可能的初始扫描),所以全组扫描总代价\(O(m \cdot k \log n)\)
单个小组总代价
触发总次数上界:
加上全组扫描的 \(m\) 次遍历,总操作次数:
\(k \left( \sum_{j=1}^{m} \frac{d_{j-1}}{d_j} + m \right)\)
每次操作 \(O(\log n)\),所以单个小组总时间:
2. 所有小组求和
设共有\(G\)个小组,第 \(i\) 个小组有 \(k_i\) 个监测点,阈值序列长度为 \(m_i\)。则总时间(不含工厂区间加)为:
再加上\(q\)次工厂区间加,每次 \(O(\log n)\):
3. 结论
对于任意阈值序列 \((d_0, d_1, \dots, d_m=1)\),总时间复杂度为
当我们取\(d_i=2^i\)时,时间复杂度为
\(\text{Code:}\)
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=3e5+7;
const int mod=998244353;
const int INF=1e18;
int c,n,m,q,a[N],s[N],dfn[N],ap[N],tot,ed[N];
int base[N],cnt[N],ans,tp[N];
bool BJ[N];
vector<int> e[N],ele[N];
void dfs(int x,int ff){
dfn[x]=++tot;ed[x]=tot;
ap[tot]=a[x];
for(auto y:e[x]){
if(y==ff) continue;
dfs(y,x);
ed[x]=max(ed[x],ed[y]);
}
}
int mp(int a,int b){
if(b==0) return 1;
int sum=mp(a,b>>1);
sum=sum*sum%mod;
if(b&1) sum=sum*a%mod;
return sum;
}
int get(int a,int b){
if(b==0) return 1;int k=(a+b-1)/b;if(k<=1) return 1;
int id=upper_bound(base,base+63,k)-base;
return base[id-1];
}
struct node{int l,r,mi,tag;}t[N<<2];
struct Segment{
void pushup(int u){t[u].mi=min(t[u<<1].mi,t[u<<1|1].mi);}
void Sakura(int u,int p){
t[u].mi+=p;
t[u].tag+=p;
}
void pushdown(int u){
if(t[u].tag){
Sakura(u<<1,t[u].tag);
Sakura(u<<1|1,t[u].tag);
t[u].tag=0;
}
}
void build(int u,int l,int r){
t[u].l=l,t[u].r=r;
if(l==r){
int k=ap[l];
t[u].mi=tp[k];
return;
}int mid=(l+r)>>1;
build(u<<1,l,mid);build(u<<1|1,mid+1,r);
pushup(u);
}
void update(int u,int l,int r,int k){
if(t[u].l>=l&&t[u].r<=r){
Sakura(u,k);
return ;
}pushdown(u);
if(t[u<<1].r>=l) update(u<<1,l,r,k);
if(t[u<<1|1].l<=r) update(u<<1|1,l,r,k);
pushup(u);
}
void sub(int u,int pos,int x){
if(t[u].l==t[u].r){
t[u].mi=x;
return ;
}pushdown(u);
if(t[u<<1].r>=pos) sub(u<<1,pos,x);
else sub(u<<1|1,pos,x);
pushup(u);
}
int query(int u, int pos) {
if(t[u].l==t[u].r) return t[u].mi;
pushdown(u);
if(pos<=t[u<<1].r) return query(u<<1,pos);
else return query(u<<1|1,pos);
}
int find(int u) {
if(t[u].l==t[u].r) return u;
pushdown(u);
if(t[u<<1].mi<=0) return find(u<<1);
else return find(u<<1|1);
}
}T;
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
base[0]=1;for(int i=1;i<=62;i++) base[i]=base[i-1]*2;
cin>>c>>n>>m>>q;
for(int i=2;i<=n;i++){
int f;cin>>f;
e[i].push_back(f);
e[f].push_back(i);
}
for(int i=1;i<=n;i++){cin>>a[i];cnt[a[i]]++;}
for(int i=1;i<=m;i++){
cin>>s[i];
tp[i]=get(s[i],cnt[i]);
}
dfs(1,0);T.build(1,1,n);
for(int i=1;i<=n;i++) ele[a[i]].push_back(dfn[i]);
while(q--){
int u,x;cin>>u>>x;
u=u^(c*ans),x=x^(c*ans);
int l=dfn[u],r=ed[u];
T.update(1,l,r,-x);
if(t[1].mi>0){
cout<<ans<<"\n";
continue;
}
while(t[1].mi<=0){
int u=T.find(1);
int pos=t[u].l,id=ap[pos];
int p=tp[id]-t[u].mi;
if(BJ[id]){
T.sub(1,pos,INF);
continue;
}
s[id]-=p;
T.sub(1,pos,tp[id]);
if(s[id]<=0){
BJ[id]=1;
for(auto k:ele[id])T.sub(1,k,INF);
ans=(ans+mp(2,id-1))%mod;
continue;
}
if(cnt[id]*tp[id]>s[id]){
for(auto k:ele[id]){
int d=T.query(1,k),p=tp[id]-d;
s[id]-=p;
}
if(s[id]<=0){
BJ[id]=1;
for(auto k:ele[id])T.sub(1,k,INF);
ans=(ans+mp(2,id-1))%mod;
continue;
}
while(tp[id]>1&&cnt[id]*tp[id]>s[id]) tp[id]>>=1;
for(auto k:ele[id]) T.sub(1,k,tp[id]);
}
}cout<<ans<<"\n";
}
return 0;
}
posted on 2026-10-13 11:27 Underthetwilight 阅读(7) 评论(0) 收藏 举报
浙公网安备 33010602011771号