2019南昌邀请赛 H. Another Sequence (fwt+离散化+线段树/fwt+珂朵莉树)

题意:

给两个长度为n的数组,数字大小不超过1e5,求出它们两两相或的数组c,从小到达排序后支持两种操作:
0 x :查询位置为x的数的当前值
l r:对[l,r]区间的数开方

题解

首先通过fwt得到两两相或的数组。
离散化+线段树版本:
因为区间范围很大,所以先离散化,之后就是写线段树支持区间赋值,区间开方,单点查询的操作。

#include<bits/stdc++.h>
#define ll long long
#define mid ((l+r)>>1)
#define lson rt<<1, l, mid
#define rson rt<<1|1, mid+1, r
using namespace std;
int n, m;
const int maxn = 4e5 + 50;
ll a[maxn];
ll b[maxn];
void fwt(int len,ll p[],int opt){
	for(int i = 2;i <= len;i<<=1){
		for(int j = 0,h = i>>1;j < len;j+=i){
			for(int k = j;k < j+h;++k){
				ll x = p[k],y = p[k+h];
				if(opt==1) p[k+h]=(x+y);
                else p[k+h]=(y-x); // or
			}
		}
	}return;
}
int len;
void init()
{
    memset(a, 0, sizeof a);
    memset(b, 0, sizeof b);
    scanf("%d", &n);
    int mx = 0;
    for(int i = 1; i <= n; ++i){
        int x;
        scanf("%d", &x);
        mx = max(x, mx);
        a[x]++;
    }
    for(int i = 1;i <= n; ++i){
        int x;
        scanf("%d", &x);
        mx = max(mx, x);
        b[x]++;
    }
    len = 1;while(len <= mx) len<<=1;
    fwt(len, a, 1); fwt(len, b, 1);
    for(int i = 0; i < len; ++i) a[i] *= b[i];
    fwt(len, a, -1);
    for(int i = 1; i < len; ++i){
        printf("i: %d   num: %d\n",i,a[i]);
    }
}
ll lz[maxn<<2];
void down(int rt){
    if(lz[rt] == -1) return;
    lz[rt<<1] = lz[rt<<1|1] = lz[rt];
    return;
}
void up(int rt){
    if(lz[rt<<1] == lz[rt<<1|1]) lz[rt] = lz[rt<<1];
    else lz[rt] = -1;
    return;
}
void update(int rt, int l, int r, int L,int R, ll x){
    if(L <= l && r <= R){
        lz[rt] = x;return;
    }
    down(rt);
    if(L <= mid) update(lson, L, R, x);
    if(R > mid) update(rson, L, R, x);
    up(rt);
    return;
}
void change(int rt, int l, int r, int L, int R){
    if(lz[rt] == 1) return;
    if(L <= l && r <= R){
        if(lz[rt] != -1){
            lz[rt] = sqrt(lz[rt]);return;
        }
    }
    down(rt);
    if(L <= mid) change(lson, L, R);
    if(R > mid) change(rson, L, R);
    up(rt);
    return;
}
ll query(int rt, int l, int r, int i)
{
    if(l <= i && i <= r){
        if(lz[rt] != -1) return lz[rt];
    }
    down(rt);
    if( i <= mid) return query(lson, i);
    else return query(rson, i);
}
ll cc[maxn*2];
ll num;
struct node{
    ll l, r;
    int op;//-1为赋值, 0为查询, 1为开根号
    ll x;
}e[maxn];
int cnt = 0;
void sol()
{
    num = 0;
    cnt = 0;
    ll res = 0;
    for(int i = 0; i < len; ++i){
        if(!a[i]) continue;
        cc[++num] = res+1;
        cc[++num] = res+a[i];
        e[cnt].l = res+1;
        e[cnt].r = res + a[i];
        e[cnt].op = -1;
        e[cnt].x = i;
        cnt++;
        res += a[i];
    }
    scanf("%d", &m);
    while(m--){
        ll l, r;
        scanf("%lld%lld", &l, &r);
        if(l == 0){
            e[cnt].op = 0;
            e[cnt].x = r;
            cnt++;
            cc[++num] = r-1;
            cc[++num] = r;
        }
        else{
            e[cnt].op = 1;
            e[cnt].l = l;
            e[cnt].r = r;
            cnt++;
            cc[++num] = l-1;
            cc[++num] = l;
            cc[++num] = r;
        }
    }
    sort(cc+1,cc+num+1);
    num = unique(cc+1,cc+1+num) - cc-1;
    for(int i = 0; i < cnt; ++i){
       // cout<<"op:"<<e[i].op<<endl;
        if(e[i].op == -1){
            int l = lower_bound(cc+1,cc+1+num,e[i].l) - cc;
            int r = lower_bound(cc+1,cc+1+num,e[i].r) - cc;
            update(1,1,num,l,r,e[i].x);
        }
        else if(e[i].op == 0){
            int id = lower_bound(cc+1,cc+1+num,e[i].x) - cc;
            int ans = query(1,1,num,id);
            printf("%d\n",ans);
        }
        else {
            int l = lower_bound(cc+1,cc+1+num,e[i].l) - cc;
            int r = lower_bound(cc+1,cc+1+num,e[i].r) - cc;
            change(1,1,num,l,r);
        }
    }
}
int main()
{
    init();sol();
}

