4137: [FJOI2015]火星商店问题

50 篇文章 0 订阅
10 篇文章 0 订阅

4137: [FJOI2015]火星商店问题

Time Limit: 20 Sec   Memory Limit: 256 MB
Submit: 181   Solved: 85
[ Submit][ Status][ Discuss]

Description

火星上的一条商业街里按照商店的编号1,2 ,…,n ,依次排列着n个商店。商店里出售的琳琅满目的商品中,每种商品都用一个非负整数val来标价。每个商店每天都有可能进一些新商品,其标价可能与已有商品相同。 
火星人在这条商业街购物时,通常会逛这条商业街某一段路上的所有商店,譬如说商店编号在区间[L,R]中的商店,从中挑选1件自己最喜欢的商品。每个火星人对商品的喜好标准各不相同。通常每个火星人都有一个自己的喜好密码x。对每种标价为val的商品,喜好密码为x的火星人对这种商品的喜好程度与val异或x的值成正比。也就是说,val xor x的值越大,他就越喜欢该商品。每个火星人的购物卡在所有商店中只能购买最近d天内(含当天)进货的商品。另外,每个商店都有一种特殊商品不受进货日期限制,每位火星人在任何时刻都可以选择该特殊商品。每个商店中每种商品都能保证供应,不存在商品缺货的问题。 
对于给定的按时间顺序排列的事件,计算每个购物的火星人的在本次购物活动中最喜欢的商品,即输出val xor x的最大值。这里所说的按时间顺序排列的事件是指以下2种事件: 
事件0,用三个整数0,s,v,表示编号为s的商店在当日新进一种标价为v 的商品。 
事件1,用5个整数1,L,R,x,d,表示一位火星人当日在编号为L到R的商店购买d天内的商品,该火星人的喜好密码为x。

Input

第1行中给出2个正整数n,m,分别表示商店总数和事件总数。 
第2行中有n个整数,第i个整数表示商店i的特殊商品标价。 
接下来的m行,每行表示1个事件。每天的事件按照先事件0,后事件1的顺序排列。 

Output

将计算出的每个事件1的val xor x的最大值依次输出。

Sample Input

4 6
1 2 3 4
1 1 4 1 0
0 1 4
0 1 3
1 1 1 1 0
1 1 1 1 1
1 1 2 1 2

Sample Output

5
0
2
5

HINT

n, m <= 100000

数据中,价格不大于 100000

Source

[ Submit][ Status][ Discuss]



假如有一个数字集合,给你一个数,问这个数和集合内任意数的xor最大值,那么就是个经典的trie贪心

本题还加上了时间,商店编号等限制,不妨逐个消除它们的影响

每个询问等价于在某个时间段,处理某些商店内的物品的影响

建立一棵时间线段树,考虑在线段树上分治

把每个询问对应到线段树上,是O(logn)个点

每个新增商品对应到线段树上,也是O(logn)个点

这样,对于线段树内的每个点,该点储存的商品只要满足询问的商店编号限制就能产生贡献了

对于线段树每个点,离散该点出现过的商店的编号后,建立一棵可持久化trie

因为每个询问都对应一个连续的区间,所以直接在数据结构上跑贪心就行了

点集扩大了O(logn)倍,每次询问代价也是O(logn),所以总复杂度是O(nlog^2n)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
  
const int maxn = 1E5 + 10;
const int T = 18;
  
struct Query{
    int L,R,x,Num; Query(){}
    Query(int L,int R,int x,int Num): L(L),R(R),x(x),Num(Num){}
    bool operator < (const Query &B) const {return R < B.R;}
};
  
struct Modify{
    int va,Num; Modify(){}
    Modify(int va,int Num): va(va),Num(Num){}
    bool operator < (const Modify &B) const {return Num < B.Num;}
};
  
struct data{
    int typ,a,b,c,d,e; data(){}
    data(int typ,int a,int b,int c,int d,int e): typ(typ),a(a),b(b),c(c),d(d),e(e){}
}D[maxn];
  
int n,m,tot = 1,cur,cnt,A[maxn * 3],Ans[maxn],s[maxn],rt[maxn],ch[maxn*20][2],Cnt[maxn*20];
  
vector <Modify> F[maxn*20],ff;
vector <Query> G[maxn*20],gg;
  
int getint()
{
    char ch = getchar(); int ret = 0;
    while (ch < '0' || '9' < ch) ch = getchar();
    while ('0' <= ch && ch <= '9')
        ret = ret * 10 + ch - '0',ch = getchar();
    return ret;
}
  
