2019 徐州 icpc树状数组套线段树 H - Yuuki and a problem

it2025-02-17  4

H - Yuuki and a problem

题目 题意大概是 : 给一个数组,有两种操作: 1 x y 把x位置上的值替换为y 2 l r 求 l ~ r 区间内元素任意子集的和都不能构成的最小值。 这个题刚开始看到,没有一点想法,, 后来也没一点想法。。 如果只有查询怎么办? 只有查询的话,我只能想到背包。再次感慨我好菜 对,就是背包的想法。 如果把1 ~ x 内的数都能由他的子集表示出来,那么x + 1满足什么条件才能表示出来? 在1 ~ x 的数都能表示出来的前提下,1 ~ x + 1 内数的和如果大于等于x + 1,那么x + 1就能被表示出来。 为什么呢?可以试想一下背包,0 ~ x 内dp值全为1 那么只要随便有一个1 ~ x + 1的数 就可以转移过来了。所以成立。 如果x + 1能表示出来,后面的同理, 所以可得: 如果1 ~ x能表示出来,则直接跳到(区间在1 ~ x内数的和) + 1就可以了。 这个最低时间复杂度情况下是一个Fibonacci数列。所以查询不了几次。很快。 那么全是查询,没有修改的情况可以用主席树里的查询来完成。 现在多了一个修改,怎么办? 树状数组套线段树来写。

代码:

#include <algorithm> #include <iostream> #include <cstdio> #include <string> #include <queue> #include <cstring> #include <stack> #include <map> #include <bitset> #include <math.h> #include <set> #include <ctime> #include <unordered_set> #include <climits> using namespace std; typedef long long ll; typedef pair<int,ll> pii; typedef pair<ll,ll> pll; typedef pair<double,double> pdd; typedef unsigned long long ull; typedef unordered_set<int>::iterator sit; #define st first #define sd second #define mkp make_pair #define pb push_back void tempwj(){freopen("P3380_2.in","r",stdin);freopen("hash.out","w",stdout);} ll gcd(ll a,ll b){return b == 0 ? a : gcd(b,a % b);} ll qpow(ll a,ll b,ll mod){a %= mod;ll ans = 1;while(b > 0){if(b & 1)ans = ans * a % mod;a = a * a % mod;b >>= 1;}return ans % mod;} struct cmp{bool operator()(const pii & a, const pii & b){return a.second > b.second;}}; int lb(int x){return x & -x;} const int inf = INT_MAX; const double esp = 1e-9; const ll INF = 0x3f3f3f3f3f3f; const ll mod = 1e9+7; const int maxn = 4e5 + 5; const int M = 3e5+5; struct Node { int l,r; ll sum; }node[maxn * 40]; int root[maxn]; int cnt = 0; int n; int tree[maxn]; void build(int l,int r,int pos,int& no,int num) { if(no == 0) no = ++cnt; node[no].sum += num; if(l == r) return; int mid = l + r>> 1; if(pos <= mid) build(l,mid,pos,node[no].l,num); else build(mid + 1,r,pos,node[no].r,num); } ll query(int l,int r,int lq,int rq,int no) { if(no == 0) return 0; if(l > rq || r < lq) return 0; if(l >= lq && r <= rq) return node[no].sum; int mid = l + r >> 1; return query(l,mid,lq,rq,node[no].l) + query(mid + 1,r,lq,rq,node[no].r); } void add(int x,int pos,int num) { while(x <= n) { build(1,n,pos,root[x],num); x += lb(x); } } ll ask(int x,int l,int r) { ll ans = 0; while(x) { ans += query(1,n,l,r,root[x]); x -= lb(x); } return ans; } int a[maxn]; int main() { int m; scanf("%d%d",&n,&m); for (int i = 1; i <= n; i ++ ) scanf("%d",&a[i]); for (int i = 1;i <= n; i++ ) add(i,a[i],a[i]); while(m -- ) { int f,x,y; scanf("%d%d%d",&f,&x,&y); if(f == 1) { add(x,a[x],-a[x]); add(x,y,y); a[x] = y; } else { ll s1 = ask(y,1,1); ll s2 = ask(x - 1,1,1); // printf("%lld %lld\n",s1,s2); if(s1 - s2 == 0) { printf("1\n"); continue; } ll p = 2; while(1) { ll s1 = ask(y,1,min(p,1ll * n)); ll s2 = ask(x - 1,1,min(p,1ll * n)); if(s1 - s2 < p) break; // printf("%lld %lld %d\n",s1,s2,p); p = s1 - s2 + 1; } printf("%lld\n",p); } } }

(好短的树套树),如果想到了,代码应该不会有太大的问题。 但是想不到解法呀。。 我好菜

最新回复(0)