1 条题解
-
2
首先断环为链。
下文中 表示联通之后的军事基地的战斗力的最大值。
考虑给定 和 的轰炸机是否可以完美轰炸 次的本质是判断 $\sum\limits_{i=1}^n{\operatorname{min}(\left\lfloor\dfrac{a_i}{k}\right\rfloor,c)} \geq b \times c$。 原因是因为一共轰炸 次,一个军事基地最多被轰炸 次,而若 ,则代表每次都可以选择轰炸它。否则对剩余的 的军事基地轮流轰炸,即求和之后除以 下取整,最后加起来判断是否能否有大于等于 个军事基地被一同选中。
- 特殊性质
即每次询问一个 ,求 $\sum\limits_{i=1}^n{\operatorname{min}(\left\lfloor\dfrac{a_i}{v}\right\rfloor,c)}$。 考虑对 数组排序,设定一个阈值 ,当 时,预处理计算。当 时,枚举 用树状数组统计。再特判一下 的数的个数即可。取 ,则复杂度为 。
- 特殊性质
即给定一个长度为 的数组 , 次操作。
-
表示将 。
-
表示求 $\sum\limits_{i=1}^n{\operatorname{min}(\left\lfloor\dfrac{a_i}{v}\right\rfloor,c)}$。 。
考虑对于 的每个 建立一个树状数组直接维护,预处理复杂度 ,单次询问复杂度 ,单次修改复杂度 。
当 时与特殊性质 的方法一样做即可,预处理复杂度 ,单次询问复杂度 ,单次修改复杂度 。
取 ,则复杂度为 。
对于 ,暴力判断目前这个区间是否被其他区间覆盖。
对于 ,用特殊性质 的方法暴力做即可。
- 正解
重点是要维护军事基地间的连通性,然后并查集记录连通块大小即可用特殊性质 的方法直接做了。
考虑维护一个连通块中每个元素连接到下一个同连通块内的点。
设 为 号点上方的边数。设我们要合并 和 ()。
若 ,不妨设 ,则我们向右找到第一个位置 满足 ,因为 ,且此时 所在的连通块必定被 所在的连通块包含,故直接合并 与 。反之同理。
若 ,依旧找到 ,若 则如上操作,否则代表 与 之间没有其他连通块阻碍了,直接连接即可。
寻找 可以直接线段树上二分。
而合并连通块时分类讨论:
首先定义 表示 所在连通块最左边的位置, 同理。这个直接用并查集维护。
若 与 所在连通块无包含关系,则直接给区间 的 值加一。
否则需要断掉 这条边,加上 和 这两条边,同样线段树上区间加法维护。
这部分总复杂度 。
然后维护一下连通块的 之和直接继续做即可。
总复杂度 $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; }
信息
- ID
- 6
- 时间
- 2000ms
- 内存
- 512MiB
- 难度
- 9
- 标签
- 递交数
- 21
- 已通过
- 3
- 上传者