NOI2010 超级钢琴 和 十二省2019 异或粽子 和 test20181031 简单数据结构
超级钢琴
小Z是一个小有名气的钢琴家,最近C博士送给了小Z一架超级钢琴,小Z希望能够用这架钢琴创作出世界上最美妙的音乐。
这架超级钢琴可以弹奏出n个音符,编号为1至n。第i个音符的美妙度为Ai,其中Ai可正可负。
一个“超级和弦”由若干个编号连续的音符组成,包含的音符个数不少于L且不多于R。我们定义超级和弦的美妙度为其包含的所有音符的美妙度之和。两个超级和弦被认为是相同的,当且仅当这两个超级和弦所包含的音符集合是相同的。
小Z决定创作一首由k个超级和弦组成的乐曲,为了使得乐曲更加动听,小Z要求该乐曲由k个不同的超级和弦组成。我们定义一首乐曲的美妙度为其所包含的所有超级和弦的美妙度之和。小Z想知道他能够创作出来的乐曲美妙度最大值是多少。
n,k ⩽ 500000
题解
固定左端点,维护右端点的区间,用堆维护最大值。每次取出的时候拆区间即可。
时间复杂度 O(k log n)。
#include<bits/stdc++.h>
using namespace std;
template<class T> T read(){
T x=0,w=1;char c=getchar();
for(;!isdigit(c);c=getchar())if(c=='-') w=-w;
for(;isdigit(c);c=getchar()) x=x*10+c-'0';
return x*w;
}
template<class T> T read(T&x){
return x=read<T>();
}
#define CO const
#define IN inline
typedef long long LL;
CO int N=500000+10;
pair<int,int> st[N][19];
int lg[N];
IN pair<int,int> query(int l,int r){
int k=lg[r-l+1];
return max(st[l][k],st[r-(1<<k)+1][k]);
}
struct node {int val,p,st,l,r;};
IN bool operator<(CO node&a,CO node&b){
return a.val<b.val;
}
priority_queue<node> Q;
int main(){
int n=read<int>(),K=read<int>();
int L=read<int>(),R=read<int>();
lg[0]=-1;
for(int i=1;i<=n;++i) lg[i]=lg[i>>1]+1;
for(int i=1;i<=n;++i) st[i][0]=make_pair(st[i-1][0].first+read<int>(),i);
for(int j=1;j<=lg[n];++j)
for(int i=1;i+(1<<j)-1<=n;++i) st[i][j]=max(st[i][j-1],st[i+(1<<(j-1))][j-1]);
for(int i=1;i<=n-L+1;++i){
pair<int,int> t=query(i+L-1,min(i+R-1,n));
Q.push((node){t.first-st[i-1][0].first,t.second,i,i+L-1,min(i+R-1,n)});
}
LL ans=0;
while(K--){
node x=Q.top();Q.pop();
ans+=x.val;
if(x.l<=x.p-1){
pair<int,int> t=query(x.l,x.p-1);
Q.push((node){t.first-st[x.st-1][0].first,t.second,x.st,x.l,x.p-1});
}
if(x.p+1<=x.r){
pair<int,int> t=query(x.p+1,x.r);
Q.push((node){t.first-st[x.st-1][0].first,t.second,x.st,x.p+1,x.r});
}
}
printf("%lld\n",ans);
return 0;
}
异或粽子
题目描述
小粽是一个喜欢吃粽子的好孩子。今天她在家里自己做起了粽子。
小粽面前有 $n$ 种互不相同的粽子馅儿,小粽将它们摆放为了一排,并从左至右编号为 $1$ 到 $n$。第 $i$ 种馅儿具有一个非负整数的属性值 $a_i$。每种馅儿的数量都足够多,即小粽不会因为缺少原料而做不出想要的粽子。小粽准备用这些馅儿来做出 $k$ 个粽子。
小粽的做法是:选两个整数数 $l$, $r$,满足 $1 \leqslant l \leqslant r \leqslant n$,将编号在 $[l, r]$ 范围内的所有馅儿混合做成一个粽子,所得的粽子的美味度为这些粽子的属性值的异或和。(异或就是我们常说的 xor 运算,即 C/C++ 中的 ˆ
运算符或 Pascal 中的 xor
运算符)
小粽想品尝不同口味的粽子,因此它不希望用同样的馅儿的集合做出一个以上的 粽子。
小粽希望她做出的所有粽子的美味度之和最大。请你帮她求出这个值吧!
输入输出格式
输入格式:第一行两个正整数 $n$, $k$,表示馅儿的数量,以及小粽打算做出的粽子的数量。
接下来一行为 $n$ 个非负整数,第 $i$ 个数为 $a_i$,表示第 $i$ 个粽子的属性值。 对于所有的输入数据都满足:$1 \leqslant n \leqslant 5 \times 10^5$, $1 \leqslant k \leqslant \min\left\{\frac{n(n-1)}{2},2 \times 10^{5}\right\}$, $0 \leqslant a_i \leqslant 4 294 967 295$。
输出格式:输出一行一个整数,表示小粽可以做出的粽子的美味度之和的最大值。
输入输出样例
说明
测试点 | $n$ | $k$ |
---|---|---|
$1$, $2$, $3$, $4$, $5$, $6$, $7$, $8$ | $\leqslant 10^3$ | $\leqslant 10^3$ |
$9$, $10$, $11$, $12$ | $\leqslant 5 \times 10^5$ | $\leqslant 10^3$ |
$13$, $14$, $15$, $16$ | $\leqslant 10^3$ | $\leqslant 2 \times 10^5$ |
$17$, $18$, $19$, $20$ | $\leqslant 5 \times 10^5$ | $\leqslant 2 \times 10^5$ |
分析
又是问前k大,可以参考线段树求所有区间前k大的做法,拆区间。
但是Trie树不支持任意区间查询,即使可持久化后也只能查询右端点固定的。但是没有关系,把每个右端点对应的部分区间都插入二叉堆就行了。
时间复杂度\(O(n \log v+k \log v)\)
#include<bits/stdc++.h>
#define rg register
#define il inline
#define co const
template<class T>il T read(){
rg T data=0,w=1;rg char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') w=-1;ch=getchar();}
while(isdigit(ch)) data=data*10+ch-'0',ch=getchar();
return data*w;
}
template<class T>il T read(rg T&x) {return x=read<T>();}
typedef long long ll;
using namespace std;
co int N=2e5+5,INF=0x3f3f3f3f;
char s[N];
int n,na,nb,m;
pair<int,int> a[N],b[N];
int trie[N][26],tot,root;
vector<int> val[N],edge[N];
int newnode(){
++tot,memset(trie[tot],0,sizeof trie[tot]),val[tot].clear();
return tot;
}
void insert(char*s,int n,int id){
int u=root;
for(int i=0;i<n;++i){
int k=s[i]-'a';
if(!trie[u][k]) trie[u][k]=newnode();
u=trie[u][k],val[u].push_back(id);
}
}
void link(char*s,int n,int id){
int u=root;
for(int i=0;u&&i<n;++i){
int k=s[i]-'a';
u=trie[u][k];
}
if(!u) return;
for(int i=0;i<val[u].size();++i)
edge[id].push_back(val[u][i]);
}
int deg[N],dis[N],pos[N],ref[N],ans;
queue<int> que;
int lpfa(){
fill(deg+1,deg+na+1,0);
fill(pos,pos+na+1,0);
fill(dis+1,dis+na+1,-INF);
for(int i=1;i<=na;++i)
for(int j=0;j<edge[i].size();++j)
++deg[edge[i][j]];
for(int i=1;i<=na;++i)
if(!deg[i]) que.push(i),dis[i]=a[i].second-a[i].first+1;
for(int u;que.size();){
u=que.front(),que.pop();
pos[u]=++pos[0],::ref[pos[0]]=u;
for(int i=0,v;i<edge[u].size();++i){
v=edge[u][i];
if(--deg[v]==0) que.push(v);
}
}
if(pos[0]<na) return -1;
ans=-INF;
for(int i=1,u;i<=na;++i){
u=::ref[i],ans=max(ans,dis[u]);
for(int j=0,v;j<edge[u].size();++j){
v=edge[u][j];
if(dis[v]<dis[u]+a[v].second-a[v].first+1)
dis[v]=dis[u]+a[v].second-a[v].first+1;
}
}
return ans;
}
void String(){
scanf("%s",s+1),n=strlen(s+1);
read(na),tot=0,root=newnode();;
for(int i=1;i<=na;++i){
read(a[i].first),read(a[i].second),edge[i].clear();
insert(s+a[i].first,a[i].second-a[i].first+1,i);
}
read(nb);
for(int i=1;i<=nb;++i) read(b[i].first),read(b[i].second);
read(m);
for(int i=1,x,y;i<=m;++i){
read(x),read(y);
link(s+b[y].first,b[y].second-b[y].first+1,x);
}
printf("%d\n",lpfa());
}
int main(){
// freopen(".in","r",stdin),freopen(".out","w",stdout);
for(int T=read<int>();T--;) String();
return 0;
}
伪作法
逐位考虑,计数。\(O(n \log^2 v)\)。最慢的点要跑13s。
#include<bits/stdc++.h>
#define rg register
#define il inline
#define co const
il char nc(){
static char buf[6000000], *p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
il unsigned read(){
rg char ch=nc();rg unsigned sum=0;
while(!(ch>='0'&&ch<='9'))ch=nc();
while(ch>='0'&&ch<='9')sum=sum*10+ch-48,ch=nc();
return sum;
}
typedef long long ll;
using namespace std;
co int N=5e5+1;
int n,k;
unsigned s,a[N];
unordered_map<unsigned,int> c;
unordered_map<unsigned,unordered_map<int,int> > v;
ll cnt,ans,res;
int main(){
// freopen("xor.in","r",stdin),freopen("xor.out","w",stdout);
n=read(),k=read();
for(int i=1;i<=n;++i) a[i]=read()^a[i-1];
for(int p=31;k&&p>=0;--p){
s|=1U<<p,c.clear(),cnt=0;
for(int i=0;i<=n;++i){
cnt+=c[(s^a[i])>>p];
if(cnt>k) break;
++c[a[i]>>p];
}
// fprintf(stderr,"%d s=%u cnt=%lld\n",p,s,cnt);
if(cnt<=k){
ans+=(ll)s*cnt,k-=cnt,c.clear(),v.clear();
for(int i=0;i<=n;++i){
for(int q=p-1;q>=0;--q){
if(a[i]>>q&1) ans+=(1LL<<q)*(c[(s^a[i])>>p]-v[(s^a[i])>>p][q]),++v[a[i]>>p][q];
else ans+=(1LL<<q)*v[(s^a[i])>>p][q];
}
++c[a[i]>>p];
}
s^=1U<<p;
}
}
ans+=(ll)s*k;
printf("%lld\n",ans);
return 0;
}
伪作法2
从前往后扫描,建立Trie树,直接用每个点更新二叉堆,当这个点的当前最大值小于堆中的最小值时continue
,时间复杂度\(O(n^2 \log v)\),爆踩标算。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
inline ll read()
{
ll out=0,fh=1;
char jp=getchar();
while ((jp>'9'||jp<'0')&&jp!='-')
jp=getchar();
if (jp=='-')
fh=-1,jp=getchar();
while (jp>='0'&&jp<='9')
out=out*10+jp-'0',jp=getchar();
return out*fh;
}
const int MAXN=5e5+10;
const int N=32;
ll a[MAXN];
ll s[MAXN],t=0;
struct Trie
{
int idx;
int ch[MAXN*32][2];
int tot[MAXN*32][2];
Trie(){idx=0;}
void ins(ll x)
{
int u=0;
for(int i=N-1; i>=0; --i)
{
int k=(int)((x>>i)&1LL);
tot[u][k]++;
if(ch[u][k])
u=ch[u][k];
else
u=(ch[u][k]=++idx);
}
}
ll query(ll x,int p)
{
int u=0;
--p;
ll res=0;
for(ll i=N-1; i>=0; --i)
{
int k=(int)((x>>i)&1LL);
int v;
if(ch[u][k^1])
{
if(p>=tot[u][k^1] && ch[u][k])
{
v=k;
p-=tot[u][k^1];
}
else
{
v=k^1;
res+=(1LL<<i);
}
}
else
{
v=k;
}
u=ch[u][v];
}
return res;
}
} T;
priority_queue<ll> q;
int siz=0;
int n,k;
void solve()//One Must Have His Dream.
{
srand(19260817);
random_shuffle(a+1,a+1+n);
reverse(a+1,a+1+n);
for(int i=1; i<=n; ++i)
{
int f=1;
for(int p=1; p<=min(k,i-1) && f; ++p)
{
ll c=T.query(a[i],p);
if(siz<k)
q.push(-c),++siz;
else
{
if(c<=-q.top())
f=0;
else
{
q.pop();
q.push(-c);
}
}
}
T.ins(a[i]);
}
ll ans=0;
while(!q.empty())
{
ll x=q.top();
ans-=x;
q.pop();
}
cout<<ans<<endl;
}
int main()
{
// freopen("xor.in","r",stdin);
// freopen("xor.out","w",stdout);
n=read(),k=read();
for(int i=1; i<=n; ++i)
a[i]=a[i-1]^read();
a[++n]=0;
solve();
return 0;
}
其实BZOJ上有一道只是求最大值的可持久化Trie原题。
BZOJ3261 最大异或和
给定一个非负整数序列{a},初始长度为N。
有M个操作,有以下两种操作类型:
1、Ax:添加操作,表示在序列末尾添加一个数x,序列的长度N+1。
2、Qlrx:询问操作,你需要找到一个位置p,满足l<=p<=r,使得:
a[p] xor a[p+1] xor ... xor a[N] xor x 最大,输出最大是多少。
对于测试点 8-10,N,M<=300000 。
分析
记个异或前缀和数组s,则
a[p] xor a[p+1] xor ... xor a[N] xor x =s[p-1] xor s[N] xor x
那么使用可持久化Trie,对于每个节点维护latest,表示子节点中最新插入的是哪个,然后查询是时候往root[r-1]中latest ≥ l-1的节点走就行了。
时间复杂度\(O((N+M)\log 10^7)\)
由于要更新latest,所以插入时要用递归实现。
co int N=6e5+1;
int trie[N*24][2],latest[N*24];
int s[N],root[N],n,m,tot;
void insert(int i,int k,int p,int q){
if(k<0) return latest[q]=i,void();
int c=s[i]>>k&1;
if(p) trie[q][c^1]=trie[p][c^1];
trie[q][c]=++tot;
insert(i,k-1,trie[p][c],trie[q][c]);
latest[q]=std::max(latest[trie[q][0]],latest[trie[q][1]]);
}
int ask(int now,int val,int k,int limit){
if(k<0) return s[latest[now]]^val;
int c=val>>k&1;
if(latest[trie[now][c^1]]>=limit)
return ask(trie[now][c^1],val,k-1,limit);
else
return ask(trie[now][c],val,k-1,limit);
}
int main(){
// freopen(".in","r",stdin),freopen(".out","w",stdout);
read(n),read(m);
latest[0]=-1,root[0]=++tot;
insert(0,23,0,root[0]);
for(int i=1;i<=n;++i){
read(s[i]),s[i]^=s[i-1];
root[i]=++tot;
insert(i,23,root[i-1],root[i]);
}
for(int i=1;i<=m;++i){
static char op[2];scanf("%s",op);
if(op[0]=='A'){
static int x;read(x);
root[++n]=++tot,s[n]=s[n-1]^x;
insert(n,23,root[n-1],root[n]);
}
else{
static int l,r,x;read(l),read(r),read(x);
printf("%d\n",ask(root[r-1],x^s[n],23,l-1));
}
}
return 0;
}
简单数据结构
分析
#3:
区间把比一个数小的数字变成这个数,查询区间最小值.
因为查询内容与区间和值等无关,所以无需Segment Tree Beats中的方法实现,
直接在每个线段树节点上维护当前区间min值以及区间与哪个数取max的lazy标记即可
#4:
考虑操作2怎么解决,因为K的和值不超过5*10^6,考虑与其相关的做法.
对序列建一棵线段树,线段树上每个节点维护当前区间最小值的值和位置,同时用一个小根堆去维护最后的答案,线段树上每次query(l,r)返回一组{val,pos,l,r}表示[l,r]区间中最小值为val,位置为pos,按照val去建立这个小根堆.
开始把query(1,n)的结果入堆,然后重复K次以下操作:
每次弹出堆顶,显然这时的val是所剩下的数中的最小值,然后将query(l,pos-1)以及query(pos+1,r)的结果入堆,
这样得到的K个堆顶显然是最小的K个元素,判断-1后输出即可,
而关于时间复杂度,query与入堆的次数都是2K次,弹出的次数为K次,所以总复杂度为\(O(n+\sum K \cdot \log n)\).
#5,#6:
发现在#4与#3的维护区间min值并不会产生影响,于是将#3,#4一起实现,即可通过全部数据,时间复杂度\(O\left((n+\sum K) \cdot \log n\right)\),空间复杂度\(O(n+\sum K)\).
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<string>
#include<vector>
#include<list>
#include<deque>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<complex>
#include<cassert>
#define rg register
#define il inline
#define co const
#pragma GCC optimize ("O3")
using namespace std;
il char nc(){
static char buf[100000],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
il int read(int&x){
char ch=nc();int sum=0;
while(!(ch>='0'&&ch<='9'))ch=nc();
while(ch>='0'&&ch<='9')sum=sum*10+ch-48,ch=nc();
return x=sum;
}
typedef long long ll;
typedef pair<int,int> pii;
co int INF=0x7fffffff;
co int MAXN=5e5+7;
int n,m;
struct SegTree
{
int minv[MAXN<<2],pos[MAXN<<2];
int tag[MAXN<<2];
#define lson (now<<1)
#define rson (now<<1|1)
il void pushup(rg int now)
{
minv[now]=min(minv[lson],minv[rson]);
pos[now]=minv[lson]<minv[rson]?pos[lson]:pos[rson];
}
il void puttag(rg int now,rg int v)
{
minv[now]=max(minv[now],v);
tag[now]=max(tag[now],v);
}
il void pushdown(rg int now)
{
if(tag[now])
{
puttag(lson,tag[now]);
puttag(rson,tag[now]);
tag[now]=0;
}
}
il void build(rg int now,rg int l,rg int r)
{
if(l==r)
{
read(minv[now]);
pos[now]=l;
return;
}
rg int m=(l+r)>>1;
build(lson,l,m);
build(rson,m+1,r);
pushup(now);
}
il void modify(rg int now,rg int l,rg int r,rg int ql,rg int qr,rg int v)
{
if(ql<=l&&r<=qr)
{
puttag(now,v);
return;
}
pushdown(now);
rg int m=(l+r)>>1;
if(ql<=m)
modify(lson,l,m,ql,qr,v);
if(qr>=m+1)
modify(rson,m+1,r,ql,qr,v);
pushup(now);
}
il pii qmin(rg int now,rg int l,rg int r,rg int ql,rg int qr)
{
if(ql<=l&&r<=qr)
{
return pii(minv[now],pos[now]);
}
pushdown(now);
rg int m=(l+r)>>1;
if(qr<=m)
return qmin(lson,l,m,ql,qr);
if(ql>=m+1)
return qmin(rson,m+1,r,ql,qr);
pii x=qmin(lson,l,m,ql,qr),y=qmin(rson,m+1,r,ql,qr);
return x.first<y.first?x:y;
}
}T;
struct node
{
int val,pos,l,r;
il bool operator<(const node&rhs)const
{
return val>rhs.val;
}
};
priority_queue<node>H;
il void addH(rg int l,rg int r)
{
node t;
pii p=T.qmin(1,1,n,l,r);
t.val=p.first,t.pos=p.second;
t.l=l,t.r=r;
H.push(t);
}
int ans[MAXN];
il void query(rg int l,rg int r,rg int x,rg int k)
{
if(r-l+1<k)
{
puts("-1");
return;
}
while(!H.empty())
H.pop();
addH(l,r);
for(rg int i=1;i<=k;++i)
{
rg node t=H.top();
H.pop();
if(t.val>=x)
{
puts("-1");
return;
}
ans[i]=t.val;
if(t.l<=t.pos-1)
addH(t.l,t.pos-1);
if(t.pos+1<=t.r)
addH(t.pos+1,t.r);
}
for(rg int i=1;i<=k;++i)
{
printf("%d ",ans[i]);
}
puts("");
}
int main()
{
freopen("segtree.in","r",stdin);
freopen("segtree.out","w",stdout);
read(n);
T.build(1,1,n);
read(m);
rg int opt,l,r,x,k;
while(m--)
{
read(opt);
if(opt==1)
{
read(l);read(r);read(x);
T.modify(1,1,n,l,r,x);
}
else
{
read(l);read(r);read(x);read(k);
query(l,r,x,k);
}
}
// fclose(stdin);
// fclose(stdout);
return 0;
}