线段树
强大好用的数据结构!
Top:线段树尽量保持一种写法,不要再写错了!!!11
P5670
prob:
1.区间加
2.区间异或和后 m 位 (\(m \le 10\))
3.n 1e5
sol:
用 bitset 维护线段树区间。
具体的,开 1024 位 bitset 表示每种数对异或贡献为 0/1,有区间可加性。
时间复杂度 \(O(\frac{n \log n 2^m}{w})\),但是这题半秒,很卡常,需要优化。。(感觉甚至不如暴力/lh)
发现区间较小时 bitset 算确实不如暴力优,令长度小于阈值的区间暴力算即可。
注意:
1.暴力算的时候要把当前节点的 tag 加上。
2.不要习惯性的把循环里所有东西都写成 i !!!(modify 里那个 p)
namespace sgt {
#define ls(p) p<<1
#define rs(p) p<<1|1
const int M = 1<<10;
const int B = 1<<7; // bf range
bitset <M> b[N<<2];
int ad[N<<2];
void pushup(int p) {
b[p] = b[ls(p)]^b[rs(p)];
}
void upd(int p, int k) {
k &= M-1;
b[p] = b[p]<<k | b[p]>>M-k;
ad[p] += k;
}
void pushdown(int p) {
if(!ad[p]) return ;
upd(ls(p), ad[p]), upd(rs(p), ad[p]);
ad[p] = 0;
}
void build(int p, int l, int r) {
if(r-l+1 <= B) {
rep(i, l, r) b[p].flip(a[i]);
return ;
}
int mid = l+r>>1;
build(ls(p), l, mid), build(rs(p), mid+1, r);
pushup(p);
}
void modify(int p, int l, int r, int x, int y, int k) {
if(r-l+1 <= B) {
int lb = max(l, x), rb = min(r, y);
rep(i, lb, rb) b[p].flip(a[i]+ad[p] & M-1);
rep(i, lb, rb) a[i] += k;
rep(i, lb, rb) b[p].flip(a[i]+ad[p] & M-1);
return ;
}
if(l >= x && r <= y) return upd(p, k);
pushdown(p);
int mid = l+r>>1;
if(x <= mid) modify(ls(p), l, mid, x, y, k);
if(y > mid) modify(rs(p), mid+1, r, x, y, k);
pushup(p);
}
bitset <M> ask(int p, int l, int r, int x, int y) {
bitset <M> s;
if(r-l+1 <= B) {
int lb = max(l, x), rb = min(r, y);
rep(i, lb, rb) s.flip(a[i]+ad[p] & M-1);
return s;
}
if(l >= x && r <= y) return b[p];
pushdown(p);
int mid = l+r>>1;
if(x <= mid) s ^= ask(ls(p), l, mid, x, y);
if(y > mid) s ^= ask(rs(p), mid+1, r, x, y);
return s;
}
}
总结:bitset 太神秘!
ABC356F
唐题
2 log 无脑暴力线段树
先离散化,维护区间中数的总个数和联通块个数,顺便记录左右端点合并即可。
查询时直接二分联通块左右端点(若查询区间联通块数量为 1 则合法),注意最终答案要减掉自己重复算的一次。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
typedef long double ldb;
typedef pair<int, int> pii;
#define pb push_back
#define pc putchar
#define sp pc(' ')
#define et pc('\n')
#define debug cerr<<"--ERROR--\n"
#define rep(i, a, b) for(int i = (a); i <= (b); ++i)
#define per(i, a, b) for(int i = (a); i >= (b); --i)
#define mem(a, x) memset(a, x, sizeof(a))
#define in(a, n) rep(i, 1, n) a[i]=rd()
#define all(x) x.begin(), x.end()
inline ll rd(){ll x=0; int f=1; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1; c=getchar();} while(c>='0'&&c<='9'){x=x*10+c-48; c=getchar();} return x*f;}
inline void wr(ll x){if(x<0) putchar('-'), x=-x; static int stk[35]; int tp=0; do stk[tp++]=x%10;while(x/=10); do pc(stk[--tp]^48);while(tp);}
//const ldb eps = 1e-9;
const ll INF = 0x3f3f3f3f3f3f3f3f;
//const int mo = 1e9+7;
const int N = 2e5+3;
int n, m, op[N], vis[N];
ll k, a[N], x[N];
struct node {
ll l, r;
int tot, cnt;
// leftest number, rightest number, total number, count of intervals
};
node operator + (const node &a, const node &b) {
if(!a.tot) return b;
if(!b.tot) return a;
ll l = min(a.l, b.l), r = max(a.r, b.r);
int s = a.tot+b.tot, c = a.cnt+b.cnt - (max(a.l, b.l)-min(a.r, b.r) <= k);
return {l, r, s, c};
}
namespace sgt {
#define ls(p) p<<1
#define rs(p) p<<1|1
node t[N<<2];
void pushup(int p) {
t[p] = t[ls(p)]+t[rs(p)];
}
void modify(int p, int l, int r, int x, int k) {
if(l == r) {
t[p].l = t[p].r = a[x];
t[p].tot += k, t[p].cnt += k;
return ;
}
int mid = l+r>>1;
if(x <= mid) modify(ls(p), l, mid, x, k);
else modify(rs(p), mid+1, r, x, k);
pushup(p);
}
node ask(int p, int l, int r, int x, int y) {
if(l >= x && r <= y) return t[p];
int mid = l+r>>1;
if(y <= mid) return ask(ls(p), l, mid, x, y);
if(x > mid) return ask(rs(p), mid+1, r, x, y);
return ask(ls(p), l, mid, x, y) + ask(rs(p), mid+1, r, x, y);
}
}
using sgt::modify;
using sgt::ask;
void clear() { }
int findl(int x) {
int l=1, r=x, s=1;
node t;
while(l <= r) {
int mid = l+r>>1;
if((t=ask(1, 1, n, mid, x)).cnt == 1) r = mid-1, s = t.tot;
else l = mid+1;
}
return s;
}
int findr(int x) {
int l=x, r=n, s=1;
node t;
while(l <= r) {
int mid = l+r>>1;
if((t=ask(1, 1, n, x, mid)).cnt == 1) l = mid+1, s = t.tot;
else r = mid-1;
}
return s;
}
signed main() {
// freopen(".in", "r", stdin);
// freopen(".out", "w", stdout);
int TT = 1;
//TT = rd();
while(TT--)
{
clear();
cin >> m >> k;
rep(i, 1, m) {
op[i]=rd(), x[i]=a[i]=rd();
}
sort(a+1, a+m+1);
n = unique(a+1, a+m+1) - a-1;
mem(vis, -1);
rep(i, 1, m) {
x[i] = lower_bound(a+1, a+n+1, x[i]) - a;
if(op[i] == 1) {
vis[x[i]] = -vis[x[i]];
modify(1, 1, n, x[i], vis[x[i]]);
}
else {
wr(findr(x[i]) + findl(x[i]) - 1), et;
}
}
et;
}
return 0;
}
讲个笑话,把 findl 当成 1 可以过样例和前面的点,别问我怎么知道的。这次终于不是线段树写错了!!(AT random数据是真的水)

浙公网安备 33010602011771号