题解 english

传送门

好题 肝完这题感觉头巨痛

首先\(n \leqslant 1000\)的部分可以\(n^2\)单调队列,有30pts
然后考场上魔改了下单调栈,让它能顺便维护出以\(1~i-1\)为左端点的区间信息,又拿下op=1的30pts
其实这里第11、12个点的答案必定为0,但是没意识到

至于正解:
其实第一问就跑上面魔改单调栈解法就行,是基于这个性质:

  • \((a\oplus b)*k = \sum\limits_{i=0}^{30} (a\oplus b)*2^i*k\)
    然后就可以按位拆开求解了
    然后第二问利用可持久化trie树实现了查询区间内大于某个数的个数

「模板」可持久化trie树查询区间内大于某个数的个数:

int n, m;
int tot, rot[N], son[N][2], cnt[N];
#define son(a, b) son[a][b]
#define cnt(a) cnt[a]
void ins(int u, int p1, int p2, int dat) {
	if (u<0) return ;
	int s=bool((1<<u)&dat);
	son(p2, s)=++tot;
	cnt(son(p2, s))=cnt(son(p1, s))+1;
	son(p2, s^1)=son(p1, s^1);
	ins(u-1, son(p1, s), son(p2, s), dat);
}
int query(int u, int p1, int p2, int dat) {
	//cout<<"query "<<u<<endl;
	if (u<0) return 0;
	int s=bool((1<<u)&dat), ans=0;
	if (!s) ans=query(u-1, son(p1, 0), son(p2, 0), dat)+cnt(son(p2, 1))-cnt(son(p1, 1));
	else ans=query(u-1, son(p1, 1), son(p2, 1), dat);
	return ans;
}

signed main()
{
	#ifdef DEBUG
	freopen("1.in", "r", stdin);
	#endif
	
	n=read(); m=read();
	for (int i=1; i<=n; ++i) rot[i]=++tot, ins(25, rot[i-1], rot[i], read());
	for (int i=1,a,b,c,d; i<=m; ++i) {
		a=read(); b=read(); c=read();
		cout<<query(25, rot[a-1], rot[b], c)<<endl;
	}

	return 0;
}

但是这题要求区间内数先异或上一个数,再比较大小
其实稍稍改下query函数即可

int n, m;
int tot, rot[N], son[N][2], cnt[N];
#define son(a, b) son[a][b]
#define cnt(a) cnt[a]
void ins(int u, int p1, int p2, int dat) {
	if (u<0) return ;
	int s=bool((1<<u)&dat);
	son(p2, s)=++tot;
	cnt(son(p2, s))=cnt(son(p1, s))+1;
	son(p2, s^1)=son(p1, s^1);
	ins(u-1, son(p1, s), son(p2, s), dat);
}
int query(int u, int p1, int p2, int dat, int dlt) {
	//cout<<"query "<<u<<endl;
	if (u<0) return 0;
	int s=bool((1<<u)&dat), s2=bool((1<<u)&dlt), ans=0;
	if (!s2) {
		if (!s) ans=query(u-1, son(p1, 0), son(p2, 0), dat, dlt)+cnt(son(p2, 1))-cnt(son(p1, 1));
		else ans=query(u-1, son(p1, 1), son(p2, 1), dat, dlt);
		return ans;
	}
	else {
		if (!s) ans=query(u-1, son(p1, 1), son(p2, 1), dat, dlt)+cnt(son(p2, 0))-cnt(son(p1, 0));
		else ans=query(u-1, son(p1, 0), son(p2, 0), dat, dlt);
		return ans;
	}
}

signed main()
{
	#ifdef DEBUG
	freopen("1.in", "r", stdin);
	#endif
	
	n=read(); m=read();
	for (int i=1; i<=n; ++i) rot[i]=++tot, ins(25, rot[i-1], rot[i], read());
	for (int i=1,a,b,c,d; i<=m; ++i) {
		a=read(); b=read(); c=read(); d=read();
		cout<<query(25, rot[a-1], rot[b], c, d)<<endl;
	}

	return 0;
}

那有了这个东西,我们考虑原题
要求区间端点异或值大于区间内最大值
那可以分别预处理出每个值所“管辖“的区间,单调栈扫两遍即可,这样不枚举端点而是枚举最大值的话就可以少个\(O(n)\)
有个神仙转化:
每个值所“管辖“的区间至多相互包含,但不能相交
这跟线段树的结构相似,所以区间总长度至多为nlogn
对于每个区间,直接暴力枚举左右区间中较小的一个
树链剖分的折半思想可知,至多枚举log次,枚举的总长度为区间总长度
所以时间复杂度\(O(nlog^2n)\)

Code:

#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 100010
#define ll long long 
#define ld long double
#define usd unsigned
#define ull unsigned long long
//#define int long long 

#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
char buf[1<<21], *p1=buf, *p2=buf;
inline int read() {
	int ans=0, f=1; char c=getchar();
	while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
	while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
	return ans*f;
}

int n, op;
const ll p=1e9+7;
ll a[N], ans1, ans2;