珂朵莉树版本:
fwt之后直接上珂朵莉树,需要注意的是开方操作的同时检查开方完之后整个区间每个结点的值是否相同,如果相同就合并这些结点。
代码又短又好写,赞美柯学!!

#include<bits/stdc++.h>
#define ll long long
#define mid ((l+r)>>1)
#define lson rt<<1, l, mid
#define rson rt<<1|1, mid+1, r
using namespace std;
int n, m;
const int maxn = 4e5 + 50;
ll a[maxn];
ll b[maxn];
void fwt(int len,ll p[],int opt){
	for(int i = 2;i <= len;i<<=1){
		for(int j = 0,h = i>>1;j < len;j+=i){
			for(int k = j;k < j+h;++k){
				ll x = p[k],y = p[k+h];
				if(opt==1) p[k+h]=(x+y);
                else p[k+h]=(y-x); // or
			}
		}
	}return;
}
int len;
void init()
{
    memset(a, 0, sizeof a);
    memset(b, 0, sizeof b);
    scanf("%d", &n);
    int mx = 0;
    for(int i = 1; i <= n; ++i){
        int x;
        scanf("%d", &x);
        mx = max(x, mx);
        a[x]++;
    }
    for(int i = 1;i <= n; ++i){
        int x;
        scanf("%d", &x);
        mx = max(mx, x);
        b[x]++;
    }
    len = 1;while(len <= mx) len<<=1;
    fwt(len, a, 1); fwt(len, b, 1);
    for(int i = 0; i < len; ++i) a[i] *= b[i];
    fwt(len, a, -1);
}
struct node{
    ll l, r;
    mutable int val;
    node(ll x = 0, ll y = 0, int z = 0):l(x), r(y), val(z){}
    bool operator < (const node& x) const {return l < x.l;}
};
set<node> s;
set<node>::iterator split(ll x){
    set<node>::iterator it = s.lower_bound(node(x));
    if(it!=s.end() && it->l == x) return it;
    it--;
    ll l = it->l, r = it->r;
    int val = it->val;
    s.erase(it);
    s.insert(node(l, x-1, val));
    return s.insert(node(x, r, val)).first;
}
int query(ll l){
    set<node> ::iterator it = s.lower_bound(node(l));
    if(it!=s.end() && it->l == l) return it->val;
    it--;
    return it->val;
}
void change(ll l, ll r){
    set<node>::iterator rit = split(r+1), lit = split(l);
    int ok = 1;
    int pre = -1;
    while(lit != rit){
        lit->val = sqrt(lit->val);
        if(pre != -1 && lit->val != pre) ok = 0;
        pre = lit->val;
        lit++;
    }
    if(ok){//让时间变松的关键
        lit = split(l);
        s.erase(lit, rit);
        s.insert(node(l, r, pre));
    }
}
void sol()
{
    ll res = 0;
    for(int i = 0; i < len; ++i){
        if(!a[i]) continue;
        s.insert(node(res+1,res+a[i], i));
        res += a[i];
    }
    int m;
    scanf("%d", &m);
    while(m--){
        ll l ,r;
        scanf("%lld%lld", &l, &r);
        if(l == 0){
            printf("%d\n", query(r));
        }
        else change(l,r);
    }
}
int main()
{
    init();sol();
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值