1 条题解

  • 2
    @ 2025-11-17 18:54:47

    首先断环为链。

    下文中 AA 表示联通之后的军事基地的战斗力的最大值。

    考虑给定 kkbb 的轰炸机是否可以完美轰炸 cc 次的本质是判断 $\sum\limits_{i=1}^n{\operatorname{min}(\left\lfloor\dfrac{a_i}{k}\right\rfloor,c)} \geq b \times c$。 原因是因为一共轰炸 cc 次,一个军事基地最多被轰炸 aik\left\lfloor\dfrac{a_i}{k}\right\rfloor 次,而若 aikc\left\lfloor\dfrac{a_i}{k}\right\rfloor \geq c,则代表每次都可以选择轰炸它。否则对剩余的 aik<c\left\lfloor\dfrac{a_i}{k}\right\rfloor < c 的军事基地轮流轰炸,即求和之后除以 cc 下取整,最后加起来判断是否能否有大于等于 bb 个军事基地被一同选中。

    • 特殊性质 AA

    即每次询问一个 vv,求 $\sum\limits_{i=1}^n{\operatorname{min}(\left\lfloor\dfrac{a_i}{v}\right\rfloor,c)}$。 考虑对 aa 数组排序,设定一个阈值 BB,当 vBv \leq B 时,预处理计算。当 v>Bv > B 时,枚举 aiv\left\lfloor\dfrac{a_i}{v}\right\rfloor 用树状数组统计。再特判一下 aiv×ca_i \geq v \times c 的数的个数即可。取 B=AB=\sqrt{A},则复杂度为 O((1+logn)nA)O((1+\log n)n\sqrt{A})

    • 特殊性质 BB

    即给定一个长度为 nn 的数组 aaqq 次操作。

    • 1 u v1\ u\ v 表示将 auau+av,av0a_u \gets a_u+a_v,a_v \gets 0

    • 2 k v2\ k\ v 表示求 $\sum\limits_{i=1}^n{\operatorname{min}(\left\lfloor\dfrac{a_i}{v}\right\rfloor,c)}$。 1n,q,ai1051 \leq n,q,a_i \leq 10^5

    考虑对于 vBv \leq B 的每个 vv 建立一个树状数组直接维护,预处理复杂度 O(AB)O(AB),单次询问复杂度 O(logn)O(\log n),单次修改复杂度 O(Blogn)O(B \log n)

    v>Bv > B 时与特殊性质 AA 的方法一样做即可,预处理复杂度 O(n)O(n),单次询问复杂度 O(ABlogn)O(\frac{A}{B}\log n),单次修改复杂度 O(logn)O(\log n)

    B=AB=\sqrt{A},则复杂度为 O(nlognA)O(n \log n \sqrt{A})

    • n<=1000n<=1000

    对于 op=1op=1,暴力判断目前这个区间是否被其他区间覆盖。

    对于 op=2op=2,用特殊性质 BB 的方法暴力做即可。

    • 正解

    重点是要维护军事基地间的连通性,然后并查集记录连通块大小即可用特殊性质 BB 的方法直接做了。

    考虑维护一个连通块中每个元素连接到下一个同连通块内的点。

    pip_iii 号点上方的边数。设我们要合并 xxyy (xyx \leq y)。

    pxpyp_x \neq p_y,不妨设 px>pyp_x > p_y,则我们向右找到第一个位置 pospos 满足 px>pposp_x > p_{pos},因为 posypos \leq y,且此时 xx 所在的连通块必定被 pospos 所在的连通块包含,故直接合并 xxpospos。反之同理。

    px=pyp_x=p_y,依旧找到 pospos,若 pos<ypos < y 则如上操作,否则代表 xxyy 之间没有其他连通块阻碍了,直接连接即可。

    寻找 pp 可以直接线段树上二分。

    而合并连通块时分类讨论:

    首先定义 lxl_x 表示 xx 所在连通块最左边的位置, rxr_x 同理。这个直接用并查集维护。

    xxyy 所在连通块无包含关系,则直接给区间 (rx,ly)(r_x,l_y)pp 值加一。

    否则需要断掉 (lx,rx)(l_x,r_x) 这条边,加上 (lx,ly)(l_x,l_y)(ry,rx)(r_y,r_x) 这两条边,同样线段树上区间加法维护。

    这部分总复杂度 O(nlognα(n))O(n\log n\operatorname{\alpha}(n))

    然后维护一下连通块的 aia_i 之和直接继续做即可。

    总复杂度 $O(n\log n\operatorname{\alpha}(n)+n\log n \sqrt{A}))$。

    代码:

    #include<bits/stdc++.h>
    #define int long long
    using namespace std;
    const int B = 400,A = 100000;
    int n,q,c,sid,a[100010],s[100010];
    int LSB(int i){
    	return i & (-i);
    }
    struct Bit{
    	int bit[100010];
    	void upd(int i,int k){
    		if(!i) return;
    		while(i <= A){
    			bit[i] += k;
    			i += LSB(i);
    		}
    	}
    	int psq(int i){
    		int s = 0;
    		while(i){
    			s += bit[i];
    			i -= LSB(i);
    		}
    		return s;
    	}
    }bt[B + 5];
    void modify(int pre,int v){
    	for(int i = 1; i <= B; i++){
    		bt[i].upd(pre,-pre / i);
    		bt[i].upd(v,v / i);
    	}
    	bt[B + 1].upd(pre,-1);
    	bt[B + 1].upd(v,1);
    }
    namespace DSU{
    	int fa[100010],l[100010],r[100010],sz[100010],val[100010];
    	void init(){
    		for(int i = 1; i <= n; i++) fa[i] = l[i] = r[i] = i,sz[i] = 1,val[i] = a[i];
    	}
    	int Find(int i){
    		return fa[i] == i ? i : fa[i] = Find(fa[i]);
    	}	
    	void unite(int u,int v){
    		int fu = Find(u),fv = Find(v);
    		if(fu == fv) return;
    		if(sz[fu] > sz[fv]) swap(fu,fv);
    		fa[fu] = fv;
    		l[fv] = min(l[fu],l[fv]);
    		r[fv] = max(r[fu],r[fv]);
    		modify(val[fv],val[fu] + val[fv]);
    		modify(val[fu],0);
    		val[fv] += val[fu];
    		val[fu] = 0;
    	}
    };
    using DSU::init;
    using DSU::Find;
    using DSU::unite;
    int mn[400010],tag[400010];
    #define ls(i) (i << 1)
    #define rs(i) (i << 1 | 1)
    void pushup(int i){
    	mn[i] = min(mn[ls(i)],mn[rs(i)]);
    }
    void Tag(int i,int v){
    	mn[i] += v,tag[i] += v;
    }
    void pushdown(int i){
    	if(tag[i]){
    		Tag(ls(i),tag[i]);
    		Tag(rs(i),tag[i]);
    		tag[i] = 0;
    	}
    }
    void upd(int i,int l,int r,int ql,int qr,int v){
    	if(ql <= l && r <= qr){
    		Tag(i,v);
    		return;
    	}
    	pushdown(i);
    	int mid = (l + r) >> 1;
    	if(ql <= mid) upd(ls(i),l,mid,ql,qr,v);
    	if(qr > mid) upd(rs(i),mid + 1,r,ql,qr,v);
    	pushup(i);
    }
    int query(int i,int l,int r,int pos){
    	if(l == r) return mn[i];
    	pushdown(i);
    	int mid = (l + r) >> 1;
    	if(pos <= mid) return query(ls(i),l,mid,pos);
    	else return query(rs(i),mid + 1,r,pos);
    }
    int queryl(int i,int l,int r,int pos,int v){
    	if(r <= pos){
    		if(mn[i] >= v) return 0;
    		if(l == r) return l;
    		pushdown(i);
    		int mid = (l + r) >> 1;
    		if(mn[rs(i)] < v) return queryl(rs(i),mid + 1,r,pos,v);
    		return queryl(ls(i),l,mid,pos,v);
    	}
    	pushdown(i);
    	int mid = (l + r) >> 1,res = 0;
    	if(mid + 1 <= pos) res = queryl(rs(i),mid + 1,r,pos,v);
    	if(!res) res = queryl(ls(i),l,mid,pos,v);
    	return res; 
    }
    int queryr(int i,int l,int r,int pos,int v){
    	if(pos <= l){
    		if(mn[i] >= v) return n + 1;
    		if(l == r) return l;
    		pushdown(i);
    		int mid = (l + r) >> 1;
    		if(mn[ls(i)] < v) return queryr(ls(i),l,mid,pos,v);
    		return queryr(rs(i),mid + 1,r,pos,v);
    	}
    	pushdown(i);
    	int mid = (l + r) >> 1,res = n + 1;
    	if(pos <= mid) res = queryr(ls(i),l,mid,pos,v);
    	if(res == n + 1) res = queryr(rs(i),mid + 1,r,pos,v);
    	return res;
    }
    void merge(int x,int y){
    	x = Find(x),y = Find(y);
    	if(DSU::r[x] < DSU::l[y]) upd(1,1,n,DSU::r[x] + 1,DSU::l[y] - 1,1);
    	else upd(1,1,n,max(DSU::l[x],DSU::l[y]),min(DSU::r[x],DSU::r[y]),-1);
    	unite(x,y);
    }
    signed main(){
    	scanf("%lld %lld %lld",&n,&q,&sid);
    	for(int i = 1; i <= n; i++) scanf("%lld",&a[i]);
    	for(int i = 1; i <= B; i++){
    		for(int j = 1; j <= n; j++) bt[i].bit[a[j]] += (a[j] / i);
    		for(int j = 1; j <= A; j++) s[j] = s[j - 1] + bt[i].bit[j];
    		for(int j = 1; j <= A; j++) bt[i].bit[j] = s[j] - s[j - LSB(j)];
    	}
    	for(int i = 1; i <= n; i++) bt[B + 1].bit[a[i]]++;
    	for(int i = 1; i <= A; i++) s[i] = s[i - 1] + bt[B + 1].bit[i];
    	for(int i = 1; i <= A; i++) bt[B + 1].bit[i] = s[i] - s[i - LSB(i)];
    	init();
    	int op,u,v;
    	while(q--){
    		scanf("%lld %lld %lld",&op,&u,&v);
    		if(op == 1){
    			if(u > v) swap(u,v);
    			int vu = query(1,1,n,u),vv = query(1,1,n,v);
    			while(Find(u) != Find(v)){
    				if(vu > vv) merge(u,queryr(1,1,n,u,vu)),vu--;
    				else if(vu < vv) merge(queryl(1,1,n,v,vv),v),vv--;
    				else{
    					int pos = queryr(1,1,n,u,vu);
    					if(pos < v) merge(u,pos),vu--;
    					else merge(u,v);
    				}
    			}
    		}
    		else{
    			scanf("%lld",&c);
    			swap(c,v);
    			int sum = (bt[B + 1].psq(A) - bt[B + 1].psq(u * v - 1)) * v;
    			if(u <= B) sum += bt[u].psq(u * v - 1);
    			else{
    				for(int cc = u; cc <= u * v - 1; cc += u){ //cc ~ min(u * v - 1,cc + u - 1)
    					sum += (bt[B + 1].psq(min(u * v - 1,cc + u - 1)) - bt[B + 1].psq(cc - 1)) * (cc / u);
    				}
    			}
    			if(sum >= c * v) printf("Yes\n");
    			else printf("No\n");
    		}
    	}
      	return 0;
    }
    
    • 1

    信息

    ID
    6
    时间
    2000ms
    内存
    512MiB
    难度
    9
    标签
    递交数
    21
    已通过
    3
    上传者