数据结构专题
[NOIP2012]借教室
可以看到答案是有单调性的,若第i个可以那么第i-1个也可以,就可以二分答案,用差分维护区间加,也可以用树状数组
#include <bits/stdc++.h>
using namespace std;
#define int long long
//#define double long double
#define PII pair<int, int>
const int N = 1e6 + 55, mod = 1e9 + 7;
int n, m, sum[N];
void add(int p, int x){ //这个函数用来在树状数组中直接修改
while(p <= n) sum[p] += x, p += p & -p;
}
void range_add(int l, int r, int x){ //给区间[l, r]加上x
add(l, x), add(r + 1, -x);
}
int ask(int p){ //单点查询
int res = 0;
while(p) res += sum[p], p -= p & -p;
return res;
}
struct E{
int d, s, t;
};
void solve() {
cin >> n >> m;
vector<int> ve(n + 1);
for (int i = 1; i <= n; ++i) cin >> ve[i];
vector<E> f(m + 1);
for (int i = 1; i <= m; ++i) cin >> f[i].d >> f[i].s >> f[i].t;
int l = 0, r = m, ans;
auto check = [&] (int x) {
fill(sum, sum + n + 5, 0);
for (int i = 1; i <= x; ++i) {
range_add(f[i].s, f[i].t, f[i].d);
}
for (int i = 1; i <= n; ++i) {
int c = ask(i);
if (c > ve[i]) return false;
}
return true;
};
// auto check = [&] (int x) {
// vector<int> d(n + 5);
// for (int i = 1; i <= x; ++i) {
// d[f[i].s] += f[i].d;
// d[f[i].t + 1] -= f[i].d;
// }
// for (int i = 1; i <= n; ++i) {
// d[i] += d[i - 1];
// if (d[i] > ve[i]) return false;
// }
// return true;
// };
while (l <= r) {
int mid = l + r >> 1;
if (check(mid)) ans = mid, l = mid + 1;
else r = mid - 1;
}
if (ans >= m) cout << 0;
else cout << -1 << '\n' << ans + 1;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
int T = 1;
while (T--) {
solve();
}
return 0;
}
[SDOI2009]HH的项链
离线+树状数组
考虑只标记在当前右边界前,所有数出现的最后位置,对于一个询问,只需要用线段树查询区间和即可。
首先把所有查询按右边界排序,根据右边界的更新去更新标记点,用pre维护当前位置的数出现的上一个位置。
每当标记一个位置的数,需要把该数出现的上一个位置的标记删掉,即它的pre删掉,这样就保证标记的数是唯一的,直接查询区间和即可
这里我自己写的也是离线,然后动态的维护查询区间的答案,通过判断每个数的个数来更新答案
#include <bits/stdc++.h>
using namespace std;
#define int long long
//#define double long double
#define PII pair<int, int>
#define PDI pair<double, int>
const int N = 2e5 + 5, mod = 1e9 + 7, M = 1e6 + 5;
void solve() {
int n;
cin >> n;
vector<int> ve(n + 1);
for (int i = 1; i <= n; ++i) cin >> ve[i];
int m;
cin >> m;
vector<pair<PII, int>> f(m);
for (int i = 0; i < m; ++i) {
cin >> f[i].first.first>> f[i].first.second;
f[i].second = i;
}
sort(f.begin(), f.end());
vector<int> cnt(M), ans(m);
int l = 1, r = 1, sum = 1;
cnt[ve[1]] = 1;
for (int i = 0; i < m; ++i) {
// cout << f[i].first << ' ' << f[i].second << '\n';
while (f[i].first.second < r) {
if (--cnt[ve[r]] == 0) sum --;
r --;
}
while (f[i].first.second > r) {
r ++;
if (++cnt[ve[r]] == 1) sum ++;
}
while (f[i].first.first > l) {
if (--cnt[ve[l]] == 0) sum --;
l ++;
}
// cout << l << ' ' << r << '\n';
ans[f[i].second] = sum;
}
for (int i = 0; i < m; ++i) cout << ans[i] << '\n';
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
int T = 1;
while (T --) {
solve();
}
return 0;
}
[HEOI2012]采花
这道题跟上面那道其实是一样的,也是离线+树状数组,只需要改下更新的条件
考虑只标记在当前右边界前的所有满足采摘条件的种类中的最后两朵,对于一个询问,也只需要查询区间和即可
首先把所有查询区间按右区间排序,根据右边界的更新去更新标记的点,用pre维护当前位置的数出现的前一个和前前一个位置。
若当前位置的数存在前一个出现的位置,则可以标记当前位置,且若前一个位置也满足这个情况,则同时把前一个位置的标记删掉,这样保证了标记是唯一的,可以直接查询区间和得到答案
#include <bits/stdc++.h>
using namespace std;
#define int long long
//#define double long double
#define PII pair<int, int>
#define PDI pair<double, int>
const int N = 2e5 + 5, mod = 1e9 + 7, M = 1e6 + 5;
const int MAXN = 2e6 + 5;
int a[MAXN];// 原数组
int c[MAXN];// 树状数组
int n, col, m; // 题目所给的数组 A 数据长度
int lowbit(int x)// 返回最后一位 1,即 2^k
{
return x & -x;
}
int query(int x)// 查询区间 [1, x] 的和
{
int ans = 0;
for(int i = x; i ; i -= lowbit(i))
ans += c[i];
return ans;
}
int getsum(int l, int r) {
return query(r) - query(l - 1);
}
void add(int x, int v)//修改位置 x 的值
{
for(int i = x; i < MAXN; i += lowbit(i))
c[i] += v;
}
void init()// 初始化, 这里我选用O(nlogn)的方式
{
for(int i = 1; i <= n; i ++ )
add(i, a[i]);
}
void solve() {
cin >> n >> col >> m;
vector<int> ve(n + 1);
for (int i = 1; i <= n; ++i) {
cin >> ve[i];
}
vector<pair<PII, int> > f(m);
for (int i = 0; i < m; ++i) {
cin >> f[i].first.first >> f[i].first.second;
f[i].second = i;
}
auto cmp = [] (pair<PII, int> a, pair<PII, int> b) {
if (a.first.second != b.first.second) return a.first.second < b.first.second;
return a.first.first < b.first.first;
};
sort(f.begin(), f.end(), cmp);
vector<int> ans(m);
vector<array<int, 2>> pos(M, {-1, -1});
int now = 1;
for (int i = 0; i < m; ++i) {
while (now <= f[i].first.second) {
int val = ve[now];
if (pos[val][0] != -1) add(pos[val][0], -1);
pos[val][0] = pos[val][1], pos[val][1] = now;
if (pos[val][0] != -1) add(pos[val][0], 1);
now++;
}
ans[f[i].second] = getsum(f[i].first.first, f[i].first.second);
}
for (int i = 0; i < m; ++i) cout << ans[i] << '\n';
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
int T = 1;
while (T --) {
solve();
}
return 0;
}
树状数组基础模板
单点修改+区间查询
int n;
int a[N],c[N]; //对应原数组和树状数组
int lowbit(int x){
return x&(-x);
}
void add(int i,int k){ //在i位置加上k
while(i <= n){
c[i] += k;
i += lowbit(i);
}
}
int getsum(int i){ //求A[1 ~ i]的和
int res = 0;
while(i > 0){
res += c[i];
i -= lowbit(i);
}
return res;
}
int get(int x, int y) { //求A[x ~ y]的和
return getsum(y) - getsum(x - 1);
}
区间更新+单点查询
int n, m;
int a[N] = {0}, c[N]; //对应原数组和树状数组
int lowbit(int x) {
return x & (-x);
}
void add(int i, int k) { //在i位置加上k
while (i <= n) {
c[i] += k;
i += lowbit(i);
}
}
int getsum(int i) { //求D[1 - i]的和,即A[i]值
int res = 0;
while (i > 0) {
res += c[i];
i -= lowbit(i);
}
return res;
}
// A[x ~ y] 加k
void add1(int x, int y, int k) {
add(x, k), add(y + 1, -k);
}
void work() {
for (int i = 1; i <= n; ++i) {
add(i, a[i] - a[i - 1]); // 维护差分
}
}
区间加+区间查询
int n, m;
int a[N] = {0};
int sum1[N]; //(D[1] + D[2] + ... + D[n])
int sum2[N]; //(1*D[1] + 2*D[2] + ... + n*D[n])
int lowbit(int x) {
return x & (-x);
}
void add(int i, int k) {
int x = i; //因为x不变,所以得先保存i值
while (i <= n) {
sum1[i] += k;
sum2[i] += k * (x - 1);
i += lowbit(i);
}
}
int getsum(int i) { //求前缀和A[1 ~ i]
int res = 0, x = i;
while (i > 0) {
res += x * sum1[i] - sum2[i];
i -= lowbit(i);
}
return res;
}
void work() {
for (int i = 1; i <= n; ++i) {
add(i, a[i] - a[i - 1]);
}
}
// [x, y]区间加k
void add1(int x, int y, int k) {
add(x, k), add(y + 1, -k);
}
// 求[x, y]区间和
int get(int x, int y) {
return getsum(y) - getsum(x - 1);
}
数据结构
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define PII pair<int, int>
//#define double long double
const int N = 1e5 + 5, mod = 1e9 + 7;
struct Node {
int a, b, sum, mul2;
}tree[N];
int val[N];
int n, m;
void build(int root, int l, int r) {
if (l == r) {
tree[root].a = 1, tree[root].b = 0, tree[root].sum = val[l], tree[root].mul2 = val[l] * val[l];
return ;
}
int mid = l + r >> 1;
build(root << 1, l, mid), build(root << 1 | 1, mid + 1, r);
tree[root].a = 1, tree[root].b = 0;
tree[root].sum = tree[root << 1].sum + tree[root << 1 | 1].sum,
tree[root].mul2 = tree[root << 1].mul2 + tree[root << 1 | 1].mul2;
}
void updata(int p, int l, int r) {
if (tree[p].a == 1 && tree[p].b == 0) return ;
int mid = l + r >> 1, lson = p << 1, rson = p << 1 | 1;
//sum,mul2
tree[lson].mul2 = tree[p].a * tree[p].a * tree[lson].mul2 + (mid - l + 1) * tree[p].b * tree[p].b + 2 * tree[p].a * tree[p].b * tree[lson].sum;
tree[rson].mul2 = tree[p].a * tree[p].a * tree[rson].mul2 + (r - mid) * tree[p].b * tree[p].b + 2 * tree[p].a * tree[p].b * tree[rson].sum;
tree[lson].sum = tree[p].a * tree[lson].sum + (mid - l + 1) * tree[p].b;
tree[rson].sum = tree[p].a * tree[rson].sum + (r - mid) * tree[p].b;
//lazy
tree[lson].b = tree[p].a * tree[lson].b + tree[p].b, tree[rson].b = tree[p].a * tree[rson].b + tree[p].b;
tree[lson].a = tree[p].a * tree[lson].a, tree[rson].a = tree[p].a * tree[rson].a;
tree[p].a = 1, tree[p].b = 0;
}
void change(int p, int l, int r, int al, int ar, int x, int op) {
int mid = l + r >> 1;
if (l == al && r == ar) {
if (op == 3) {
// ?
tree[p].mul2 = tree[p].mul2 * x * x;
tree[p].sum = tree[p].sum * x;
tree[p].a *= x, tree[p].b *= x;
} else {
tree[p].mul2 += 2 * x * tree[p].sum + (r - l + 1) * x * x;
tree[p].sum = tree[p].sum + (r - l + 1) * x;
tree[p].b += x;
}
return ;
}
updata(p, l, r);
if (mid >= ar) change( p << 1, l, mid, al, ar, x, op);
else if (al > mid) change (p << 1 | 1, mid + 1, r, al, ar, x, op);
else {
change(p << 1, l, mid, al, mid, x, op);
change(p << 1 | 1, mid + 1, r , mid + 1, ar, x, op);
}
tree[p].sum = tree[p << 1].sum + tree[p << 1 | 1].sum;
tree[p].mul2 = tree[p << 1].mul2 + tree[p << 1 | 1].mul2;
}
int getsum(int p, int l, int r, int al, int ar, int op) {
int mid = l + r >> 1;
if (l == al && r == ar) {
if (op == 1) return tree[p].sum;
else return tree[p].mul2;
}
updata(p, l, r);
if (mid >= ar) return getsum(p << 1, l, mid, al, ar, op);
else if (al > mid) return getsum(p << 1 | 1, mid + 1, r, al, ar, op);
else return getsum(p << 1, l, mid, al, mid, op) + getsum(p << 1 | 1, mid + 1, r, mid + 1, ar, op);
}
void solve() {
cin >> n >> m;
for (int i = 1; i <= n; ++i) cin >> val[i];
build(1, 1, n);
for (int i = 0; i < m; ++i) {
int op, l, r, x;
cin >> op;
if (op == 1 || op == 2) {
cin >> l >> r;
cout << getsum(1, 1, n, l, r, op) << '\n';
} else {
cin >> l >> r >> x;
change(1, 1, n, l, r, x, op);
}
}
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
int T = 1;
while (T --) {
solve();
}
return 0;
}
标准线段树板子
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define maxn 10005
#define lson l,mid,root<<1
#define rson mid+1,r,root<<1|1
long long n,m,tree[maxn<<2][2],add1[maxn<<2],add2[maxn<<2];
void pushup(long long root)
{
tree[root][0]=tree[root<<1][0]+tree[root<<1|1][0];
tree[root][1]=tree[root<<1][1]+tree[root<<1|1][1];
}
void pushdown1(long long l,long long r,long long root)
{ //加法下推
if(add1[root])
{
add1[root<<1]+=add1[root]; //+号不能少,切记
add1[root<<1|1]+=add1[root];
long long x=tree[root<<1][0];
long long y=tree[root<<1|1][0];
tree[root<<1][0]+=l*add1[root];
tree[root<<1|1][0]+=r*add1[root];
tree[root<<1][1]+=2*x*add1[root]+l*add1[root]*add1[root];
tree[root<<1|1][1]+=2*y*add1[root]+r*add1[root]*add1[root];
add1[root]=0;
}
}
void pushdown2(long long l,long long r,long long root)
{ //乘法下推
if(add2[root]!=1)
{
add2[root<<1]*=add2[root]; //*号不能少,切记
add2[root<<1|1]*=add2[root];
tree[root<<1][0]*=add2[root];
tree[root<<1|1][0]*=add2[root];
tree[root<<1][1]*=add2[root]*add2[root];
tree[root<<1|1][1]*=add2[root]*add2[root];
add2[root]=1;
}
}
void build(long long l,long long r,long long root)
{
add1[root]=0;
add2[root]=1;
if(l==r)
{
scanf("%lld",&tree[root][0]);
tree[root][1]=tree[root][0]*tree[root][0];
return;
}
long long mid=(l+r)>>1;
build(lson);
build(rson);
pushup(root);
}
void update1(long long L,long long R,long long C,long long l,long long r,long long root)
{ //加法更新
if(L<=l&&r<=R)
{
long long x=tree[root][0];
tree[root][0] += (r-l+1)*C;
tree[root][1] += 2*x*C+(r-l+1)*C*C;
add1[root] += C;
return ;
}
long long mid = (l+r)/2;
pushdown2(mid-l+1,r-mid,root); pushdown1(mid-l+1,r-mid,root);
if(L<=mid)
update1(L,R,C,lson);
if(R>mid)
update1(L,R,C,rson);
pushup(root);
}
void update2(long long L,long long R,long long C,long long l,long long r,long long root)
{ //乘法更新
if(L<=l&&r<=R)
{
tree[root][0] *= C;
tree[root][1] *= C*C;
add2[root] *= C; if(add1[root]) add1[root] *= C;
return ;
}
long long mid = (l+r)/2;
pushdown2(mid-l+1,r-mid,root); pushdown1(mid-l+1,r-mid,root);
if(L<=mid)
update2(L,R,C,lson);
if(R>mid)
update2(L,R,C,rson);
pushup(root);
}
long long query(long long L,long long R,long long c,long long l,long long r,long long root)
{
if(L<=l&&R>=r)
{
if(c==1)
return tree[root][0];
if(c==2)
return tree[root][1];
}
long long ans=0,mid=(l+r)>>1;
pushdown2(mid-l+1,r-mid,root); pushdown1(mid-l+1,r-mid,root);
if(L<=mid)
ans+=query(L,R,c,lson);
if(R>mid)
ans+=query(L,R,c,rson);
return ans;
}
int main()
{
cin>>n>>m;
build(1,n,1);
while(m--)
{
long long op,x,y,val;
scanf("%lld",&op);
if(op==1||op==2)
{
scanf("%lld%lld",&x,&y);
printf("%lld\n",query(x,y,op,1,n,1));
}
else if(op==3||op==4)
{
scanf("%lld%lld%lld",&x,&y,&val);
if(op==3)
update2(x,y,val,1,n,1);
else
update1(x,y,val,1,n,1);
}
}
return 0;
}
线段树
#include <bits/stdc++.h>
using namespace std;
#define int long long
//#define double long double
const int N = 1e5 + 5;
int mod, A[N];
struct tnode {
int sum[2], lazy[2];
int l, r;
};
tnode operator + (const tnode &A, const tnode &B) {
tnode C;
C.l = A.l, C.r = B.r;
C.lazy[0] = 1, C.lazy[1] = 0;
C.sum[0] = (A.sum[0] + B.sum[0]) % mod;
C.sum[1] = (A.sum[1] + B.sum[1]) % mod;
C.sum[1] = (C.sum[1] + A.sum[0] * B.sum[0] % mod) % mod;
return C;
}
struct Segmen_Tree {
tnode t[4 * N];
void init_lazy(int root) {
t[root].lazy[0] = 1, t[root].lazy[1] = 0;
}
void union_lazy(int fa, int ch) {
int temp[2];
//a = fa.a * ch.a
temp[0] = t[fa].lazy[0] * t[ch].lazy[0] % mod;
//b = fa.a * ch.b + fa.b
temp[1] = ((t[fa].lazy[0] * t[ch].lazy[1] % mod) + t[fa].lazy[1]) % mod;
t[ch].lazy[0] = temp[0];
t[ch].lazy[1] = temp[1];
}
void cal_lazy(int root) {
int len = (t[root].r - t[root].l + 1) % mod;
//len * (len - 1) / 2 * b * b + a * a * sum[1] + a * b * (len - 1) * sum[0]
t[root].sum[1] = (len * (len - 1) / 2 % mod * t[root].lazy[1] % mod * t[root].lazy[1] % mod +
t[root].lazy[0] * t[root].lazy[0] % mod * t[root].sum[1] % mod +
t[root].lazy[0] * t[root].lazy[1] % mod * (len - 1) % mod * t[root].sum[0] % mod) % mod;
// len * b + a * sum[0]
t[root].sum[0] = (len * t[root].lazy[1] % mod +
t[root].lazy[0] * t[root].sum[0] % mod) % mod;
return;
}
void push_down(int root) {
if (t[root].lazy[0] != 1 || t[root].lazy[1] != 0)
{
cal_lazy(root);
if (t[root].l != t[root].r)
{
int ch = root << 1;
union_lazy(root, ch);
union_lazy(root, ch + 1);
}
init_lazy(root);
}
}
void update(int root) {
int ch = root << 1;
push_down(ch);
push_down(ch + 1);
t[root] = t[ch] + t[ch + 1];
}
void build(int root, int l, int r) {
t[root].l = l;
t[root].r = r;
init_lazy(root);
if (l != r)
{
int mid = (l + r) >> 1;
int ch = root << 1;
build(ch, l, mid);
build(ch + 1, mid + 1, r);
update(root);
}
else
{
t[root].sum[0] = A[l] % mod;
t[root].sum[1] = 0;
}
}
void change(int root, int l, int r, int delta, int op) {
push_down(root);
if (l == t[root].l && r == t[root].r)
{
t[root].lazy[op] = delta % mod;
return;
}
int mid = (t[root].l + t[root].r) >> 1;
int ch = root << 1;
if (r <= mid)change(ch, l, r, delta, op);
else if (l > mid)change(ch + 1, l, r, delta, op);
else {change(ch, l, mid, delta, op); change(ch + 1, mid + 1, r, delta, op);}
update(root);
}
tnode sum(int root, int l, int r) {
push_down(root);
if (t[root].l == l && t[root].r == r)
{
return t[root];
}
int mid = (t[root].l + t[root].r) >> 1;
int ch = root << 1;
if (r <= mid)return sum(ch, l, r);
else if (l > mid)return sum(ch + 1, l, r);
else return sum(ch, l, mid) + sum(ch + 1, mid + 1, r);
}
};
Segmen_Tree ST;
int n, m, op, l, r, x;
void solve() {
cin >> n >> m >> mod;
for (int i = 1; i <= n; ++i) cin >> A[i];
ST.build(1, 1, n);
for (int i = 0; i < m; ++i) {
cin >> op >> l >> r;
if (op <= 2) {
cin >> x;
ST.change(1, l, r, x, 2 - op);
} else {
cout << ST.sum(1, l, r).sum[1] << '\n';
}
}
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
int T = 1;
cin >> T;
while (T --) {
solve();
}
return 0;
}
区间最值
struct node {
int l;
int r;
int sum;
} tree[4 * N];
void build(int i, int l, int r) {
tree[i].l = l, tree[i].r = r;
if (l == r) {
tree[i].sum = num[l];
return;
}
int mid = (l + r) / 2;
build(i * 2, l, mid);
build(i * 2 + 1, mid + 1, r);
tree[i].sum = min(tree[i * 2].sum, tree[i * 2 + 1].sum);//存储子树中最小的那个
return;
}
int find(int i, int l, int r) {
if (tree[i].l >= l && tree[i].r <= r) {
return tree[i].sum;
}
int mid = (tree[i].l + tree[i].r) / 2;
int mi = LLONG_MAX;
if (l <= mid) mi = min(mi, find(2 * i, l, r));
if (r > mid) mi = min(mi, find(2 * i + 1, l, r));
return mi;
}