namespace force{
	struct que{ll val; int pos; inline void build(ll v_, int p_){val=v_; pos=p_;}}q[N];
	void solve() {
		ll t;
		for (int len=2,l,r; len<=n; ++len) {
			l=1; r=0;
			for (int i=1; i<=len; ++i) {
				while (l<=r && a[i]>=q[r].val) --r;
				q[++r].build(a[i], i);
			}
			for (int i=len,l2; i<=n; ++i) {
				l2=i-len+1;
				t=a[l2]^a[i];
				while (l<=r && q[l].pos<l2) ++l;
				while (l<=r && a[i]>=q[r].val) --r;
				q[++r].build(a[i], i);
				//cout<<"+= "<<t*q[l].val<<endl;
				ans1 = (ans1+t*q[l].val)%p;
				ans2 = (ans2+((t>q[l].val)?q[l].val:0))%p;
			}
		}
		if (op==1) printf("%lld\n", ans1%p);
		else if (op==2) printf("%lld\n", ans2%p);
		else printf("%lld\n%lld\n", ans1%p, ans2%p);
		exit(0);
	}
}

namespace task1{
	struct que2{
		struct que{ll val, cnt, sum; inline void build(ll v_, ll p_, ll s_){val=v_; cnt=p_; sum=s_;}}q[N];
		int l, r;
		que2():l(1),r(0){}
		void upd(ll w) {
			if (l>r || w<q[r].val) return ;
			ll cnt=0;
			while (l<=r && w>=q[r].val) cnt+=q[r--].cnt;
			ll s=(l<=r)?q[r].sum:0;
			q[++r].build(w, cnt, (s+w*(cnt)%p)%p);
		}
		void add(ll w) {
			ll cnt=0;
			while (l<=r && w>=q[r].val) cnt+=q[r--].cnt;
			ll s=(l<=r)?q[r].sum:0;
			q[++r].build(w, cnt+1, (s+w*(cnt+1)%p)%p);
		}
		ll ask(ll w) {return l<=r?q[r].sum:0;}
	}que[22][2];
	void solve() {
		for (int i=1; i<=n; ++i) {
			for (int j=0; j<22; ++j) {
				int s=(a[i]&(1<<j))?1:0;
				que[j][s^1].upd(a[i]);
				ans1=(ans1+(que[j][s^1].ask(a[i]))*(1<<j)%p)%p;
				que[j][s].add(a[i]);
			}
		} 
		printf("%lld\n", ans1%p);
	}
}

namespace task2{
	int l[N], r[N];
	struct que{ll val; int pos; inline void build(ll v_, int p_){val=v_; pos=p_;}}q[N];
	int ql, qr;
	
	const int SIZE=N*50;
	int tot, rot[N], son[SIZE][2], cnt[SIZE];
	#define son(a, b) son[a][b]
	#define cnt(a) cnt[a]
	void ins(int u, int p1, int p2, int dat) {
		if (u<0) return ;
		int s=bool((1<<u)&dat);
		son(p2, s)=++tot;
		cnt(son(p2, s))=cnt(son(p1, s))+1;
		son(p2, s^1)=son(p1, s^1);
		ins(u-1, son(p1, s), son(p2, s), dat);
		//if (u>1) pushup(p2);
	}
	int query(int u, int p1, int p2, int dat, int dlt) {
		//cout<<"query "<<u<<endl;
		if (u<0) return 0;
		int s=bool((1<<u)&dat), s2=bool((1<<u)&dlt), ans=0;
		if (!s2) {
			if (!s) ans=query(u-1, son(p1, 0), son(p2, 0), dat, dlt)+cnt(son(p2, 1))-cnt(son(p1, 1));
			else ans=query(u-1, son(p1, 1), son(p2, 1), dat, dlt);
			return ans;
		}
		else {
			if (!s) ans=query(u-1, son(p1, 1), son(p2, 1), dat, dlt)+cnt(son(p2, 0))-cnt(son(p1, 0));
			else ans=query(u-1, son(p1, 0), son(p2, 0), dat, dlt);
			return ans;
		}
	}
	
	void solve() {
		ql=qr=1; q[1].build(INF, 0);
		for (int i=1; i<=n; ++i) {
			while (ql<=qr && q[qr].val<a[i]) --qr;
			l[i]=q[qr].pos+1;
			q[++qr].build(a[i], i);
		}
		ql=qr=1; q[1].build(INF, n+1);
		for (int i=n; i; --i) {
			while (ql<=qr && q[qr].val<=a[i]) --qr;
			r[i]=q[qr].pos-1;
			q[++qr].build(a[i], i);
		}
		//cout<<"l: "; for (int i=1; i<=n; ++i) cout<<l[i]<<' '; cout<<endl;
		//cout<<"r: "; for (int i=1; i<=n; ++i) cout<<r[i]<<' '; cout<<endl;
		for (int i=1; i<=n; ++i) rot[i]=++tot, ins(21, rot[i-1], rot[i], a[i]);
		for (int i=1; i<=n; ++i) {
			if (l[i]==r[i]) continue;
			if (i-l[i]<r[i]-i) for (int j=l[i]; j<=i; ++j) ans2=(ans2+a[i]*query(21, rot[i-1], rot[r[i]], a[i], a[j])%p)%p; //, cout<<"query "<<i<<' '<<r[i]<<' '<<j<<endl;
			else for (int j=i; j<=r[i]; ++j) ans2=(ans2+a[i]*query(21, rot[l[i]-1], rot[i], a[i], a[j])%p)%p; //, cout<<"query "<<l[i]<<' '<<i<<' '<<j<<endl;
		}
		printf("%lld\n", ans2%p);
	}
}

signed main()
{
	#ifdef DEBUG
	freopen("1.in", "r", stdin);
	#endif
	
	n=read(); op=read();
	for (int i=1; i<=n; ++i) a[i]=read();
	if (op==1||op==3) task1::solve();
	if (op==2||op==3) task2::solve();

	return 0;
}
posted @ 2021-07-13 06:37  Administrator-09  阅读(10)  评论(0编辑  收藏  举报