void Insert_Query(int o,int l,int r,int ql,int qr,const Query &k)
{
    if (ql <= l && r <= qr) {G[o].push_back(k); return;}
    int mid = (l + r) >> 1;
    if (ql <= mid) Insert_Query(o<<1,l,mid,ql,qr,k);
    if (qr > mid) Insert_Query(o<<1|1,mid+1,r,ql,qr,k);
}
  
void Insert_Modify(int o,int l,int r,int pos,const Modify &k)
{
    F[o].push_back(k); if (l == r) return;
    int mid = (l + r) >> 1;
    if (pos <= mid) Insert_Modify(o<<1,l,mid,pos,k);
    else Insert_Modify(o<<1|1,mid+1,r,pos,k);
}
  
void Work1(const Modify &f,int &pre)
{
    while (pre < f.Num) ++pre,rt[pre] = rt[pre - 1];
    int o1 = rt[pre],o2 = rt[f.Num] = ++cnt;
    for (int i = T - 1; i >= 0; i--)
    {
        int k = (f.va >> i) & 1;
        ch[o2][k^1] = ch[o1][k^1]; ch[o2][k] = ++cnt;
        Cnt[cnt] = Cnt[ch[o1][k]] + 1;
        o1 = ch[o1][k]; o2 = ch[o2][k];
    }
}
  
void Work2(const Query &g)
{
    if (g.L > g.R) return;
    int ans = 0,o1 = rt[g.L - 1],o2 = rt[g.R];
    for (int i = T - 1; i >= 0; i--)
    {
        int k = (g.x >> i) & 1;
        if (Cnt[ch[o2][k^1]] - Cnt[ch[o1][k^1]] == 0) o1 = ch[o1][k],o2 = ch[o2][k];
        else o1 = ch[o1][k^1],o2 = ch[o2][k^1],ans |= (1 << i);
    }
    Ans[g.Num] = max(Ans[g.Num],ans);
}
  
void Solve(vector <Modify> &f,vector <Query> &g)
{
    if (!g.size() || !f.size()) return;
    int pre,siz; pre = siz = 0; sort(f.begin(),f.end());
    for (int i = 0; i < f.size(); i++) A[++siz] = f[i].Num;
    for (int i = 0; i < g.size(); i++) A[++siz] = g[i].L,A[++siz] = g[i].R;
    sort(A + 1,A + siz + 1); cur = 1;
    for (int i = 2; i <= siz; i++) if (A[i] != A[i-1]) A[++cur] = A[i];
    for (int i = 0; i < f.size(); i++)
    {
        f[i].Num = lower_bound(A + 1,A + cur + 1,f[i].Num) - A;
        Work1(f[i],pre);
    }
    for (int i = pre + 1; i <= cur; i++) rt[i] = rt[i - 1];
    for (int i = 0; i < g.size(); i++)
    {
        int l = lower_bound(A + 1,A + cur + 1,g[i].L) - A;
        int r = lower_bound(A + 1,A + cur + 1,g[i].R) - A;
        g[i].L = l; g[i].R = r; Work2(g[i]);
    }
    for (int i = 1; i <= cnt; i++) memset(ch[i],0,sizeof(ch[i])),Cnt[i] = 0;
    for (int i = 1; i <= cur; i++) rt[i] = 0; cnt = 1;
}
  
void Work(int o,int l,int r)
{
    Solve(F[o],G[o]); if (l == r) return;
    int mid = (l + r) >> 1;
    Work(o<<1,l,mid); Work(o<<1|1,mid+1,r);
}
  
int main()
{
    #ifdef DMC
        freopen("DMC.txt","r",stdin);
    #endif
      
    n = getint(); m = getint(); rt[0] = cnt = 1;
    for (int i = 1; i <= n; i++) s[i] = getint();
    for (int i = 1; i <= m; i++)
    {
        int typ = getint(),a,b,c,d,e; if (!typ) ++tot;
        if (!typ)
        {
            a = getint(); b = getint(); 
            D[i] = data(typ,a,b,tot,d,e);
        }
        else
        {
            a = getint(); b = getint(); c = getint(); d = getint(); 
            e = tot; d = max(tot - d + 1,1); D[i] = data(typ,a,b,c,d,e);
        }
    }
    for (int i = 1; i <= m; i++)
        if (D[i].typ) Insert_Query(1,1,tot,D[i].d,D[i].e,Query(D[i].a,D[i].b,D[i].c,i));
        else Insert_Modify(1,1,tot,D[i].c,Modify(D[i].b,D[i].a));
    for (int i = 1; i <= n; i++) ff.push_back(Modify(s[i],i));
    for (int i = 1; i <= m; i++) if (D[i].typ) gg.push_back(Query(D[i].a,D[i].b,D[i].c,i));
    Solve(ff,gg); Work(1,1,tot); for (int i = 1; i <= m; i++) if (D[i].typ) printf("%d\n",Ans[i]);
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值