A - 敌兵布阵
#include <algorithm>
#include <bitset>
#include <cassert>
#include <cctype>
#include <cmath>
#include <complex>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <deque>
#include <functional>
#include <iomanip>
#include <iostream>
#include <list>
#include <map>
#include <numeric>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <stdexcept>
#include <string>
#include <vector>
using namespace std;
const int N = 50010;
int abc;
int n, x, y, t;
int cnt = 1;
char s[10];
int input[N];
struct node {
int l, r, sum, lazy /*懒惰标记,下文会提到*/;
node() { l = r = sum = lazy = 0; } //给每一个元素赋初值
} tree[150000]; // N为总节点数
inline void build(int i, int l, int r) { //递归建树
tree[i].l = l;
tree[i].r = r;
if (l == r) { //如果这个节点是叶子节点
tree[i].sum = input[l];
return;
}
int mid = (l + r) >> 1;
build(i * 2, l, mid); //分别构造左子树和右子树
build(i * 2 + 1, mid + 1, r);
tree[i].sum =
tree[i * 2].sum + tree[i * 2 + 1].sum; //刚才我们发现的性质return ;
}
inline int search(int i, int l, int r) {
if (tree[i].l >= l &&
tree[i].r <=
r) //如果这个区间被完全包括在目标区间里面,直接返回这个区间的值
return tree[i].sum;
if (tree[i].r < l || tree[i].l > r)
return 0; //如果这个区间和目标区间毫不相干,返回0
int s = 0;
if (tree[i * 2].r >= l)
s += search(i * 2, l,
r); //如果这个区间的左儿子和目标区间又交集,那么搜索左儿子
if (tree[i * 2 + 1].l <= r)
s += search(i * 2 + 1, l,
r); //如果这个区间的右儿子和目标区间又交集,那么搜索右儿子
return s;
}
inline void add(int i, int dis, int k) {
if (tree[i].l == tree[i].r) { //如果是叶子节点,那么说明找到了
tree[i].sum += k;
return;
}
if (dis <= tree[i * 2].r)
add(i * 2, dis, k); //在哪往哪跑
else
add(i * 2 + 1, dis, k);
tree[i].sum = tree[i * 2].sum + tree[i * 2 + 1].sum; //返回更新
return;
}
int main() {
cin >> t;
while (t--) {
printf("Case %d:\n", cnt++);
scanf("%d", &n);
for (int i = 1; i <= n; ++i)
scanf("%d", &input[i]);
build(1, 1, n);
while (1) {
scanf("%s", s);
if (s[0] == 'E')
break;
scanf("%d%d", &x, &y);
if (s[0] == 'Q')
cout << search(1, x, y) << endl;
else if (s[0] == 'A')
add(1, x, y);
else if (s[0] == 'S')
add(1, x, -y);
}
}
return 0;
}
这里也是套用了单点修改,区间查询的模板。
B - I Hate It
#include <algorithm>
#include <bitset>
#include <cassert>
#include <cctype>
#include <cmath>
#include <complex>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <deque>
#include <functional>
#include <iomanip>
#include <iostream>
#include <list>
#include <map>
#include <numeric>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <stdexcept>
#include <string>
#include <vector>
using namespace std;
const int N = 200050;
int n, x, y, t, m;
char s[10];
int input[N];
struct node {
int l, r, sum, lazy /*懒惰标记,下文会提到*/;
node() { l = r = sum = lazy = 0; } //给每一个元素赋初值
} tree[525000]; // N为总节点数,
inline void build(int i, int l, int r) { //递归建树
tree[i].l = l;
tree[i].r = r;
if (l == r) { //如果这个节点是叶子节点
tree[i].sum = input[l];
return;
}
int mid = (l + r) >> 1;
build(i * 2, l, mid); //分别构造左子树和右子树
build(i * 2 + 1, mid + 1, r);
tree[i].sum =
max(tree[i * 2].sum, tree[i * 2 + 1].sum); //刚才我们发现的性质return ;
}
inline int search(int i, int l, int r) {
if (tree[i].l >= l &&
tree[i].r <=
r) //如果这个区间被完全包括在目标区间里面,直接返回这个区间的值
return tree[i].sum;
if (tree[i].r < l || tree[i].l > r)
return 0; //如果这个区间和目标区间毫不相干,返回0
int s = 0, s2 = 0;
if (tree[i * 2].r >= l)
s = search(i * 2, l,
r); //如果这个区间的左儿子和目标区间又交集,那么搜索左儿子
if (tree[i * 2 + 1].l <= r)
s2 = search(i * 2 + 1, l,
r); //如果这个区间的右儿子和目标区间又交集,那么搜索右儿子
return max(s, s2);
}
inline void update(int i, int dis, int k) {
if (tree[i].l == tree[i].r) { //如果是叶子节点,那么说明找到了
tree[i].sum = k;
return;
}
if (dis <= tree[i * 2].r)
update(i * 2, dis, k); //在哪往哪跑
else
update(i * 2 + 1, dis, k);
tree[i].sum = max(tree[i * 2].sum, tree[i * 2 + 1].sum);
return;
}
int main() {
while (scanf("%d%d", &n, &m) != EOF) {
for (int i = 1; i <= n; ++i)
scanf("%d", &input[i]);
build(1, 1, n);
while (m--) {
scanf("%s", s);
scanf("%d%d", &x, &y);
if (s[0] == 'U')
update(1, x, y);
else if (s[0] == 'Q')
cout << search(1, x, y) << endl;
}
}
return 0;
}
这里套用了单点修改,区间查询的模板,tree数组里面的sum在这里是表示区间最大值的意思(懒得改了),其他改动不大。
C - A Simple Problem with Integers
#include <algorithm>
#include <bitset>
#include <cassert>
#include <cctype>
#include <cmath>
#include <complex>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <deque>
#include <functional>
#include <iomanip>
#include <iostream>
#include <list>
#include <map>
#include <numeric>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <stdexcept>
#include <string>
#include <vector>
using namespace std;
typedef long long ll;
const int MAXN = 1e5 + 5;
ll tree[MAXN << 2], mark[MAXN << 2], n, m, A[MAXN];
void push_down(int p, int len) {
tree[p << 1] += mark[p] * (len - len / 2);
mark[p << 1] += mark[p];
tree[p << 1 | 1] += mark[p] * (len / 2);
mark[p << 1 | 1] += mark[p];
mark[p] = 0;
}
void build(int p = 1, int cl = 1, int cr = n) {
if (cl == cr) {
tree[p] = A[cl];
return;
}
int mid = (cl + cr) >> 1;
build(p << 1, cl, mid);
build(p << 1 | 1, mid + 1, cr);
tree[p] = tree[p << 1] + tree[p << 1 | 1];
}
ll query(int l, int r, int p = 1, int cl = 1, int cr = n) {
if (cl >= l && cr <= r)
return tree[p];
push_down(p, cr - cl + 1);
ll mid = (cl + cr) >> 1, ans = 0;
if (mid >= l)
ans += query(l, r, p << 1, cl, mid);
if (mid < r)
ans += query(l, r, p << 1 | 1, mid + 1, cr);
return ans;
}
void update(int l, int r, int d, int p = 1, int cl = 1, int cr = n) {
if (cl >= l && cr <= r) {
tree[p] += d * (cr - cl + 1), mark[p] += d;
return;
}
push_down(p, cr - cl + 1);
int mid = (cl + cr) >> 1;
if (mid >= l)
update(l, r, d, p << 1, cl, mid);
if (mid < r)
update(l, r, d, p << 1 | 1, mid + 1, cr);
tree[p] = tree[p << 1] + tree[p << 1 | 1];
}
int main() {
while (scanf("%d%d", &n, &m) != EOF) {
for (int i = 1; i <= n; ++i)
cin >> A[i];
build();
while (m--) {
int l, r, d;
string s;
cin >> s >> l >> r;
if (s[0] == 'C')
cin >> d, update(l, r, d);
else
cout << query(l, r) << '\n';
}
}
return 0;
}
比较标准的模板,区间修改+区间查询。
D - Hotel
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
#define lson(x) ((x << 1) + 1)
#define rson(x) ((x << 1) + 2)
const int N = 50005;
int n, m;
struct Node {
// lsum是从左到右的可出租的房子的最大值,同理rsum是从右到左,sum是最大块块空房间
//的大小,sumv是指最大块空房间的起始位置(下标)
int l, r, lsum, rsum, sum, sumv, lazy;
int size() { return r - l + 1; }
void gao(int v) {
lazy = v;
if (v)
lsum = rsum = sum = 0;
else
lsum = rsum = sum = r - l + 1;
sumv = l;
}
} node[N * 4];
void pushup(int x) {
if (node[lson(x)].lsum == node[lson(x)].size())
node[x].lsum = node[lson(x)].lsum + node[rson(x)].lsum;
else
node[x].lsum = node[lson(x)].lsum;
if (node[rson(x)].rsum == node[rson(x)].size())
node[x].rsum = node[lson(x)].rsum + node[rson(x)].rsum;
else
node[x].rsum = node[rson(x)].rsum;
node[x].sum = node[lson(x)].sum;
node[x].sumv = node[lson(x)].sumv;
if (node[x].sum < node[lson(x)].rsum + node[rson(x)].lsum) {
node[x].sum = node[lson(x)].rsum + node[rson(x)].lsum;
node[x].sumv = node[lson(x)].r - node[lson(x)].rsum + 1;
}
if (node[x].sum < node[rson(x)].sum) {
node[x].sum = node[rson(x)].sum;
node[x].sumv = node[rson(x)].sumv;
}
}
void pushdown(int x) {
if (node[x].lazy != -1) {
node[lson(x)].gao(node[x].lazy);
node[rson(x)].gao(node[x].lazy);
node[x].lazy = -1;
}
}
void build(int l, int r, int x = 0) {
node[x].l = l;
node[x].r = r;
node[x].lazy = -1;
if (l == r) {
node[x].lsum = node[x].rsum = node[x].sum = 1;
return;
}
int mid = (l + r) / 2;
build(l, mid, lson(x));
build(mid + 1, r, rson(x));
pushup(x);
}
void add(int l, int r, int v, int x = 0) {
if (node[x].l >= l && node[x].r <= r) {
node[x].gao(v);
return;
}
int mid = (node[x].l + node[x].r) / 2;
pushdown(x);
if (l <= mid)
add(l, r, v, lson(x));
if (r > mid)
add(l, r, v, rson(x));
pushup(x);
}
int query(int v, int x = 0) {
if (node[x].l == node[x].r) {
if (node[x].sum >= v)
return node[x].sumv;
return 0;
}
pushdown(x);
int ans = 0;
if (node[lson(x)].sum >= v)
ans = query(v, lson(x));
else if (node[lson(x)].rsum + node[rson(x)].lsum >= v)
ans = node[lson(x)].r - node[lson(x)].rsum + 1;
else if (node[rson(x)].sum >= v)
ans = query(v, rson(x));
pushup(x);
return ans;
}
int main() {
while (~scanf("%d%d", &n, &m)) {
build(1, n);
int op, a, b;
while (m--) {
scanf("%d%d", &op, &a);
if (op == 2) {
scanf("%d", &b);
add(a, a + b - 1, 0);
} else {
int tmp = query(a);
printf("%d\n", tmp);
if (tmp == 0)
continue;
add(tmp, tmp + a - 1, 1);
}
}
}
return 0;
}
题解:是用到了线段树,不过加了很多东西,比如用pushup去分析本区间的最大连续空房间有三种可能,一是最大连续空房间完全在左区间,二是完全在右区间,三是刚好垮了两个区间,并记录起始下标。
监视任务
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <algorithm>
#include <functional>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <vector>
using namespace std;
#define ll long long
#define inf 1000000000
#define mod 1000000007
#define maxn 500005
#define lowbit(x) (x & -x)
#define eps 1e-9
struct node {
int l, r, k;
} a[maxn * 2];
int n, m, sum[maxn * 5], lazy[maxn * 5];
bool comp(node a, node b) {
return a.r < b.r;
}
void pushdown(int l, int r, int id) {
if (lazy[id]) {
int mid = (l + r) / 2;
lazy[id * 2] = 1;
sum[id * 2] = mid - l + 1;
lazy[id * 2 + 1] = 1;
sum[id * 2 + 1] = r - mid;
lazy[id] = 0;
}
}
int update(int id, int L, int R, int l, int r, int val) {
if (val <= 0)
return 0;
if (L >= l && R <= r) {
if (R - L + 1 - sum[id] <=
val) { //需要布置监视点 (即便当前全部布满也不能达到要求)
val -= R - L + 1 - sum[id]; //还需要的监视点
lazy[id] = 1;
sum[id] = R - L + 1; //全部布满
return val; //还需要布满的数量
}
}
pushdown(L, R, id);
int mid = (L + R) / 2;
if (r > mid)
val = update(id * 2 + 1, mid + 1, R, l, r, val);
if (l <= mid)
val = update(id * 2, L, mid, l, r, val);
sum[id] = sum[id * 2] + sum[id * 2 + 1];
return val;
}
int query(int id, int L, int R, int l, int r) {
if (L >= l && R <= r)
return sum[id];
pushdown(L, R, id);
int mid = (L + R) / 2, res = 0;
if (l <= mid)
res += query(id * 2, L, mid, l, r);
if (r > mid)
res += query(id * 2 + 1, mid + 1, R, l, r);
return res;
}
int main(void) {
int i;
scanf("%d%d", &n, &m);
for (i = 1; i <= m; i++)
scanf("%d%d%d", &a[i].l, &a[i].r, &a[i].k);
sort(a + 1, a + m + 1, comp);
for (i = 1; i <= m; i++) {
int w = query(1, 1, n, a[i].l, a[i].r);
update(1, 1, n, a[i].l, a[i].r, a[i].k - w);
}
printf("%d\n", query(1, 1, n, 1, n));
return 0;
}
题解:用了线段树+贪心,从左往右处理,优先在防卫右边的点,优先处理右端点在前面的区间,所以排序是按照右端点越小越在前。
Sum
#include <bits/stdc++.h>
#define il inline
#define lt p<<1
#define rt p<<1|1
using namespace std;
typedef long long ll;
int const N=1e5+100;
ll const MOD=1e9+7;
ll n,m,op,x,y,l,r;
ll a[N],b[N];
struct Segment_tree
{
int l,r;
ll sum[35];
}T[N<<2];
il void pushup(int p)
{
for(int i=0;i<=32;i++)
{
T[p].sum[i]=(T[lt].sum[i]+T[rt].sum[i])%MOD;
}
}
il void build(int l,int r,int p)
{
T[p].l=l,T[p].r=r;
if(l==r)
{
for(int i=0;i<=32;i++)
{
T[p].sum[i]=a[l]%2;
a[l]/=2;
}
return ;
}
int m=(l+r)>>1;
build(l,m,lt),build(m+1,r,rt);
pushup(p);
}
il void update(int pos,int val,int p)
{
if(T[p].l==T[p].r)
{
for(int i=0;i<=32;i++)
{
T[p].sum[i]=val%2;
val/=2;
}
return ;
}
int m=(T[p].l+T[p].r)>>1;
if(pos<=m)update(pos,val,lt);
else update(pos,val,rt);
pushup(p);
}
il Segment_tree query(int l,int r,int p)
{
if(l<=T[p].l&&T[p].r<=r)
{
return T[p];
}
int m=(T[p].l+T[p].r)>>1;
Segment_tree a,b,c;
memset(a.sum,0,sizeof(a.sum));
memset(b.sum,0,sizeof(b.sum));
if(l<=m)a=query(l,r,lt);
if(r>m)b=query(l,r,rt);
for(int i=0;i<=32;i++)
{
c.sum[i]=(a.sum[i]+b.sum[i])%MOD;
}
return c;
}
int main()
{
b[0]=1;
for(int i=1;i<N;i++)
{
b[i]=(b[i-1]<<1)%MOD;
}
scanf("%lld",&n);
for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
scanf("%lld",&m);
build(1,n,1);
while(m--)
{
scanf("%lld",&op);
if(op==2)
{
scanf("%lld%lld",&l,&r);
Segment_tree ans=query(l,r,1);
ll res=0;
for(int i=0;i<=32;i++)
{
res=(res+(b[i]*(b[ans.sum[i]]-1))%MOD)%MOD;
}
printf("%lld\n",res%MOD);
}
else
{
scanf("%lld%lld",&x,&y);
update(x,y,1);
}
}
return 0;
}
题解:我们如果直接计算操作2的话会很困难,我们可以直接考虑一个数的二进制位对答案做出的贡献,显然二进制位为0时就不会有任何贡献,当我们知道所有的数在二进制第i位上的总数目为x时,我们就可以直接计算出所有的子集所做出的贡献,我们会想到,会有2的x次幂减1个子集,然后再乘上个位权(1LL<<i),有一个地方不要忘记,就是每次对于1操作,我们不要忘记更新a数组里的元素。我们能够发现,and操作的原理是,全1得1,每个数加起来,只有1做出了贡献,那么我们维护的是1的个数。首先把每个数拆成0和1,求和的时候,把1个数加起来。
下面图片帮助理解:题解链接
The Trip On Abandoned Railway
#include<bits/stdc++.h>
#define ll long long
#define mod 1000000007
using namespace std;
const int maxx=1e5+100;
struct node{
int l;
int r;
ll sum;
ll lazy1;
}p[maxx<<2];
ll a[maxx];
ll c[maxx];
int n,m;ll d;
/*-----------树状数组------------*/
inline int lowbit(int x){return x & -x;}
inline void add(int cur,ll v)
{
while(cur<maxx)
{
c[cur]=(c[cur]+v)%mod;
cur+=lowbit(cur);
}
}
inline ll Query(int cur)
{
ll ans=0;
while(cur>0)
{
ans=(ans+c[cur])%mod;
cur-=lowbit(cur);
}
return ans;
}
/*------------线段树-------------*/
inline void pushup(int cur)
{
p[cur].sum=(p[cur<<1].sum+p[cur<<1|1].sum);
}
inline void pushdown(int cur)
{
if(p[cur].lazy1)
{
p[cur<<1].lazy1=(p[cur<<1].lazy1+p[cur].lazy1);
p[cur<<1|1].lazy1=(p[cur<<1|1].lazy1+p[cur].lazy1);
p[cur<<1].sum=(p[cur<<1].sum+(p[cur<<1].r-p[cur<<1].l+1)*1ll*p[cur].lazy1);
p[cur<<1|1].sum=(p[cur<<1|1].sum+(p[cur<<1|1].r-p[cur<<1|1].l+1)*1ll*p[cur].lazy1);
p[cur].lazy1=0;
}
}
inline void build(int l,int r,int cur)
{
p[cur].l=l;
p[cur].r=r;
p[cur].lazy1=p[cur].sum=0;
if(l==r) return ;
int mid=l+r>>1;
build(l,mid,cur<<1);
build(mid+1,r,cur<<1|1);
}
inline void update(int l,int r,ll v,int cur)
{
int L=p[cur].l;
int R=p[cur].r;
if(l<=L&&R<=r)
{
p[cur].lazy1=(p[cur].lazy1+v);
p[cur].sum=(p[cur].sum+(R-L+1)*v);
return ;
}
int mid=L+R>>1;
pushdown(cur);
if(r<=mid) update(l,r,v,cur<<1);
else if(l>mid) update(l,r,v,cur<<1|1);
else update(l,mid,v,cur<<1),update(mid+1,r,v,cur<<1|1);
pushup(cur);
}
inline ll query(int l,int r,int cur)
{
int L=p[cur].l;
int R=p[cur].r;
if(l<=L&&R<=r) return p[cur].sum;
pushdown(cur);
int mid=L+R>>1;
ll ans;
if(r<=mid) return query(l,r,cur<<1);
else if(l>mid) return query(l,r,cur<<1|1);
else return (query(l,mid,cur<<1)+query(mid+1,r,cur<<1|1));
return ans;
}
int main()
{
int t,op,x;ll y;
scanf("%d",&t);
while(t--)
{
memset(c,0,sizeof(c));
scanf("%d%d%lld",&n,&m,&d);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
build(1,n,1);
while(m--)
{
scanf("%d",&op);
if(op==1)
{
scanf("%d%lld",&x,&y);
add(x,y);
update(x+1,n,1,1);
}
else
{
scanf("%d",&x);
ll ans=(Query(x)+query(1,x,1)*d*1ll+a[x]);
printf("%lld\n",ans%mod);
a[x]-=ans;
}
}
}
return 0;
}
题解:用树状数组去存+k的操作,用线段树去存+k*d的操作。题解链接