Ynoi合集

杂项:

P5608 [Ynoi2013] 文化课

先考虑没有修改的情况,此时我们可以直接在线段树上维护,记录 \(ans\) 表示区间 \([l,r]\) 的表达式的值,然后 \(push\_up\) 时如果中间的符号为 \(+\) 那么直接将当前节点的 \(ans\) 更新为两侧区间的和,如果为 $\times $ 那么我们可以维护一个 \(lw,llen\)\(rw,rlen\) 分别表示当前区间最左边和右边一个极大连乘段的值和个数,这连个的转移是较为容易的,然后 \(ans\) 的转移就是左边 \(ans\) 减去最右边的极大连乘段的值加上右边 \(ans\) 减去最左边的极大连乘段的值最后加上左边最右边的极大连乘段的值乘上右边最左边的极大连乘段的值。

然后考虑只有操作二和操作三的情况。我们发现只需要维护区间和和区间乘积然后每次更新直接整段更新打懒标记即可。

最难处理的是操作一,我们发现我们实际上会将一整段区间修改为一个多项式 \(\sum_i cnt_ix^i\) 的值,我们发现这个多项式需要满足 \(\sum_i cnt_i\times i\le len\) ,所以 \(cnt_i\) 不为 \(0\) 的项实际只有 \(O(\sqrt{len})\) 项,我们可以直接暴力遍历这个多项式来更新 \(ans\) ,至于多项式的合并可以直接用类似于归并的方式,然后还可以记一个 \(vis\) 表示这个多项式需不需要更新来优化常数,然后我们的复杂度就变成了 \(O(n\sqrt{n}log_2n)\) 了吗?

我们发现每次区间修改访问到的位置其实是 \(1,2,4\dots 2^i\) 这些长度的区间,而 \(\sum_{i\land 2^i\le n} \sqrt{2^i}\) ,这个式子算出来应该是和 \(\sum_{i\land {(2^i)}^2\le n}2^i\) 一个量级,而后者是 \(O(\sqrt{n})\) 的。所以这个解法的复杂度其实是 \(O(n\sqrt{n})\) 的,可以通过。

所以我认为 \(O(nlog_2n)\) 的数据结构不应该包含魔改线段树。

代码:

#include<bits/stdc++.h>
using namespace std;
#define il inline
#define lid id<<1
#define rid id<<1|1
struct IO
{
	static const int Size=(1<<21);
	char buf[Size],*p1,*p2;
	int st[105],Top;
	~IO(){clear();}
	il void clear(){fwrite(buf,1,Top,stdout);Top=0;}
	il char gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,Size,stdin),p1==p2)?EOF:*p1++;}
	il void pc(const char c){Top==Size&&(clear(),0);buf[Top++]=c;}
	il IO& operator >>(char& c){while(c=gc(),c==' ' || c=='\n' || c=='\r');return *this;}
	template<typename T>il IO& operator >>(T& x)
	{
		x=0;bool f=0;char c=gc();
		while(!isdigit(c)){if(c=='-') f=1;c=gc();}
		while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();}
		f?x=-x:0;
		return *this;
	}
	il IO& operator >>(string& s)
	{
		s="";char c=gc();
		while(c==' ' || c=='\n' || c=='\r') c=gc();
		while(c!=' ' && c!='\n' && c!='\r' && c!=EOF) s+=c,c=gc();
		return *this;
	}
	il IO& operator <<(const char c){pc(c);return *this;}
	template<typename T> il IO& operator <<(T x)
	{
		if(x<0) pc('-'),x=-x;
		do st[++st[0]]=x%10,x/=10;while(x);
		while(st[0]) pc(st[st[0]--]+'0');
		return *this;
	}
	il IO& operator <<(const string s){for(auto c:s) pc(c);return *this;}
	il IO& operator <<(const char* c){for(int i=0;c[i];i++) pc(c[i]);return *this;}
} fin,fout;
const int N=100010,mod=1e9+7;
int p[N],mx;
unsigned int a[N];
struct node
{
	int lazya,lazyp;
	int mul,sum,ans,fl,lw,rw,nd,op,llen,rlen,la,ra;
	vector<pair<int,int> > val; 
} t[N<<2];
il int qpow(int x,int y)
{
	int ret=1;
	while(y)
	{
		if(y&1) ret=1ll*ret*x%mod;
		x=1ll*x*x%mod;y>>=1;
	}
	return ret;
}
il void push_up(int id,int l,int r)
{
	int mid=(l+r)>>1;
	t[id].sum=(t[lid].sum+t[rid].sum)%mod;
	t[id].mul=1ll*t[lid].mul*t[rid].mul%mod;
	t[id].la=t[lid].la,t[id].ra=t[rid].ra;
	if(!t[id].op)
	{
		t[id].lw=t[lid].lw,t[id].llen=t[lid].llen;
		t[id].rw=t[rid].rw,t[id].rlen=t[rid].rlen;
		t[id].fl=0;
		t[id].ans=(t[lid].ans+t[rid].ans)%mod;
		if(t[id].nd)
		{
			int i=0,j=0,lst=0;
			vector<pair<int,int> > ().swap(t[id].val);
			while(i<(int)t[lid].val.size() && j<(int)t[rid].val.size())
			{
				if(t[lid].val[i].first<t[rid].val[j].first)
				{
					auto tmp=t[lid].val[i++];
					if(tmp.first==lst) t[id].val[(int)t[id].val.size()-1].second+=tmp.second;
					else t[id].val.push_back(tmp),lst=tmp.first;
				}
				else
				{
					auto tmp=t[rid].val[j++];
					if(tmp.first==lst) t[id].val[(int)t[id].val.size()-1].second+=tmp.second;
					else t[id].val.push_back(tmp),lst=tmp.first;
				}
			}
			while(i<(int)t[lid].val.size())
			{
				auto tmp=t[lid].val[i++];
				if(tmp.first==lst) t[id].val[(int)t[id].val.size()-1].second+=tmp.second;
				else t[id].val.push_back(tmp),lst=tmp.first;
			}
			while(j<(int)t[rid].val.size())
			{
				auto tmp=t[rid].val[j++];
				if(tmp.first==lst) t[id].val[(int)t[id].val.size()-1].second+=tmp.second;
				else t[id].val.push_back(tmp),lst=tmp.first;
			}
			t[id].nd=0; 
		}
	}
	else
	{
		t[id].ans=(0ll+t[lid].ans+mod-t[lid].rw+t[rid].ans+mod-t[rid].lw+1ll*t[lid].rw*t[rid].lw)%mod;
		if(t[lid].fl) t[id].lw=1ll*t[lid].mul*t[rid].lw%mod,t[id].llen=mid-l+1+t[rid].llen;
		else t[id].lw=t[lid].lw,t[id].llen=t[lid].llen;
		if(t[rid].fl) t[id].rw=1ll*t[rid].mul*t[lid].rw%mod,t[id].rlen=r-mid+t[lid].rlen;
		else t[id].rw=t[rid].rw,t[id].rlen=t[rid].rlen;
		t[id].fl=t[lid].fl&t[rid].fl;
		if(t[id].nd)
		{
			int wl=t[lid].rlen,wr=t[rid].llen,fl=0;
			int i=0,j=0,lst=0;
			vector<pair<int,int> > ().swap(t[id].val);
			while(i<(int)t[lid].val.size() && j<(int)t[rid].val.size())
			{
				if(!fl && wl+wr<t[lid].val[i].first && wl+wr<t[rid].val[j].first)
				{
					fl=1;
					if(wl+wr==lst) t[id].val[(int)t[id].val.size()-1].second++;
					else t[id].val.push_back({wl+wr,1}),lst=wl+wr;
					continue;
				}
				if(t[lid].val[i].first<t[rid].val[j].first)
				{
					auto tmp=t[lid].val[i++];
					if(tmp.first==wl) tmp.second--;
					if(!tmp.second) continue;
					if(tmp.first==lst) t[id].val[(int)t[id].val.size()-1].second+=tmp.second;
					else t[id].val.push_back(tmp),lst=tmp.first;
				}
				else
				{
					auto tmp=t[rid].val[j++];
					if(tmp.first==wr) tmp.second--;
					if(!tmp.second) continue;
					if(tmp.first==lst) t[id].val[(int)t[id].val.size()-1].second+=tmp.second;
					else t[id].val.push_back(tmp),lst=tmp.first;
				}
			}
			while(i<(int)t[lid].val.size())
			{
				if(!fl && wl+wr<t[lid].val[i].first)
				{
					fl=1;
					if(wl+wr==lst) t[id].val[(int)t[id].val.size()-1].second++;
					else t[id].val.push_back({wl+wr,1}),lst=wl+wr;
					continue;
				}
				auto tmp=t[lid].val[i++];
				if(tmp.first==wl) tmp.second--;
				if(!tmp.second) continue;
				if(tmp.first==lst) t[id].val[(int)t[id].val.size()-1].second+=tmp.second;
				else t[id].val.push_back(tmp),lst=tmp.first;
			}
			while(j<(int)t[rid].val.size())
			{
				if(!fl && wl+wr<t[rid].val[j].first)
				{
					fl=1;
					if(wl+wr==lst) t[id].val[(int)t[id].val.size()-1].second++;
					else t[id].val.push_back({wl+wr,1}),lst=wl+wr;
					continue;
				}
				auto tmp=t[rid].val[j++];
				if(tmp.first==wr) tmp.second--;
				if(!tmp.second) continue;
				if(tmp.first==lst) t[id].val[(int)t[id].val.size()-1].second+=tmp.second;
				else t[id].val.push_back(tmp),lst=tmp.first;
			}
			if(!fl)
			{
				fl=1;
				if(wl+wr==lst) t[id].val[(int)t[id].val.size()-1].second++;
				else t[id].val.push_back({wl+wr,1}),lst=wl+wr;
			}
			t[id].nd=0; 
		}
	}
}
il void lazy_chp(int id,int l,int r,int x)
{
	if(l==r) return;
	t[id].lazyp=x;
	if(!x)
	{
		t[id].ans=t[id].sum;
		t[id].lw=t[id].la,t[id].llen=1;
		t[id].rw=t[id].ra,t[id].rlen=1;
		t[id].op=x;
		vector<pair<int,int> > ().swap(t[id].val);
		t[id].val.push_back({1,r-l+1});
		t[id].fl=0;
	}
	else
	{
		t[id].ans=t[id].mul;
		t[id].lw=t[id].mul,t[id].llen=r-l+1;
		t[id].rw=t[id].mul,t[id].rlen=r-l+1;
		t[id].op=x;
		vector<pair<int,int> > ().swap(t[id].val);
		t[id].val.push_back({r-l+1,1});
		t[id].fl=1;
	}
}
il void lazy_cha(int id,int l,int r,int x)
{
	t[id].lazya=x;
	t[id].sum=1ll*(r-l+1)*x%mod;
	t[id].mul=qpow(x,r-l+1);
	t[id].la=t[id].ra=x;
	t[id].lw=qpow(x,t[id].llen);
	t[id].rw=qpow(x,t[id].rlen);
	t[id].ans=0;
	for(auto i:t[id].val) t[id].ans=(t[id].ans+1ll*i.second*qpow(x,i.first))%mod;
}
il void push_down(int id,int l,int r)
{
	int mid=(l+r)>>1; 
	if(t[id].lazyp!=-1)
	{
		lazy_chp(lid,l,mid,t[id].lazyp);
		lazy_chp(rid,mid+1,r,t[id].lazyp);
		t[id].lazyp=-1;
	}
	if(t[id].lazya!=-1)
	{
		lazy_cha(lid,l,mid,t[id].lazya);
		lazy_cha(rid,mid+1,r,t[id].lazya);
		t[id].lazya=-1;
	}
}
il void build(int id,int l,int r)
{
	t[id].lazya=t[id].lazyp=-1;
	t[id].nd=1;
	t[id].la=a[l],t[id].ra=a[r];
	if(l==r)
	{
		t[id].mul=t[id].sum=t[id].ans=a[l];
		t[id].lw=t[id].rw=a[l],t[id].fl=1;
		t[id].llen=t[id].rlen=1;
		t[id].val.push_back({1,1});
		return;
	}
	int mid=(l+r)>>1;
	build(lid,l,mid);
	build(rid,mid+1,r);
	t[id].op=p[mid];
	push_up(id,l,r);
}
il pair<int,pair<int,pair<int,int> > > query(int id,int l,int r,int L,int R)
{
	if(L<=l && r<=R) return {t[id].ans,{t[id].fl,{t[id].lw,t[id].rw}}};
	push_down(id,l,r);
	int mid=(l+r)>>1;
	if(R<=mid) return query(lid,l,mid,L,R);
	else if(L>mid) return query(rid,mid+1,r,L,R);
	else
	{
		auto lans=query(lid,l,mid,L,R);
		auto rans=query(rid,mid+1,r,L,R);
		if(t[id].op==0) return {(lans.first+rans.first)%mod,{0,{lans.second.second.first,rans.second.second.second}}};
		else
		{
			int ans=(0ll+lans.first+mod-lans.second.second.second+rans.first+mod-rans.second.second.first+1ll*lans.second.second.second*rans.second.second.first)%mod;
			int lw=(lans.second.first)?1ll*rans.second.second.first*lans.second.second.first%mod:lans.second.second.first;
			int rw=(rans.second.first)?1ll*lans.second.second.second*rans.second.second.second%mod:rans.second.second.second;
			int tag=lans.second.first&rans.second.first;
			return {ans,{tag,{lw,rw}}};
		}
	}
}
il void changep(int id,int l,int r,int L,int R,int x)
{
	if(L<=l && r-1<=R)
	{
		lazy_chp(id,l,r,x);
		return;
	}
	push_down(id,l,r);
	t[id].nd=1;
	int mid=(l+r)>>1;
	if(L<=mid && R>=mid) t[id].op=x;
	if(L<=mid-1) changep(lid,l,mid,L,R,x);
	if(R>mid) changep(rid,mid+1,r,L,R,x);
	push_up(id,l,r);
}
il void changea(int id,int l,int r,int L,int R,int x)
{
	if(L<=l && r<=R)
	{
		lazy_cha(id,l,r,x);
		return;
	}
	push_down(id,l,r);
	int mid=(l+r)>>1;
	if(L<=l) t[id].la=x;
	if(R>=r) t[id].ra=x;
	if(L<=mid) changea(lid,l,mid,L,R,x);
	if(R>mid) changea(rid,mid+1,r,L,R,x);
	push_up(id,l,r);
}
signed main()
{
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	int n,m;fin>>n>>m;
	for(int i=1;i<=n;i++) fin>>a[i],a[i]%=mod;
	for(int i=1;i<n;i++) fin>>p[i];
	build(1,1,n);
	while(m--)
	{
		int op,l,r;
		unsigned int x;fin>>op>>l>>r;
		if(op!=3) fin>>x;
		if(op==1)
		{
			x%=mod;
			changea(1,1,n,l,r,x);
		}
		if(op==2) changep(1,1,n,l,r,x);
		if(op==3) fout<<query(1,1,n,l,r).first<<"\n";
	}
	return 0;
}

P13826 [Ynoi Easy Round 2026] 寒蝉鸣泣之时

首先看到矩阵修改的第一反应肯定是离线下来以后对其中一维进行扫描线,然后另一位转化为区间加/减 \(1\) ,然后我们会发现我们此时需要求的其实是全局的 \(\frac{n}{m}\) 种数的个数,我们期望能有一个 \(O(\sqrt{n})-O(1)\) 的分块做法,但这不太现实,只能做到 \(O(\sqrt{n})-O(\sqrt{n})\) 的做法。

我们可以换一个思路,改为维护每一个块内的最小值和最大值(设其差值为 \(len\) ),每次对于一个块内的枚举就只枚举有效值,也就是在最小值到最大值之间的值。来对答案做贡献。我们现在证明为什么这样做复杂度是对的。

我们先对原数组做一个差分,容易发现 \(len\) 应该等于差分数组上块的左端点加一到块的右端点的和,每一次修改都会让一个位置加一,一个位置减一,贡献是 \(O(1)\) 的。那么 \(O(n)\) 次修改就会造成 \(O(n)\) 次贡献,所以一步的贡献为 \(O(n)\) ,则一次询问的复杂度为 \(O(\frac{n}{m})=O(\sqrt{n})\) ,所以总共 \(O(n)\) 次修改,复杂度为 \(O(n\sqrt{n})\) 。证毕。

由于分块的数组要开 \(O(n\sqrt{n})\) 的,所以空间可能会卡得比较紧,可以通过开 short 来解决。

代码:

#include<bits/stdc++.h>
using namespace std;
#define il inline
#define ll long long
bool St;
struct IO
{
	static const int Size=(1<<21);
	char buf[Size],*p1,*p2;
	int st[105],Top;
	~IO(){clear();}
	il void clear(){fwrite(buf,1,Top,stdout);Top=0;}
	il char gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,Size,stdin),p1==p2)?EOF:*p1++;}
	il void pc(const char c){Top==Size&&(clear(),0);buf[Top++]=c;}
	il IO& operator >>(char& c){while(c=gc(),c==' ' || c=='\n' || c=='\r');return *this;}
	template<typename T>il IO& operator >>(T& x)
	{
		x=0;bool f=0;char zxc=gc();
		while(!isdigit(zxc)){if(zxc=='-') f=1;zxc=gc();}
		while(isdigit(zxc)){x=(x<<1)+(x<<3)+(zxc^48);zxc=gc();}
		f?x=-x:0;
		return *this;
	}
	il IO& operator >>(string& s)
	{
		s="";char c=gc();
		while(c==' ' || c=='\n' || c=='\r') c=gc();
		while(c!=' ' && c!='\n' && c!='\r' && c!=EOF) s+=c,c=gc();
		return *this;
	}
	il IO& operator <<(const char c){pc(c);return *this;}
	template<typename T> il IO& operator <<(T x)
	{
		if(x<0) pc('-'),x=-x;
		do st[++st[0]]=x%10,x/=10;while(x);
		while(st[0]) pc(st[st[0]--]+'0');
		return *this;
	}
	il IO& operator <<(const string s){for(auto c:s) pc(c);return *this;}
	il IO& operator <<(const char* c){for(int i=0;c[i];i++) pc(c[i]);return *this;}
} fin,fout;
const int N=300010,B=550;
int mn[N],mx[N],a[N],val[N],b[N],fl[N],n,m;
ll ans[N];
short mp[N/B+10][N];
il void del(int idx)
{
	for(int i=(mx[idx]+fl[idx])/m*m;i>=mn[idx]+fl[idx];i-=m) val[i/m]-=mp[idx][i-fl[idx]-mn[idx]];
}
il void add(int idx)
{
	for(int i=(mx[idx]+fl[idx])/m*m;i>=mn[idx]+fl[idx];i-=m) val[i/m]+=mp[idx][i-fl[idx]-mn[idx]];
}
il void del_mp(int idx)
{
	for(int i=(idx-1)*B+1;i<=min(n,idx*B);i++) mp[idx][a[i]-mn[idx]]--;
}
il void rebuild(int idx)
{
	mn[idx]=1e9;mx[idx]=-1e9;
	for(int i=(idx-1)*B+1;i<=min(n,idx*B);i++)
	{
		mx[idx]=max(mx[idx],a[i]);
		mn[idx]=min(mn[idx],a[i]);
	}
	for(int i=(idx-1)*B+1;i<=min(n,idx*B);i++) mp[idx][a[i]-mn[idx]]++;
}
il void add(int l,int r,int k)
{
	if(b[l]==b[r])
	{
		del(b[l]);
		del_mp(b[l]);
		for(int i=l;i<=r;i++) a[i]+=k;
		rebuild(b[l]);
		add(b[l]);
		return;
	}
	del(b[l]);
	del_mp(b[l]);
	for(int i=l;i<=b[l]*B;i++) a[i]+=k;
	rebuild(b[l]);
	add(b[l]);
	
	
	del(b[r]);
	del_mp(b[r]);
	for(int i=(b[r]-1)*B+1;i<=r;i++) a[i]+=k;
	rebuild(b[r]);
	add(b[r]);
	
	
	for(int i=b[l]+1;i<b[r];i++) del(i),fl[i]+=k,add(i);
}
struct node
{
	int l,r,val;
};
vector<node> tmp[N];
bool Ed;
signed main()
{
//	freopen("count.in","r",stdin);
//	freopen("count.out","w",stdout);
	cerr<<(&St-&Ed)/1024.0/1024.0<<"\n";
	fin>>n>>m;
	for(int i=1;i<=n;i++) b[i]=(i-1)/B+1;
	for(int i=1;i<=n;i++)
	{
		int x,xx,y,yy;fin>>x>>xx>>y>>yy;
		tmp[x].push_back({y,yy-1,1});
		tmp[xx].push_back({y,yy-1,-1});
	}
	for(int i=1;i<=b[n];i++) mp[i][0]=min(B,n-(i-1)*B);
	for(int i=1;i<=n;i++)
	{
		for(auto j:tmp[i]) add(j.l,j.r,j.val);
		for(int j=1;j<=n/m;j++) ans[j]+=val[j];
	}
	for(int i=1;i<=n/m;i++) fout<<ans[i]<<"\n";
	return 0;
}

level 0:

P6328 我是仙人掌

bitset简单题,用bitset+bfs维护一下与1-n中每个节点不超过x的节点有哪些,求答案时或起来就好了。

代码:

#include<bits/stdc++.h>
using namespace std;
#define il inline
#define int long long
struct IO
{
	static const int Size=(1<<21);
	char buf[Size],*p1,*p2;
	int st[105],Top;
	~IO(){clear();}
	il void clear(){fwrite(buf,1,Top,stdout);Top=0;}
	il char gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,Size,stdin),p1==p2)?EOF:*p1++;}
	il void pc(const char c){Top==Size&&(clear(),0);buf[Top++]=c;}
	il IO& operator >>(char& c){while(c=gc(),c==' ' || c=='\n' || c=='\r');return *this;}
	template<typename T>il IO& operator >>(T& x)
	{
		x=0;bool f=0;char c=gc();
		while(!isdigit(c)){if(c=='-') f=1;c=gc();}
		while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();}
		f?x=-x:0;
		return *this;
	}
	il IO& operator >>(string& s)
	{
		s="";char c=gc();
		while(c==' ' || c=='\n' || c=='\r') c=gc();
		while(c!=' ' && c!='\n' && c!='\r' && c!=EOF) s+=c,c=gc();
		return *this;
	}
	il IO& operator <<(const char c){pc(c);return *this;}
	template<typename T> il IO& operator <<(T x)
	{
		if(x<0) pc('-'),x=-x;
		do st[++st[0]]=x%10,x/=10;while(x);
		while(st[0]) pc(st[st[0]--]+'0');
		return *this;
	}
	il IO& operator <<(const string s){for(auto c:s) pc(c);return *this;}
	il IO& operator <<(const char* c){for(int i=0;c[i];i++) pc(c[i]);return *this;}
} fin,fout;
const int N=1010;
int maxd[N],d[N];
vector<int> g[N];
bitset<N> st[N][N];
queue<int> Q;
signed main()
{
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	int n,m,q;fin>>n>>m>>q;
	for(int i=1;i<=m;i++)
	{
		int u,v;fin>>u>>v;
		g[u].push_back(v);
		g[v].push_back(u);
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++) d[j]=0;
		Q.push(i);
		while(!Q.empty())
		{
			int u=Q.front();Q.pop();
			maxd[i]=max(maxd[i],d[u]);
			st[i][d[u]][u]=1;
			for(int v:g[u])
			{
				if(!d[v] && v!=i) d[v]=d[u]+1,Q.push(v);
			}
		}
		for(int j=1;j<=maxd[i];j++) st[i][j]|=st[i][j-1];
	}
	while(q--)
	{
		int k;fin>>k;
		bitset<N> tmp=0;
		for(int i=1;i<=k;i++)
		{
			int x,y;fin>>x>>y;
			tmp|=st[x][min(maxd[x],y)];
		}
		fout<<tmp.count()<<"\n";
	}
	return 0;
}

P6327 区间加区间sin和

三角函数简单题。由和角公式\(\sin(\alpha+\beta)=\sin(\alpha)\cos(\beta)+\cos(\alpha)\sin(\beta)\)\(\cos(\alpha+\beta)=\cos(\alpha)\cos(\beta)-\sin(\alpha)\sin(\beta)\)放进push_down里跑一下就好了。

代码:

#include<bits/stdc++.h>
using namespace std;
#define il inline
#define int long long
#define lid id<<1
#define rid id<<1|1
struct IO
{
	static const int Size=(1<<21);
	char buf[Size],*p1,*p2;
	int st[105],Top;
	~IO(){clear();}
	il void clear(){fwrite(buf,1,Top,stdout);Top=0;}
	il char gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,Size,stdin),p1==p2)?EOF:*p1++;}
	il void pc(const char c){Top==Size&&(clear(),0);buf[Top++]=c;}
	il IO& operator >>(char& c){while(c=gc(),c==' ' || c=='\n' || c=='\r');return *this;}
	template<typename T>il IO& operator >>(T& x)
	{
		x=0;bool f=0;char c=gc();
		while(!isdigit(c)){if(c=='-') f=1;c=gc();}
		while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();}
		f?x=-x:0;
		return *this;
	}
	il IO& operator >>(string& s)
	{
		s="";char c=gc();
		while(c==' ' || c=='\n' || c=='\r') c=gc();
		while(c!=' ' && c!='\n' && c!='\r' && c!=EOF) s+=c,c=gc();
		return *this;
	}
	il IO& operator <<(const char c){pc(c);return *this;}
	template<typename T> il IO& operator <<(T x)
	{
		if(x<0) pc('-'),x=-x;
		do st[++st[0]]=x%10,x/=10;while(x);
		while(st[0]) pc(st[st[0]--]+'0');
		return *this;
	}
	il IO& operator <<(const string s){for(auto c:s) pc(c);return *this;}
	il IO& operator <<(const char* c){for(int i=0;c[i];i++) pc(c[i]);return *this;}
} fin,fout;
const int N=200010;
int a[N];
struct TreeNode
{
	double scos,ssin;
	int lazy;
} t[N<<2];
il void push_up(int id)
{
	t[id].ssin=t[lid].ssin+t[rid].ssin;
	t[id].scos=t[lid].scos+t[rid].scos;
}
il void lazy_add(int id,int k)
{
	t[id].lazy+=k;
	double s1=t[id].ssin,s2=t[id].scos;
	t[id].ssin=s1*cos(k)+s2*sin(k);
	t[id].scos=s2*cos(k)-s1*sin(k);
}
il void push_down(int id)
{
	if(t[id].lazy)
	{
		lazy_add(lid,t[id].lazy);
		lazy_add(rid,t[id].lazy);
		t[id].lazy=0;
	}
}
il void build(int id,int l,int r)
{
	if(l==r)
	{
		t[id].ssin=sin(a[l]);
		t[id].scos=cos(a[l]);
		return;
	}
	int mid=(l+r)>>1;
	build(lid,l,mid);
	build(rid,mid+1,r);
	push_up(id);
}
il void add(int id,int l,int r,int L,int R,int k)
{
	if(L<=l && r<=R)
	{
		lazy_add(id,k);
		return;
	}
	push_down(id);
	int mid=(l+r)>>1;
	if(L<=mid) add(lid,l,mid,L,R,k);
	if(R>mid) add(rid,mid+1,r,L,R,k);
	push_up(id);
}
il double query_ssin(int id,int l,int r,int L,int R)
{
	if(L<=l && r<=R) return t[id].ssin;
	push_down(id);
	int mid=(l+r)>>1;
	double ret=0;
	if(L<=mid) ret+=query_ssin(lid,l,mid,L,R);
	if(R>mid) ret+=query_ssin(rid,mid+1,r,L,R);
	return ret;
}
signed main()
{
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	int n;fin>>n;
	for(int i=1;i<=n;i++) fin>>a[i];
	build(1,1,n);
	int q;fin>>q;
	while(q--)
	{
		int op,l,r;fin>>op>>l>>r;
		if(op==1)
		{
			int k;fin>>k;
			add(1,1,n,l,r,k);
		}
		else cout<<fixed<<setprecision(1)<<query_ssin(1,1,n,l,r)<<"\n";
	}
	return 0;
}

level 1:

P5354 由乃的OJ

首先二进制考虑按位贪心,用树剖+线段树记录树链u到v的第i个二进制位放进去0/1后出来的值,可以做到\(O(mlog^2nlogV)\),考虑继续优化,发现所有位都可以一起转移,故可以做到\(O(mlog^2n+mlogV)\),可以通过。

代码:

#include<bits/stdc++.h>
using namespace std;
#define il inline
#define ull unsigned long long
#define lid id<<1
#define rid id<<1|1
struct IO
{
	static const int Size=(1<<21);
	char buf[Size],*p1,*p2;
	int st[105],Top;
	~IO(){clear();}
	il void clear(){fwrite(buf,1,Top,stdout);Top=0;}
	il char gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,Size,stdin),p1==p2)?EOF:*p1++;}
	il void pc(const char c){Top==Size&&(clear(),0);buf[Top++]=c;}
	il IO& operator >>(char& c){while(c=gc(),c==' ' || c=='\n' || c=='\r');return *this;}
	template<typename T>il IO& operator >>(T& x)
	{
		x=0;bool f=0;char c=gc();
		while(!isdigit(c)){if(c=='-') f=1;c=gc();}
		while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();}
		f?x=-x:0;
		return *this;
	}
	il IO& operator >>(string& s)
	{
		s="";char c=gc();
		while(c==' ' || c=='\n' || c=='\r') c=gc();
		while(c!=' ' && c!='\n' && c!='\r' && c!=EOF) s+=c,c=gc();
		return *this;
	}
	il IO& operator <<(const char c){pc(c);return *this;}
	template<typename T> il IO& operator <<(T x)
	{
		if(x<0) pc('-'),x=-x;
		do st[++st[0]]=x%10,x/=10;while(x);
		while(st[0]) pc(st[st[0]--]+'0');
		return *this;
	}
	il IO& operator <<(const string s){for(auto c:s) pc(c);return *this;}
	il IO& operator <<(const char* c){for(int i=0;c[i];i++) pc(c[i]);return *this;}
} fin,fout;
const int N=100010;
const ull V=-1;
int siz[N],son[N],d[N],f[N],top[N],dfn[N],tot,val[N],n,m,k;
vector<int> g[N];
struct node
{
	int op;
	ull w;
} a[N];
struct TreeNode
{
	ull w0,w1,fw0,fw1;
} t[N<<2];
il void get_son(int u,int fa)
{
	d[u]=d[fa]+1;f[u]=fa;siz[u]=1;
	for(int v:g[u])
	{
		if(v==fa) continue;
		get_son(v,u);
		siz[u]+=siz[v];
		if(siz[v]>siz[son[u]]) son[u]=v;
	}
}
il void get_top(int u,int t)
{
	top[u]=t;val[dfn[u]=++tot]=u;
	if(son[u]) get_top(son[u],t);
	for(int v:g[u])
	{
		if(v==f[u] || v==son[u]) continue;
		get_top(v,v);
	}
}
il void push_up(int id)
{
	t[id].w0=(t[lid].w0&t[rid].w1)|((t[lid].w0^V)&t[rid].w0);
	t[id].w1=(t[lid].w1&t[rid].w1)|((t[lid].w1^V)&t[rid].w0);
	t[id].fw0=(t[rid].fw0&t[lid].fw1)|((t[rid].fw0^V)&t[lid].fw0);
	t[id].fw1=(t[rid].fw1&t[lid].fw1)|((t[rid].fw1^V)&t[lid].fw0);
}
il void build(int id,int l,int r)
{
	if(l==r)
	{
		if(a[val[l]].op==1) t[id].w0=t[id].fw0=0,t[id].w1=t[id].fw1=a[val[l]].w;
		else if(a[val[l]].op==2) t[id].w0=t[id].fw0=a[val[l]].w,t[id].w1=t[id].fw1=V;
		else t[id].w0=t[id].fw0=a[val[l]].w,t[id].w1=t[id].fw1=V^a[val[l]].w;
		return;
	}
	int mid=(l+r)>>1;
	build(lid,l,mid);
	build(rid,mid+1,r);
	push_up(id);
}
il void change(int id,int l,int r,int x,node w)
{
	if(l==r)
	{
		a[val[l]]=w;
		if(a[val[l]].op==1) t[id].w0=t[id].fw0=0,t[id].w1=t[id].fw1=a[val[l]].w;
		else if(a[val[l]].op==2) t[id].w0=t[id].fw0=a[val[l]].w,t[id].w1=t[id].fw1=V;
		else t[id].w0=t[id].fw0=a[val[l]].w,t[id].w1=t[id].fw1=V^a[val[l]].w;
		return;
	}
	int mid=(l+r)>>1;
	if(x<=mid) change(lid,l,mid,x,w);
	else change(rid,mid+1,r,x,w);
	push_up(id);
}
il pair<ull,ull> query_zheng(int id,int l,int r,int L,int R)
{
	if(L<=l && r<=R) return {t[id].w0,t[id].w1};
	int mid=(l+r)>>1;
	if(R<=mid) return query_zheng(lid,l,mid,L,R);
	else if(L>mid) return query_zheng(rid,mid+1,r,L,R);
	else
	{
		auto tmp1=query_zheng(lid,l,mid,L,R),tmp2=query_zheng(rid,mid+1,r,L,R);
		ull ret1=0,ret2=0;
		ret1=(tmp1.first&tmp2.second)|((tmp1.first^V)&tmp2.first);
		ret2=(tmp1.second&tmp2.second)|((tmp1.second^V)&tmp2.first);
		return {ret1,ret2};
	}
}
il pair<ull,ull> query_fan(int id,int l,int r,int L,int R)
{
	if(L<=l && r<=R) return {t[id].fw0,t[id].fw1};
	int mid=(l+r)>>1;
	if(R<=mid) return query_fan(lid,l,mid,L,R);
	else if(L>mid) return query_fan(rid,mid+1,r,L,R);
	else
	{
		auto tmp1=query_fan(lid,l,mid,L,R),tmp2=query_fan(rid,mid+1,r,L,R);
		ull ret1=0,ret2=0;
		ret1=(tmp2.first&tmp1.second)|((tmp2.first^V)&tmp1.first);
		ret2=(tmp2.second&tmp1.second)|((tmp2.second^V)&tmp1.first);
		return {ret1,ret2};
	}
}
il pair<ull,ull> link_query(int u,int v)
{
	pair<ull,ull> ret1={0,V},ret2={0,V};
	int op=0;
	while(top[u]!=top[v])
	{
		if(d[top[u]]<d[top[v]]) swap(u,v),op^=1;
//		fout<<u<<' '<<v<<" "<<op<<"\n";
		if(op==0)
		{
			auto tmp=query_fan(1,1,n,dfn[top[u]],dfn[u]);
			ull tmp1,tmp2;
			tmp1=(ret1.first&tmp.second)|((ret1.first^V)&tmp.first);
			tmp2=(ret1.second&tmp.second)|((ret1.second^V)&tmp.first);
			ret1={tmp1,tmp2};
		}
		else
		{
			auto tmp=query_zheng(1,1,n,dfn[top[u]],dfn[u]);
			ull tmp1,tmp2;
			tmp1=(tmp.first&ret2.second)|((tmp.first^V)&ret2.first);
			tmp2=(tmp.second&ret2.second)|((tmp.second^V)&ret2.first);
			ret2={tmp1,tmp2};
		}
		u=f[top[u]];
	}
	if(d[u]<d[v]) swap(u,v),op^=1;
	if(op==0)
	{
		auto tmp=query_fan(1,1,n,dfn[v],dfn[u]);
		ull tmp1,tmp2;
		tmp1=(ret1.first&tmp.second)|((ret1.first^V)&tmp.first);
		tmp2=(ret1.second&tmp.second)|((ret1.second^V)&tmp.first);
		ret1={tmp1,tmp2};
	}
	else
	{
		auto tmp=query_zheng(1,1,n,dfn[v],dfn[u]);
		ull tmp1,tmp2;
		tmp1=(tmp.first&ret2.second)|((tmp.first^V)&ret2.first);
		tmp2=(tmp.second&ret2.second)|((tmp.second^V)&ret2.first);
		ret2={tmp1,tmp2};
	}
	ull tmp1,tmp2;
	tmp1=(ret1.first&ret2.second)|((ret1.first^V)&ret2.first);
	tmp2=(ret1.second&ret2.second)|((ret1.second^V)&ret2.first);
	return {tmp1,tmp2};
}
signed main()
{
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	fin>>n>>m>>k;
	for(int i=1;i<=n;i++) fin>>a[i].op>>a[i].w;
	for(int i=1;i<n;i++)
	{
		int u,v;fin>>u>>v;
		g[u].push_back(v);
		g[v].push_back(u);
	}
	get_son(1,0);get_top(1,1);
	build(1,1,n);
	while(m--)
	{
		int op,x,y;ull z;fin>>op>>x>>y>>z;
		if(op==1)
		{
			ull w0,w1;
			tie(w0,w1)=link_query(x,y);
//			fout<<w0<<" "<<w1<<'\n';
			int fl=1;ull ans=0;
			for(int i=k-1;i>=0;i--)
			{
				if(fl)
				{
					if(z>>i&1)
					{
						if(w0>>i&1) ans|=(1ull<<i),fl=0;
						else if(w1>>i&1) ans|=(1ull<<i);
						else fl=0;
					}
					else if(w0>>i&1) ans|=(1ull<<i);
				}
				else if((w0>>i&1) || (w1>>i&1)) ans|=(1ull<<i);
			}
			fout<<ans<<"\n";
		}
		else change(1,1,n,dfn[x],{y,z});
	}
	return 0;
}

P5607 无力回天

\(m=1e6\)容易误导人想\(O(mlogm)\)做法,然而这并不好做。正解为根号分治,设定阈值\(B\),离线下来后出现次数大于\(B\)则用bitset优化由于数字个数不会超过\(\frac{m}{B}\)个,所以时间复杂度不会超过\(O(\frac{m^2}{wB})\),另一部分可以使用并等于两个部分的和减去两个部分的交,交则可以用哈希表统计,由于这些数字的出现次数不会超过\(B\),所以每一次加入新增的数对不会超过\(B\),这部分时间复杂度不会超过\(O(mB)\)\(B\)\(\sqrt{\frac{m}{w}}\),时有最小值\(O(m\sqrt{\frac{m}{w}})\),大约是\(1.25*1e8\)的复杂度,空间有点大,可能得用分块bitset。

代码:

#include<bits/stdc++.h>
using namespace std;
#define il inline
bool St;
struct IO
{
	static const int Size=(1<<21);
	char buf[Size],*p1,*p2;
	int st[105],Top;
	~IO(){clear();}
	il void clear(){fwrite(buf,1,Top,stdout);Top=0;}
	il char gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,Size,stdin),p1==p2)?EOF:*p1++;}
	il void pc(const char c){Top==Size&&(clear(),0);buf[Top++]=c;}
	il IO& operator >>(char& c){while(c=gc(),c==' ' || c=='\n' || c=='\r');return *this;}
	template<typename T>il IO& operator >>(T& x)
	{
		x=0;bool f=0;char c=gc();
		while(!isdigit(c)){if(c=='-') f=1;c=gc();}
		while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();}
		f?x=-x:0;
		return *this;
	}
	il IO& operator >>(string& s)
	{
		s="";char c=gc();
		while(c==' ' || c=='\n' || c=='\r') c=gc();
		while(c!=' ' && c!='\n' && c!='\r' && c!=EOF) s+=c,c=gc();
		return *this;
	}
	il IO& operator <<(const char c){pc(c);return *this;}
	template<typename T> il IO& operator <<(T x)
	{
		if(x<0) pc('-'),x=-x;
		do st[++st[0]]=x%10,x/=10;while(x);
		while(st[0]) pc(st[st[0]--]+'0');
		return *this;
	}
	il IO& operator <<(const string s){for(auto c:s) pc(c);return *this;}
	il IO& operator <<(const char* c){for(int i=0;c[i];i++) pc(c[i]);return *this;}
} fin,fout;
const int N=1000010,B=150;
int cnt[N],vis[N],lsh[N],ans[N];
struct Hash_map
{
	static const int Mod=1e6+3;
	int h[Mod],tot;
	struct node
	{
		int nxt;
		pair<int,int> key;
	} e[N];
	int MyHash(pair<int,int> x)
	{
		return (1ll*x.first*998244353+x.second)%Mod;
	}
	void ins(pair<int,int> x)
	{
		int id=MyHash(x);
		for(int i=h[id];i;i=e[i].nxt)
		{
			if(e[i].key==x) return;
		}
		tot++;
		e[tot].nxt=h[id],e[tot].key=x;
		h[id]=tot;
	}
	int find(pair<int,int> x)
	{
		int id=MyHash(x);
		for(int i=h[id];i;i=e[i].nxt)
		{
			if(e[i].key==x) return 1;
		}
		return 0;
	}
} mp;
struct Hash_map2
{
	static const int Mod=1e6+3;
	int h[Mod],tot;
	struct node
	{
		int nxt,val;
		pair<int,int> key;
	} e[N];
	int MyHash(pair<int,int> x)
	{
		return (1ll*x.first*998244353+x.second)%Mod;
	}
	void add(pair<int,int> x,int y)
	{
		int id=MyHash(x);
		for(int i=h[id];i;i=e[i].nxt)
		{
			if(e[i].key==x)
			{
				e[i].val+=y;
				return;
			}
		}
		tot++;
		e[tot].nxt=h[id],e[tot].key=x;e[tot].val=y;
		h[id]=tot;
	}
	int query(pair<int,int> x)
	{
		int id=MyHash(x);
		for(int i=h[id];i;i=e[i].nxt)
		{
			if(e[i].key==x) return e[i].val;
		}
		return 0;
	}
} val;
bitset<N/B/2+10> b[N];
vector<int> idx[N];
struct node
{
	int op,x,y;
} q[N];
bool Ed;
signed main()
{
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
//	cerr<<(&St-&Ed)/1024.0/1024.0<<"\n";
	int m;fin>>m;
	for(int i=1;i<=m;i++)
	{
		fin>>q[i].op>>q[i].x>>q[i].y;
		if(q[i].op==1) vis[q[i].y]++;
		else
		{
			if(q[i].x>q[i].y) swap(q[i].x,q[i].y);
			mp.ins({q[i].x,q[i].y});
		}
	}
	int tmp=0;
	for(int i=1;i<=m;i++) if(vis[i]>B) lsh[i]=tmp++;
	for(int i=1;i<=m;i++)
	{
		if(q[i].op==1)
		{
			if(vis[q[i].y]<=B)
			{
				idx[q[i].y].emplace_back(q[i].x);
				for(int j:idx[q[i].y])
				{
					if(j<q[i].x)
					{
						if(mp.find({j,q[i].x})) val.add({j,q[i].x},1);
					}
					else if(mp.find({q[i].x,j})) val.add({q[i].x,j},1);
				}
				cnt[q[i].x]++;
			}
			else if(lsh[q[i].y]<=N/B/2) b[q[i].x][lsh[q[i].y]]=1;
		}
		else
		{
			ans[i]=(cnt[q[i].x]+cnt[q[i].y]);
			ans[i]-=val.query({q[i].x,q[i].y});
			ans[i]+=(b[q[i].x]|b[q[i].y]).count();
		}
	}
	for(int i=1;i<=m;i++) b[i]=0;
	for(int i=1;i<=m;i++)
	{
		if(q[i].op==1)
		{
			if(vis[q[i].y]>B && lsh[q[i].y]>N/B/2) b[q[i].x][lsh[q[i].y]-N/B/2]=1;
		}
		else fout<<ans[i]+(b[q[i].x]|b[q[i].y]).count()<<"\n";
	}
	return 0;
}

P6108 rprsvq

组合数学好题。
首先拆式子。

\[\begin{aligned} \frac{1}{n}\sum_{i=1}^{n}(a_i-\bar{a})^2&=\frac{1}{n}\sum_{i=1}^{n}(a_i^2+\bar{a}^2-2\times a_i\times\bar{a})\\ &=(\frac{1}{n}\sum_{i=1}^na_i^2)+\bar{a}^2-(\frac{2}{n}\bar{a}\sum_{i=1}^na_i)\\ &=(\frac{1}{n}\sum_{i=1}^na_i^2)+(\frac{1}{n^2}(\sum_{i=1}^na_i)^2)-(\frac{2}{n^2}(\sum_{i=1}^na_i)^2)\\ &=(\frac{1}{n}\sum_{i=1}^na_i^2)-(\frac{1}{n^2}(\sum_{i=1}^na_i)^2) \end{aligned} \]

然后拆系数

\(s_1=\sum_{i=1}^na_i,s_2=\sum_{i=1}^na_i^2\)

首先是\(s_2\)\(ans\)中的系数,

\(s_2\)在前项中系数为\(\sum_{i=0}^{r-l} C_{r-l}^{i}\times \frac{1}{i+1}\)其中第\(i\)项表示\(a_i^2\)在长为\(i+1\)的序列里的贡献。在后项中的系数为\(-\sum_{i=0}^{r-l} C_{r-l}^{i}\times \frac{1}{(i+1)^2}\),计算方式同上。

\(s_1^2-s_2\)的系数只在后项中有,为\(-\sum_{i=0}^{r-l-1} C_{r-l-1}^{i}\times \frac{1}{(i+2)^2}\)其中第\(i\)项表示现在已经钦定两个数必选,剩余数一共选\(i\)个,总共选的区间长度为\(i+2\)的贡献。

其中\(s_1\)可以用线段树轻松维护,\((s_2+k)^2=s_2^2+2s_2k+k^2\),把这个式子写到push_down里就可以求出动态维护\(s_2\)

然后我们需要快速处理出三个系数的式子。

第一个:\(h(n)=\sum_{i=0}^{n} C_{n}^{i}\times \frac{1}{i+1}\)

\[\begin{aligned} h(n)&=\sum_{i=0}^{n} C_{n}^{i}\times \frac{1}{i+1}\\ &=\sum_{i=0}^{n} \frac{n!}{(i+1)i!(n-i)!}\\ &=\frac{1}{n+1}\sum_{i=0}^{n} \frac{(n+1)!}{(i+1)!(n-i)!}\\ &=\frac{1}{n+1}\sum_{i=0}^{n} C_{n+1}^{i+1}\\ &=\frac{1}{n+1}\sum_{i=1}^{n+1} C_{n+1}^{i}\\ &=\frac{1}{n+1}(2^{n+1}-1) \end{aligned} \]

第二个:\(\sum_{i=0}^{n} C_{n}^{i}\times \frac{1}{(i+1)^2}\)

\[\begin{aligned} \sum_{i=0}^{n} C_{n}^{i}\times \frac{1}{(i+1)^2}&=\frac{1}{n+1}\sum_{i=0}^{n}C_{n+1}^{i+1}\cdot \frac{1}{i+1}\\ &=\frac{1}{n+1}\sum_{i=1}^{n+1}C_{n+1}^{i}\cdot \frac{1}{i} \end{aligned} \]

\(f(n)=\sum_{i=1}^{n+1}C_{n+1}^{i}\cdot \frac{1}{i}\)
则有

\[\begin{aligned} f(n)&=\sum_{i=1}^{n+1}C_{n+1}^{i}\cdot \frac{1}{i}\\ &=\sum_{i=1}^{n+1}C_{n}^{i}\cdot \frac{1}{i}+\sum_{i=1}^{n+1}C_{n}^{i-1}\cdot \frac{1}{i}\\ &=\sum_{i=1}^{n}C_{n}^{i}\cdot \frac{1}{i}+\sum_{i=0}^{n}C_{n}^{i-1}\cdot \frac{1}{i+1}\\ &=f(n-1)+h(n) \end{aligned} \]

\(O(n)\)递推求出。

所以可以\(O(n)\)求出\(1-n\)的第二种系数。

第三个:\(g(n)=\sum_{i=0}^{n} C_{n}^{i}\times \frac{1}{(i+2)^2}\)

\[\begin{aligned} g(n)&=\sum_{i=0}^{n} C_{n}^{i}\times \frac{1}{(i+2)^2}\\ &=\sum_{i=0}^{n} \frac{n!}{i!(n-i)!(i+2)^2}\\ &=\frac{1}{(n+1)(n+2)}\sum_{i=0}^{n} \frac{(n+2)!}{(i+2)!(n-i)!}\times \frac{i+1}{i+2}\\ &=\frac{1}{(n+1)(n+2)}\sum_{i=2}^{n+2} C_{n+2}^{i}\times \frac{i}{i-1}\\ &=\frac{1}{(n+1)(n+2)}(\sum_{i=2}^{n+2} C_{n+2}^{i}-\sum_{i=2}^{n+2} C_{n+2}^{i}\times \frac{1}{i})\\ &=\frac{1}{(n+1)(n+2)}(\sum_{i=0}^{n+2} C_{n+2}^{i}-1-(n+2)-\sum_{i=1}^{n+2} C_{n+2}^{i}\times \frac{1}{i}+(n+2))\\ &=\frac{1}{(n+1)(n+2)}(2^{n+2}-1-f(n+1)) \end{aligned} \]

所以可以\(O(n)\)求出\(g(1)-g(n)\)。这个题就做完了。

代码:

#include<bits/stdc++.h>
using namespace std;
#define il inline
#define lid id<<1
#define rid id<<1|1
struct IO
{
	static const int Size=(1<<21);
	char buf[Size],*p1,*p2;
	int st[105],Top;
	~IO(){clear();}
	il void clear(){fwrite(buf,1,Top,stdout);Top=0;}
	il char gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,Size,stdin),p1==p2)?EOF:*p1++;}
	il void pc(const char c){Top==Size&&(clear(),0);buf[Top++]=c;}
	il IO& operator >>(char& c){while(c=gc(),c==' ' || c=='\n' || c=='\r');return *this;}
	template<typename T>il IO& operator >>(T& x)
	{
		x=0;bool f=0;char c=gc();
		while(!isdigit(c)){if(c=='-') f=1;c=gc();}
		while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();}
		f?x=-x:0;
		return *this;
	}
	il IO& operator >>(string& s)
	{
		s="";char c=gc();
		while(c==' ' || c=='\n' || c=='\r') c=gc();
		while(c!=' ' && c!='\n' && c!='\r' && c!=EOF) s+=c,c=gc();
		return *this;
	}
	il IO& operator <<(const char c){pc(c);return *this;}
	template<typename T> il IO& operator <<(T x)
	{
		if(x<0) pc('-'),x=-x;
		do st[++st[0]]=x%10,x/=10;while(x);
		while(st[0]) pc(st[st[0]--]+'0');
		return *this;
	}
	il IO& operator <<(const string s){for(auto c:s) pc(c);return *this;}
	il IO& operator <<(const char* c){for(int i=0;c[i];i++) pc(c[i]);return *this;}
} fin,fout;
const int N=5000010,mod=998244353;
int jc[N],fjc[N],inv[N],pow2[N],f[N],g[N],limit=1,l;
struct node
{
	int s,s2,lazy;
} t[N<<2];
il void upd(int& x,int y){x=(x+y>=mod)?x+y-mod:x+y;}
il void push_up(int id)
{
	t[id].s=(t[lid].s+t[rid].s)%mod;
	t[id].s2=(t[lid].s2+t[rid].s2)%mod;
}
il void lazy_add(int id,int k,int l,int r)
{
	upd(t[id].lazy,k);
	t[id].s2=(t[id].s2+(2ll*t[id].s*k%mod+1ll*k*k%mod*(r-l+1)%mod))%mod;
	t[id].s=(t[id].s+1ll*k*(r-l+1))%mod;
}
il void push_down(int id,int l,int r)
{
	if(t[id].lazy)
	{
		int mid=(l+r)>>1;
		lazy_add(lid,t[id].lazy,l,mid);
		lazy_add(rid,t[id].lazy,mid+1,r);
		t[id].lazy=0;
	}
}
il void add(int id,int l,int r,int L,int R,int k)
{
	if(L<=l && r<=R)
	{
		lazy_add(id,k,l,r);
		return;
	}
	push_down(id,l,r);
	int mid=(l+r)>>1;
	if(L<=mid) add(lid,l,mid,L,R,k);
	if(R>mid) add(rid,mid+1,r,L,R,k);
	push_up(id);
}
il int query_s(int id,int l,int r,int L,int R)
{
	if(L<=l && r<=R) return t[id].s;
	push_down(id,l,r);
	int mid=(l+r)>>1,ret=0;
	if(L<=mid) upd(ret,query_s(lid,l,mid,L,R));
	if(R>mid) upd(ret,query_s(rid,mid+1,r,L,R));
	return ret;
}
il int query_s2(int id,int l,int r,int L,int R)
{
	if(L<=l && r<=R) return t[id].s2;
	push_down(id,l,r);
	int mid=(l+r)>>1,ret=0;
	if(L<=mid) upd(ret,query_s2(lid,l,mid,L,R));
	if(R>mid) upd(ret,query_s2(rid,mid+1,r,L,R));
	return ret;
}
il int C(int x,int y)
{
	if(x<y) return 0;
	return 1ll*jc[x]*fjc[y]%mod*fjc[x-y]%mod;
}
int qpow(int x,int y)
{
	int ret=1;
	while(y)
	{
		if(y&1) ret=1ll*ret*x%mod;
		x=1ll*x*x%mod;y>>=1;
	}
	return ret;
}
signed main()
{
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	int n,m;fin>>n>>m;
	jc[0]=fjc[0]=jc[1]=fjc[1]=inv[1]=pow2[0]=1;pow2[1]=2;
	for(int i=2;i<=n;i++)
	{
		jc[i]=1ll*jc[i-1]*i%mod;
		inv[i]=1ll*inv[mod%i]*(mod-mod/i)%mod;
		fjc[i]=1ll*fjc[i-1]*inv[i]%mod;
		pow2[i]=pow2[i-1]*2ll%mod;
	}
	f[0]=1;
	for(int i=1;i<n;i++) f[i]=(f[i-1]+1ll*(pow2[i+1]-1)*inv[i+1])%mod;
	for(int i=0;i<n-1;i++) g[i]=((long long)pow2[i+2]-1+mod-f[i+1])%mod*inv[i+1]%mod*inv[i+2]%mod;
	for(int i=1;i<n;i++) f[i]=1ll*f[i]*inv[i+1]%mod;
	while(m--)
	{
		int op,l,r;fin>>op>>l>>r;
		if(op==1)
		{
			int k;fin>>k;
			add(1,1,n,l,r,k);
		}
		else
		{
			
			int s1=query_s(1,1,n,l,r),s2=query_s2(1,1,n,l,r);
			int val1=1ll*inv[r-l+1]*(pow2[r-l+1]+mod-1)%mod,val2=0,val3=f[r-l];
			if(r-l>0) val2=g[r-l-1];
			int ans=0;
			ans=(ans+1ll*s2*val1)%mod;
			ans=(ans-(1ll*s1*s1-s2+mod)%mod*val2%mod+mod)%mod;
			ans=(ans-1ll*s2*val3%mod+mod)%mod;
			fout<<ans<<"\n";
		}
	}
	return 0;
}

P3934 炸脖龙 I

看到这一大坨子幂次第一反应就是欧拉降幂,使用树状数组维护每一位上的值,然后使用欧拉降幂就能过了。需要注意的是递归时遇到1需要中断这个hack。

代码:

#include<bits/stdc++.h>
using namespace std;
#define il inline
#define ll long long
struct IO
{
	static const int Size=(1<<21);
	char buf[Size],*p1,*p2;
	int st[105],Top;
	~IO(){clear();}
	il void clear(){fwrite(buf,1,Top,stdout);Top=0;}
	il char gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,Size,stdin),p1==p2)?EOF:*p1++;}
	il void pc(const char c){Top==Size&&(clear(),0);buf[Top++]=c;}
	il IO& operator >>(char& c){while(c=gc(),c==' ' || c=='\n' || c=='\r');return *this;}
	template<typename T>il IO& operator >>(T& x)
	{
		x=0;bool f=0;char c=gc();
		while(!isdigit(c)){if(c=='-') f=1;c=gc();}
		while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();}
		f?x=-x:0;
		return *this;
	}
	il IO& operator >>(string& s)
	{
		s="";char c=gc();
		while(c==' ' || c=='\n' || c=='\r') c=gc();
		while(c!=' ' && c!='\n' && c!='\r' && c!=EOF) s+=c,c=gc();
		return *this;
	}
	il IO& operator <<(const char c){pc(c);return *this;}
	template<typename T> il IO& operator <<(T x)
	{
		if(x<0) pc('-'),x=-x;
		do st[++st[0]]=x%10,x/=10;while(x);
		while(st[0]) pc(st[st[0]--]+'0');
		return *this;
	}
	il IO& operator <<(const string s){for(auto c:s) pc(c);return *this;}
	il IO& operator <<(const char* c){for(int i=0;c[i];i++) pc(c[i]);return *this;}
} fin,fout;
const int N=500010,V=20000010;
bool St;
int phi[V],n,m,prime[V],cnt;
bool vis[V];
struct BIT
{
    ll s[N];
    il void add(int l,int r,ll x)
    {
        for(int i=l;i<=n;i+=i&-i) s[i]+=x;
        for(int i=r+1;i<=n;i+=i&-i) s[i]-=x;
    }
    il ll query(int x)
    {
        ll ret=0;
        for(int i=x;i;i-=i&-i) ret+=s[i];
        return ret;
    }
} B;
bool Ed;
il int qpow(int x,int y,int mod)
{
    int ret=1;
    while(y)
    {
        if(y&1) ret=1ll*ret*x%mod;
        x=1ll*x*x%mod;y>>=1;
    }
    return ret;
}
il pair<int,bool> query_ans(int l,int r,int p)
{
	ll val=B.query(l);
    if(l==r || B.query(l+1)==1)
	{
		bool fl=0;
		if(val<p) fl=1;
		return {val%p,fl};
	}
    if(p==1) return {0,0};
    auto tmp=query_ans(l+1,r,phi[p]);
    if(!tmp.second) return {qpow(val%p,phi[p]+tmp.first,p),0};
    else
    {
    	bool fl=1;
    	__int128 now=1;
    	for(int i=1;i<=tmp.first;i++)
    	{
    		now*=val;
    		if(now>=phi[p])
    		{
    			fl=0;
    			break;
			}
		}
		if(fl) return {now,1};
		else return {qpow(val%p,tmp.first,p),0}; 
	}
}
il void init()
{
    for(int i=2;i<=V-10;i++)
    {
        if(!vis[i]) prime[++cnt]=i,phi[i]=i-1;
        for(int j=1;j<=cnt;j++)
        {
            ll tmp=1ll*prime[j]*i;
            if(tmp>V-10) break;
            vis[tmp]=1;
            if(i%prime[j]==0)
            {
                phi[tmp]=phi[i]*prime[j];
                continue;
            }
            phi[tmp]=phi[i]*(prime[j]-1);
        }
        // fout<<phi[i]<<" ";
    }
    // fout<<"\n";
}
signed main()
{
//	freopen("P3934_10.in","r",stdin);
//	freopen("chj.out","w",stdout);
    init();
	fin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        int a;fin>>a;
        B.add(i,i,a);
    }
    while(m--)
    {
        int op,l,r;
		ll x;fin>>op>>l>>r>>x;
        if(op==1) B.add(l,r,x);
        else fout<<query_ans(l,r,x).first<<"\n";
    }
	return 0;
}

P11620 TEST_34

问选一些数中选若干个数的异或最大值,第一反应是线性基,但线性基无法快速区间修改,所以可以考虑其差分数组的线性基,发现原数组\([l,r]\)的线性基和差分数组\([l+1,r]\)的线性基中插入原数组的第\(l\)个位置的数效果相同,所以只需要用线段树维护差分数组的线性基和差分数组的前缀和即可。

代码:

#include<bits/stdc++.h>
using namespace std;
#define il inline
#define lid id<<1
#define rid id<<1|1
struct IO
{
	static const int Size=(1<<21);
	char buf[Size],*p1,*p2;
	int st[105],Top;
	~IO(){clear();}
	il void clear(){fwrite(buf,1,Top,stdout);Top=0;}
	il char gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,Size,stdin),p1==p2)?EOF:*p1++;}
	il void pc(const char c){Top==Size&&(clear(),0);buf[Top++]=c;}
	il IO& operator >>(char& c){while(c=gc(),c==' ' || c=='\n' || c=='\r');return *this;}
	template<typename T>il IO& operator >>(T& x)
	{
		x=0;bool f=0;char c=gc();
		while(!isdigit(c)){if(c=='-') f=1;c=gc();}
		while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();}
		f?x=-x:0;
		return *this;
	}
	il IO& operator >>(string& s)
	{
		s="";char c=gc();
		while(c==' ' || c=='\n' || c=='\r') c=gc();
		while(c!=' ' && c!='\n' && c!='\r' && c!=EOF) s+=c,c=gc();
		return *this;
	}
	il IO& operator <<(const char c){pc(c);return *this;}
	template<typename T> il IO& operator <<(T x)
	{
		if(x<0) pc('-'),x=-x;
		do st[++st[0]]=x%10,x/=10;while(x);
		while(st[0]) pc(st[st[0]--]+'0');
		return *this;
	}
	il IO& operator <<(const string s){for(auto c:s) pc(c);return *this;}
	il IO& operator <<(const char* c){for(int i=0;c[i];i++) pc(c[i]);return *this;}
} fin,fout;
const int N=50010;
int n,a[N],c[N];
struct BIT
{
	int s[N];
	il void add(int x,int k)
	{
		for(int i=x;i<=n;i+=(i&-i)) s[i]^=k;
	}
	il int query(int x)
	{
		int ret=0;
		for(int i=x;i;i-=i&-i) ret^=s[i];
		return ret;
	}
} B;
struct XXJ
{
	int p[30];
	il void ins(int x)
	{
		for(int i=29;i>=0;i--)
		{
			if(x>>i&1)
			{
				if(p[i]) x^=p[i];
				else
				{
					p[i]=x;
					break;
				}
			}
		}
	}
	il int query_max(int v)
	{
		int ans=v;
		for(int i=29;i>=0;i--)
		{
			if(p[i] && (ans^p[i])>ans) ans^=p[i];
		}
		return ans;
	}
} www;
il XXJ merge(XXJ x,XXJ y)
{
	for(int i=0;i<=29;i++)
	{
		if(y.p[i]) x.ins(y.p[i]);
	}
	return x;
}
struct sgt
{
	XXJ w[N<<2];
	il void push_up(int id)
	{
		w[id]=merge(w[lid],w[rid]);
	}
	il void build(int id,int l,int r)
	{
		if(l==r)
		{
			w[id].ins(c[l]);
			return;
		}
		int mid=(l+r)>>1;
		build(lid,l,mid);
		build(rid,mid+1,r);
		push_up(id);
	}
	il void add(int id,int l,int r,int x,int k)
	{
		if(l==r)
		{
			c[l]^=k;
			w[id]=www;
			w[id].ins(c[l]);
			return;
		}
		int mid=(l+r)>>1;
		if(x<=mid) add(lid,l,mid,x,k);
		else add(rid,mid+1,r,x,k);
		push_up(id);
	}
	il XXJ query(int id,int l,int r,int L,int R)
	{
		if(L>R) return www;
		if(L<=l && r<=R) return w[id];
		int mid=(l+r)>>1;
		if(R<=mid) return query(lid,l,mid,L,R);
		else if(L>mid) return query(rid,mid+1,r,L,R);
		else return merge(query(lid,l,mid,L,R),query(rid,mid+1,r,L,R));
	}
} T;
signed main()
{
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	int q;fin>>n>>q;
	for(int i=1;i<=n;i++) fin>>a[i];
	for(int i=1;i<=n;i++) c[i]=a[i]^a[i-1];
	T.build(1,1,n);
	for(int i=1;i<=n;i++) B.add(i,c[i]);
	while(q--)
	{
		int op,l,r,v;fin>>op>>l>>r>>v;
		if(op==1)
		{
			T.add(1,1,n,l,v);
			B.add(l,v);
			if(r!=n) B.add(r+1,v),T.add(1,1,n,r+1,v);
		}
		else
		{
			XXJ tmp=T.query(1,1,n,l+1,r);
			tmp.ins(B.query(l));
			fout<<tmp.query_max(v)<<"\n";
		}
	}
	return 0;
}

P6105 [Ynoi2010] y-fast trie

首先把输入进来的每一个数都对 \(C\) 取模,然后分类讨论:

  1. \(C\le x+y<C\) ,那么只需要求出来最大值和次大值一加就好了。

  2. \(x+y<C\) ,此时我们发现现在一共有 \(O(n^2)\) 种数对,我们要想办法把它优化到 \(O(n)\) 对,我们发现如果有两个数对 \((x,y)\)\((y,z)\) 满足 \(x\ge y\) ,则只需要留下 \((x,y)\) 一对即可。然后我们就只留下了 \(O(n)\) 个数对,具体实现时我们需要维护两个 \(set\) ,一个维护原数,叫做 \(num\) ,一个维护数对和,叫做 \(sum\) 。如果是加入一个数 \(x\) ,那么先将其插入 \(num\) ,然后再调用 \(match(x)\) ,如果是删除的话,若 \(x\) 有最优匹配,设 \(x\) 的最优匹配为 \(y\) ,那就先删除 \(x\) 如果 \(x\) 有最优匹配再调用 \(match(y)\)

    \(match(x)\) 的具体实现是先在 \(num\) 中删除 \(x\) 然后再找到 \(x\)\(num\) 中的最优匹配 \(y\) ,如果 \(y\) 未被匹配,那就直接匹配 \(x\)\(y\) ,否则找到 \(y\) 的最优匹配 \(z\) ,如果 \(x\ge z\) 那么就去掉 \(y,z\) 的匹配,然后添加 \(x,y\) 的匹配。

代码:

#include<bits/stdc++.h>
using namespace std;
#define il inline
struct IO
{
	static const int Size=(1<<21);
	char buf[Size],*p1,*p2;
	int st[105],Top;
	~IO(){clear();}
	il void clear(){fwrite(buf,1,Top,stdout);Top=0;}
	il char gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,Size,stdin),p1==p2)?EOF:*p1++;}
	il void pc(const char c){Top==Size&&(clear(),0);buf[Top++]=c;}
	il IO& operator >>(char& c){while(c=gc(),c==' ' || c=='\n' || c=='\r');return *this;}
	template<typename T>il IO& operator >>(T& x)
	{
		x=0;bool f=0;char c=gc();
		while(!isdigit(c)){if(c=='-') f=1;c=gc();}
		while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();}
		f?x=-x:0;
		return *this;
	}
	il IO& operator >>(string& s)
	{
		s="";char c=gc();
		while(c==' ' || c=='\n' || c=='\r') c=gc();
		while(c!=' ' && c!='\n' && c!='\r' && c!=EOF) s+=c,c=gc();
		return *this;
	}
	il IO& operator <<(const char c){pc(c);return *this;}
	template<typename T> il IO& operator <<(T x)
	{
		if(x<0) pc('-'),x=-x;
		do st[++st[0]]=x%10,x/=10;while(x);
		while(st[0]) pc(st[st[0]--]+'0');
		return *this;
	}
	il IO& operator <<(const string s){for(auto c:s) pc(c);return *this;}
	il IO& operator <<(const char* c){for(int i=0;c[i];i++) pc(c[i]);return *this;}
} fin,fout;
set<pair<int,int> > num;
multiset<int> sum;
unordered_map<int,int> p;
int n,C;
void match(int x)
{
	num.erase({x%C,x});
	auto tmp=num.lower_bound({C-x%C,0});
	if(tmp!=num.begin())
	{
		tmp--;
		int y=(*tmp).second;
		if(p.find(y)==p.end())
		{
			p[y]=x;p[x]=y;
			sum.insert((x+y)%C);
		}
		else
		{
			int z=p[y];
			if(z%C<x%C)
			{
				p.erase(z);
				sum.erase(sum.find((y+z)%C));
				p[y]=x;p[x]=y;
				sum.insert((x+y)%C);
			}
		}
	}
	num.insert({x%C,x});
}
signed main()
{
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	int lstans=0;fin>>n>>C;
	while(n--)
	{
		int op,x;fin>>op>>x;x^=lstans;
		if(op==1)
		{
			num.insert({x%C,x});
			match(x);
		}
		else
		{
			num.erase({x%C,x});
			if(p.find(x)!=p.end())
			{
				int y=p[x];
				sum.erase(sum.find((x+y)%C));
				p.erase(x);
				p.erase(y);
				match(y);
			}
		}
		if(num.size()<=1)
		{
			lstans=0;
			fout<<"EE\n";
		}
		else
		{
			lstans=0;
			if(sum.size()) lstans=*(--sum.end());
			lstans=max(lstans,((*(--num.end())).first+(*(--(--num.end()))).first)%C);
			fout<<lstans<<"\n";
		}
	}
	return 0;
}

Level 2:

P5072 [Ynoi Easy Round 2015] 盼君勿忘

首先我们发现对于每一种数 \(x\) ,我们记这个数在这个区间的出现次数为 \(cnt_x\) ,那么设这个区间长度为 \(len\) ,则这种数对于答案的贡献为 \((2^{len}-2^{len-cnt_x})x\) ,由于它每次询问的模数不同,并且是对于每种数,所以线段树是不太可能了,所以可以使用莫队,但我们发现我们只能处理不取模的信息,所以我们不能直接维护,而是维护一些关于出现次数的信息,所以我们可以考虑根号分治,设定一个阈值 \(B=\sqrt{n}\) ,对于出现次数小于等于 \(B\) 的数,我们可以合并出现次数相同的数,对于出现次数为 \(k\) 的数,我们只需要维护所有出现次数为 \(k\) 的数的和,就可以快速求出对答案的贡献,对于出现次数大于 \(B\) 的数,总个数不会超过 \(O(\frac{n}{B})\) ,所以可以直接暴力计算每一种数的贡献。需要注意的是计算 \(2^k\) 时需要使用光速幂,不然时间复杂度会变为 \(O(n\sqrt{n}log_2n)\) ,使用后总时间复杂度为 \(O(n\sqrt{n})\)

代码:

#include<bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/hash_policy.hpp>
using namespace std;
#define il inline
#define ull unsigned long long
bool St;
struct IO
{
	static const int Size=(1<<21);
	char buf[Size],*p1,*p2;
	int st[105],Top;
	~IO(){clear();}
	il void clear(){fwrite(buf,1,Top,stdout);Top=0;}
	il char gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,Size,stdin),p1==p2)?EOF:*p1++;}
	il void pc(const char c){Top==Size&&(clear(),0);buf[Top++]=c;}
	il IO& operator >>(char& c){while(c=gc(),c==' ' || c=='\n' || c=='\r');return *this;}
	template<typename T>il IO& operator >>(T& x)
	{
		x=0;bool f=0;char c=gc();
		while(!isdigit(c)){if(c=='-') f=1;c=gc();}
		while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();}
		f?x=-x:0;
		return *this;
	}
	il IO& operator >>(string& s)
	{
		s="";char c=gc();
		while(c==' ' || c=='\n' || c=='\r') c=gc();
		while(c!=' ' && c!='\n' && c!='\r' && c!=EOF) s+=c,c=gc();
		return *this;
	}
	il IO& operator <<(const char c){pc(c);return *this;}
	template<typename T> il IO& operator <<(T x)
	{
		if(x<0) pc('-'),x=-x;
		do st[++st[0]]=x%10,x/=10;while(x);
		while(st[0]) pc(st[st[0]--]+'0');
		return *this;
	}
	il IO& operator <<(const string s){for(auto c:s) pc(c);return *this;}
	il IO& operator <<(const char* c){for(int i=0;c[i];i++) pc(c[i]);return *this;}
} fin,fout;
const int N=100010,V=100000;
int a[N],cnt[N],b[N],len,L,R,ans[N],n,mod;
ull mp[N],pow1[N],pow2[N];
const int RANDOM = time(NULL);
struct MyHash {int operator() (int x) const {return x ^ RANDOM;}};
__gnu_pbds::gp_hash_table <int,ull,MyHash> mm;
struct node
{
	int l,r,mod,idx;
} q[N];
bool Ed;
il bool cmp(node x,node y)
{
	if(b[x.l]!=b[y.l]) return b[x.l]<b[y.l];
	if(b[x.l]&1) return x.r<y.r;
	return x.r>y.r;
}
il void add(int x)
{
	x=a[x];
	if(cnt[x]>len) mm.erase(x);
	mp[cnt[x]]-=x;
	cnt[x]++;
	if(cnt[x]>len) mm[x]=cnt[x];
	mp[cnt[x]]+=x;
}
il void del(int x)
{
	x=a[x];
	if(cnt[x]>len) mm.erase(x);
	mp[cnt[x]]-=x;
	cnt[x]--;
	if(cnt[x]>len) mm[x]=cnt[x];
	mp[cnt[x]]+=x;
}
il void init_mod()
{
	pow1[0]=pow2[0]=1;
	for(int i=1;i<=len;i++)
	{
		pow1[i]=pow1[i-1]*2;
		if(pow1[i]>=mod) pow1[i]-=mod;
	}
	for(int i=1;i<=n/len;i++) pow2[i]=pow2[i-1]*pow1[len]%mod;
}
il ull pow_2(int x)
{
	return pow1[x%len]*pow2[x/len]%mod;
}
il ull query(int llen)
{
	ull ret=0,tmp=pow_2(llen);
	for(int i=1;i<=len && i<=llen;i++)
	{
		ret=(ret+mp[i]%mod*(tmp+mod-pow_2(llen-i)))%mod;
	}
	return ret;
}
signed main()
{
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
//	cerr<<(&St-&Ed)/1024.0/1024.0<<"\n";
	int m;fin>>n>>m;len=sqrt(n);
	for(int i=1;i<=n;i++) fin>>a[i];
	for(int i=1;i<=n;i++) b[i]=(i-1)/len+1;
	mp[0]=1ll*(V+1)*V/2;
	for(int i=1;i<=m;i++) fin>>q[i].l>>q[i].r>>q[i].mod,q[i].idx=i;
	sort(q+1,q+1+m,cmp);
	L=1,R=0;
	for(int i=1;i<=m;i++)
	{
		while(R<q[i].r) add(++R);
		while(L>q[i].l) add(--L);
		while(R>q[i].r) del(R--);
		while(L<q[i].l) del(L++);
		mod=q[i].mod;init_mod();
		ans[q[i].idx]=query(q[i].r-q[i].l+1);
		ull tmp=pow_2(q[i].r-q[i].l+1);
		for(auto j:mm)
		{
			ans[q[i].idx]=(ans[q[i].idx]+j.first*(tmp+mod-pow_2(q[i].r-q[i].l+1-j.second)))%mod;
		}
	}
	for(int i=1;i<=m;i++) fout<<ans[i]<<"\n";
	return 0;
}

P5071 [Ynoi Easy Round 2015] 此时此刻的光辉

莫队板子题,由于一个小于等于 \(10^9\) 的数质因数分解以后的质因数个数不会超过 \(9\) ,所以可以直接暴力维护此时区间乘积每一种质因数的个数然后套用公式计算区间乘积的因数个数

代码:

#include<bits/stdc++.h>
using namespace std;
#define il inline
struct IO
{
	static const int Size=(1<<21);
	char buf[Size],*p1,*p2;
	int st[105],Top;
	~IO(){clear();}
	il void clear(){fwrite(buf,1,Top,stdout);Top=0;}
	il char gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,Size,stdin),p1==p2)?EOF:*p1++;}
	il void pc(const char c){Top==Size&&(clear(),0);buf[Top++]=c;}
	il IO& operator >>(char& c){while(c=gc(),c==' ' || c=='\n' || c=='\r');return *this;}
	template<typename T>il IO& operator >>(T& x)
	{
		x=0;bool f=0;char c=gc();
		while(!isdigit(c)){if(c=='-') f=1;c=gc();}
		while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();}
		f?x=-x:0;
		return *this;
	}
	il IO& operator >>(string& s)
	{
		s="";char c=gc();
		while(c==' ' || c=='\n' || c=='\r') c=gc();
		while(c!=' ' && c!='\n' && c!='\r' && c!=EOF) s+=c,c=gc();
		return *this;
	}
	il IO& operator <<(const char c){pc(c);return *this;}
	template<typename T> il IO& operator <<(T x)
	{
		if(x<0) pc('-'),x=-x;
		do st[++st[0]]=x%10,x/=10;while(x);
		while(st[0]) pc(st[st[0]--]+'0');
		return *this;
	}
	il IO& operator <<(const string s){for(auto c:s) pc(c);return *this;}
	il IO& operator <<(const char* c){for(int i=0;c[i];i++) pc(c[i]);return *this;}
} fin,fout;
const int N=100010,sqrV=31622,mod=19260817;
vector<pair<int,int> > b[N];
int prime[sqrV+10],cntp,inv[N<<5],now=1,L,R,cnt[sqrV+10+N],col[N],len,ans[N];
bool vis[sqrV+10];
struct qwq
{
	int l,r,idx;
} q[N];
il void init()
{
	for(int i=2;i<=sqrV;i++)
	{
		if(!vis[i]) prime[++cntp]=i;
		for(int j=1;j<=cntp && prime[j]*i<=sqrV;j++)
		{
			vis[prime[j]*i]=1;
			if(i%prime[j]==0) break;
		}
	}
	inv[1]=1;
	for(int i=2;i<N<<5;i++) inv[i]=1ll*inv[mod%i]*(mod-mod/i)%mod;
}
il bool cmp(qwq x,qwq y)
{
	if(col[x.l]!=col[y.l]) return col[x.l]<col[y.l];
	if(col[x.l]&1) return x.r<y.r;
	return x.r>y.r;
}
il void add(int x)
{
	for(auto i:b[x])
	{
		now=1ll*now*inv[cnt[i.first]]%mod*(cnt[i.first]+i.second)%mod;
		cnt[i.first]+=i.second;
	}
}
il void del(int x)
{
	for(auto i:b[x])
	{
		now=1ll*now*inv[cnt[i.first]]%mod*(cnt[i.first]-i.second)%mod;
		cnt[i.first]-=i.second;
	}
}
map<int,int> mp;
signed main()
{
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	init();
	int n,m;fin>>n>>m;
	for(int i=1;i<=cntp;i++) mp[prime[i]]=i;
	int tmp=cntp;
	for(int i=1;i<=n;i++)
	{
		int x;fin>>x;
		for(int j=1;j<=tmp && prime[j]*prime[j]<=x;j++)
		{
			if(x%prime[j]==0)
			{
				int ccc=0;
				while(x%prime[j]==0) x/=prime[j],ccc++;
				b[i].push_back({j,ccc});
			}
		}
		if(x!=1)
		{
			if(mp.find(x)==mp.end()) mp[x]=++cntp;
			b[i].push_back({mp[x],1});
		}
	}
	for(int i=1;i<=cntp;i++) cnt[i]=1;
	len=sqrt(n);
	for(int i=1;i<=n;i++) col[i]=(i-1)/len+1;
	for(int i=1;i<=m;i++) fin>>q[i].l>>q[i].r,q[i].idx=i;
	sort(q+1,q+1+m,cmp);
	L=1,R=0;now=1;
	for(int i=1;i<=m;i++)
	{
		while(L>q[i].l) add(--L);
		while(R<q[i].r) add(++R);
		while(L<q[i].l) del(L++);
		while(R>q[i].r) del(R--);
		ans[q[i].idx]=now;
	}
	for(int i=1;i<=m;i++) fout<<ans[i]<<"\n";
	return 0;
}

P5610 [Ynoi2013] 大学

首先由于经典结论,每一个数除以一个大于等于 \(2\) 的数,最多只会除 \(log_2V\) 次就会变为 \(1\) ,所以我们每一次只遍历 \(x\) 的倍数并用树状数组维护区间和的复杂度均摊下来就是 \(O(nlog_2Vlog_2n)\) 是对的。现在的难点就变成了如何快速维护所有值是 \(x\) 的倍数的位置。

我们可以对每一个 \(x\) 开一个 \(vector\) ,存储所有一开始是 \(x\) 的倍数的位置,但是随着一部分修改,有些位置可能就不再是 \(x\) 的倍数了,我们也不能再遍历它们,不然复杂度就不对了,所以我们需要跳过不再是 \(x\) 的倍数的位置,这个东西显然可以用并查集维护,删除一个点就把这个点和它前面的节点合并起来,每次我们都需要跳到它这个联通块的下一个位置,就可以实现跳过无用节点,时间复杂度 \(O(n\alpha(n)log_2Vlog_2n)\)

然后就结束了吗?

毒瘤的 \(lxl\) 当然没有这么好心,我们需要一些较为强力的卡常。

  1. 将 vector 改为手写内存池
  2. 将多次访问的数组改为临时变量,可以拥有内存连续访问
  3. 将 int 全都改成 unsigned int

代码:

#include<bits/stdc++.h>
using namespace std;
#define il inline
#define ll long long
#define int unsigned int
struct IO
{
	static const int Size=(1<<19);
	char buf[Size],*p1,*p2;
	int st[105],Top;
	~IO(){clear();}
	il void clear(){fwrite(buf,1,Top,stdout);Top=0;}
	il char gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,Size,stdin),p1==p2)?EOF:*p1++;}
	il void pc(const char c){Top==Size&&(clear(),0);buf[Top++]=c;}
	il IO& operator >>(char& c){while(c=gc(),c==' ' || c=='\n' || c=='\r');return *this;}
	template<typename T>il IO& operator >>(T& x)
	{
		x=0;bool f=0;char c=gc();
		while(!isdigit(c)){if(c=='-') f=1;c=gc();}
		while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();}
		f?x=-x:0;
		return *this;
	}
	il IO& operator >>(string& s)
	{
		s="";char c=gc();
		while(c==' ' || c=='\n' || c=='\r') c=gc();
		while(c!=' ' && c!='\n' && c!='\r' && c!=EOF) s+=c,c=gc();
		return *this;
	}
	il IO& operator <<(const char c){pc(c);return *this;}
	template<typename T> il IO& operator <<(T x)
	{
		if(x<0) pc('-'),x=-x;
		do st[++st[0]]=x%10,x/=10;while(x);
		while(st[0]) pc(st[st[0]--]+'0');
		return *this;
	}
	il IO& operator <<(const string s){for(auto c:s) pc(c);return *this;}
	il IO& operator <<(const char* c){for(int i=0;c[i];i++) pc(c[i]);return *this;}
} fin,fout;
const int N=100010,V=500010;
int a[N],n,q,tmp[61000010],*now=tmp,*idx[V],*f[V],*mx[V],len[V],siz[V],iii,h[V],tot,w,l,r,x;
struct node
{
	int nxt,val;
} e[10000010];
ll s[N];
il void add(int& x,int& k)
{
	for(int i=x;i<=n;i+=i&-i) s[i]+=k;
}
il void jian(int& x,int k)
{
	for(int i=x;i<=n;i+=i&-i) s[i]-=k;
}
il ll query()
{
	ll ret=0;
	for(int i=r;i;i-=i&-i) ret+=s[i];
	for(int i=l-1;i;i-=i&-i) ret-=s[i];
	return ret;
}
il int fnd(int x)
{
	int &ff=f[iii][x];
	if(ff==x) return x;
	return ff=fnd(ff);
}
il void init()
{
	for(int i=1;i<=n;i++) siz[a[i]]++;
	for(int i=1;i<=500000;i++)
	{
		for(int j=i;j<=500000;j+=i)
		{
			len[i]+=siz[j];
			if(siz[j])
			{
				tot++;
				e[tot].nxt=h[j];
				e[tot].val=i;
				h[j]=tot;
			}
		}
	}
	for(int i=1;i<=500000;i++)
	{
		int & val=len[i];
		idx[i]=now,now+=val;
		f[i]=now,now+=val;
		mx[i]=now,now+=val;
		val=0;
	}
	for(int i=1;i<=n;i++)
	{
		for(int k=h[a[i]];k;k=e[k].nxt)
		{
			int j=e[k].val,&val=len[j];
			idx[j][val]=i;
			f[j][val]=val;
			mx[j][val]=val;
			val++;
		}
	}
	for(int i=1;i<=n;i++) add(i,a[i]);
}
il void del()
{
	iii=x;int lx=len[x];
	int now=lower_bound(idx[x],idx[x]+lx,l)-idx[x];
	while(now<lx && idx[x][now]<=r)
	{
		int fn=fnd(now),&val=a[idx[x][now]],&maxx=mx[x][fn];
		if(maxx!=now) now=maxx;
		else
		{
			int w=val/x;
			if(!(val%x)) jian(idx[x][now],val-w),val=w;
			if(val%x)
			{
				if(now!=lx-1)
				{
					int fy=fnd(now+1);
					f[x][fn]=fy;
				}
				else maxx=lx;
			}
			else now++;
		}
	}
}
signed main()
{
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	fin>>n>>q;
	for(int i=1;i<=n;i++) fin>>a[i];
	init();
	ll lstans=0,op;
	while(q--)
	{
		fin>>op>>l>>r;
		l^=lstans,r^=lstans;
		if(op==2) fout<<(lstans=query())<<"\n";
		else
		{
			fin>>x;x^=lstans;
			if(x-1) del();
		}
	}
	return 0;
}

P4688 [Ynoi Easy Round 2016] 掉进兔子洞

又是一个莫队板子题 \(\dots\) ,先把整个数组离散化,然后把询问拆成三个,询问时可以用 \(bitset\) 加速求交操作,多个相同的数可以开一段连续的下标,第 \(i\) 个下标表示第 \(i\) 次出现。可能有点卡空间需要分块 \(bitset\)

upd:好像并不需要分块 bitset ,可以直接对于每一个原询问开一个 bitset 。

代码:

#include<bits/stdc++.h>
using namespace std;
#define il inline
struct IO
{
	static const int Size=(1<<21);
	char buf[Size],*p1,*p2;
	int st[105],Top;
	~IO(){clear();}
	il void clear(){fwrite(buf,1,Top,stdout);Top=0;}
	il char gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,Size,stdin),p1==p2)?EOF:*p1++;}
	il void pc(const char c){Top==Size&&(clear(),0);buf[Top++]=c;}
	il IO& operator >>(char& c){while(c=gc(),c==' ' || c=='\n' || c=='\r');return *this;}
	template<typename T>il IO& operator >>(T& x)
	{
		x=0;bool f=0;char c=gc();
		while(!isdigit(c)){if(c=='-') f=1;c=gc();}
		while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();}
		f?x=-x:0;
		return *this;
	}
	il IO& operator >>(string& s)
	{
		s="";char c=gc();
		while(c==' ' || c=='\n' || c=='\r') c=gc();
		while(c!=' ' && c!='\n' && c!='\r' && c!=EOF) s+=c,c=gc();
		return *this;
	}
	il IO& operator <<(const char c){pc(c);return *this;}
	template<typename T> il IO& operator <<(T x)
	{
		if(x<0) pc('-'),x=-x;
		do st[++st[0]]=x%10,x/=10;while(x);
		while(st[0]) pc(st[st[0]--]+'0');
		return *this;
	}
	il IO& operator <<(const string s){for(auto c:s) pc(c);return *this;}
	il IO& operator <<(const char* c){for(int i=0;c[i];i++) pc(c[i]);return *this;}
} fin,fout;
const int N=100010;
struct qwq
{
	int l,r,idx;
} q[N<<2];
bitset<N> b[N/3],now;
int len,ans[N],vis[N],L,R,lsh[N],idx[N],sum[N],cnt,a[N],n;
il bool cmp(qwq x,qwq y)
{
	if(vis[x.l]!=vis[y.l]) return vis[x.l]<vis[y.l];
	if(vis[x.l]&1) return x.r<y.r;
	return x.r>y.r;
}
il void add(int x)
{
	now[idx[a[x]]]=1;
	idx[a[x]]++;
}
il void del(int x)
{
	idx[a[x]]--;
	now[idx[a[x]]]=0;
}
il void solve(int l,int r)
{
	if(l>r) return;
	sort(q+(l-1)*3+1,q+1+r*3,cmp);
	L=1,R=0;
	for(int i=1;i<=n;i++) now[i]=0;
	for(int i=1;i<=n;i++) b[0][i]=1;
	for(int i=l+1;i<=r;i++) b[i-l]=b[0];
	for(int i=1;i<=cnt;i++) idx[i]=sum[i-1]+1;
	for(int i=(l-1)*3+1;i<=r*3;i++)
	{
		while(L>q[i].l) add(--L);
		while(R<q[i].r) add(++R);
		while(L<q[i].l) del(L++);
		while(R>q[i].r) del(R--);
		b[q[i].idx-l]&=now;
	}
	for(int i=l;i<=r;i++) ans[i]-=3*b[i-l].count();
}
signed main()
{
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	int m;fin>>n>>m;len=sqrt(n);
	for(int i=1;i<=n;i++) vis[i]=(i-1)/len+1;
	for(int i=1;i<=n;i++) fin>>a[i],lsh[i]=a[i];
	sort(lsh+1,lsh+1+n);
	cnt=unique(lsh+1,lsh+1+n)-lsh-1;
	for(int i=1;i<=n;i++)
	{
		a[i]=lower_bound(lsh+1,lsh+1+cnt,a[i])-lsh;
		sum[a[i]]++;
	}
	for(int i=1;i<=cnt;i++) sum[i]+=sum[i-1];
	for(int i=1;i<=m;i++)
	{
		fin>>q[3*i-2].l>>q[3*i-2].r;q[3*i-2].idx=i;
		fin>>q[3*i-1].l>>q[3*i-1].r;q[3*i-1].idx=i;
		fin>>q[3*i].l>>q[3*i].r;q[3*i].idx=i;
		ans[i]=q[i*3-2].r-q[i*3-2].l+1+q[i*3-1].r-q[i*3-1].l+1+q[i*3].r-q[i*3].l+1;
	}
	int B=m/3+1;
	for(int i=1;i<=3;i++) solve((i-1)*B+1,min(i*B,m));
	for(int i=1;i<=m;i++) fout<<ans[i]<<"\n";
	return 0;
}

P5309 [Ynoi2011] 初始化

由于我们发现 \(x\ge\sqrt{n}\) 我们可以很快解决原问题,所以我们不难想到这个题应该是根号分治,每次修改时对于 \(x\) 的大小进行根号分治,如果 \(x\ge\sqrt{n}\) ,我们可以直接暴力修改所有位置,用分块 \(O(1)-O(\sqrt{n})\) 我们这部分就可以做到 \(O(n\sqrt{n})\) 了。如果 \(x<\sqrt{n}\) ,由于对于 \(x\) ,每一个长为 \(x\) 的块进行的修改都是一样的,所以我们可以对其中的一个块求一个前缀和以及后缀和。然后查询时直接枚举每一个 \(x\) ,分为散块和整块两种情况转移即可。

代码:

#include<bits/stdc++.h>
using namespace std;
#define il inline
#define ll long long
struct IO
{
	static const int Size=(1<<21);
	char buf[Size],*p1,*p2;
	int st[105],Top;
	~IO(){clear();}
	il void clear(){fwrite(buf,1,Top,stdout);Top=0;}
	il char gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,Size,stdin),p1==p2)?EOF:*p1++;}
	il void pc(const char c){Top==Size&&(clear(),0);buf[Top++]=c;}
	il IO& operator >>(char& c){while(c=gc(),c==' ' || c=='\n' || c=='\r');return *this;}
	template<typename T>il IO& operator >>(T& x)
	{
		x=0;bool f=0;char c=gc();
		while(!isdigit(c)){if(c=='-') f=1;c=gc();}
		while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();}
		f?x=-x:0;
		return *this;
	}
	il IO& operator >>(string& s)
	{
		s="";char c=gc();
		while(c==' ' || c=='\n' || c=='\r') c=gc();
		while(c!=' ' && c!='\n' && c!='\r' && c!=EOF) s+=c,c=gc();
		return *this;
	}
	il IO& operator <<(const char c){pc(c);return *this;}
	template<typename T> il IO& operator <<(T x)
	{
		if(x<0) pc('-'),x=-x;
		do st[++st[0]]=x%10,x/=10;while(x);
		while(st[0]) pc(st[st[0]--]+'0');
		return *this;
	}
	il IO& operator <<(const string s){for(auto c:s) pc(c);return *this;}
	il IO& operator <<(const char* c){for(int i=0;c[i];i++) pc(c[i]);return *this;}
} fin,fout;
const int N=200010,B=400,mod=1e9+7,len=450;
ll a[N],pre[B+10][B+10],suf[B+10][B+10],sum[N/len+10];
int b[N];
il void add(int x,int k)
{
	sum[b[x]]+=k;
	a[x]+=k;
}
il ll query(int l,int r)
{
	ll ret=0;
	for(int i=l;i<=min(r,b[l]*len);i++) ret+=a[i];
	if(b[l]!=b[r])
	{
		for(int i=(b[r]-1)*len+1;i<=r;i++) ret+=a[i];
	}
	for(int i=b[l]+1;i<b[r];i++) ret+=sum[i];
	return ret;
}
signed main()
{
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	int n,m;fin>>n>>m;
	for(int i=1;i<=n;i++) b[i]=(i-1)/len+1;
	for(int i=1;i<=n;i++) fin>>a[i],sum[b[i]]+=a[i];
	while(m--)
	{
		int op;fin>>op;
		if(op==1)
		{
			int x,y,z;fin>>x>>y>>z;
			if(x<=B)
			{
				for(int i=y;i<=x;i++) pre[x][i]+=z;
				for(int i=y;i>=0;i--) suf[x][i]+=z;
			}
			else
			{
				for(int i=y;i<=n;i+=x) add(i,z);
			}
		}
		else
		{
			int l,r;fin>>l>>r;
			ll ans=query(l,r)%mod;
			for(int i=1;i<=B;i++)
			{
				int lcnt=(l-1)/i+1,rcnt=r/i;
				if(lcnt<=rcnt)
				{
					ans=(ans+suf[i][l-(lcnt-1)*i])%mod;
					ans=(ans+pre[i][r-rcnt*i])%mod;
					ans=(ans+pre[i][i]%mod*(rcnt-lcnt))%mod; 
				}
				else
				{
					ans=(ans+pre[i][r-(lcnt-1)*i]+mod-pre[i][l-1-(lcnt-1)*i])%mod;
				}
			}
			fout<<ans<<"\n";
		}
	}
	return 0;
}

P5068 [Ynoi Easy Round 2015] 我回来了

首先考虑离线处理,先处理出每一个血量为 \(x\) 随从最早的出现出现时间 \(tim_x\) ,没出现过就设为 \(m+1\) ,然后对于每一个亵渎的伤害 \(y\) ,我们可以从小到大枚举血量在区间 \([ky+1,(k+1)y]\) 的出现时间最小值,那么在这个时间点之后,亵渎 \(y\) 的期望触发次数就变为了 \(k+1\) ,对于当然我们还要删掉 \([(k-1)y+1,ky]\) 这个区间的贡献。然后再枚举时间扫描线,用树状数组维护一下亵渎伤害在一个区间内的触发次数之和就可以了。

代码:

#include<bits/stdc++.h>
using namespace std;
#define il inline
#define ll long long
bool St;
struct IO
{
	static const int Size=(1<<21);
	char buf[Size],*p1,*p2;
	int st[105],Top;
	~IO(){clear();}
	il void clear(){fwrite(buf,1,Top,stdout);Top=0;}
	il char gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,Size,stdin),p1==p2)?EOF:*p1++;}
	il void pc(const char c){Top==Size&&(clear(),0);buf[Top++]=c;}
	il IO& operator >>(char& c){while(c=gc(),c==' ' || c=='\n' || c=='\r');return *this;}
	template<typename T>il IO& operator >>(T& x)
	{
		x=0;bool f=0;char c=gc();
		while(!isdigit(c)){if(c=='-') f=1;c=gc();}
		while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();}
		f?x=-x:0;
		return *this;
	}
	il IO& operator >>(string& s)
	{
		s="";char c=gc();
		while(c==' ' || c=='\n' || c=='\r') c=gc();
		while(c!=' ' && c!='\n' && c!='\r' && c!=EOF) s+=c,c=gc();
		return *this;
	}
	il IO& operator <<(const char c){pc(c);return *this;}
	template<typename T> il IO& operator <<(T x)
	{
		if(x<0) pc('-'),x=-x;
		do st[++st[0]]=x%10,x/=10;while(x);
		while(st[0]) pc(st[st[0]--]+'0');
		return *this;
	}
	il IO& operator <<(const string s){for(auto c:s) pc(c);return *this;}
	il IO& operator <<(const char* c){for(int i=0;c[i];i++) pc(c[i]);return *this;}
} fin,fout;
const int N=100010,M=1000010,inf=1e9;
int mn[17][N],n,m;
struct node
{
	int op,x,y;
} q[M];
vector<pair<int,int> > b[M];
bool Ed;
int get_min(int l,int r)
{
	r=min(r,n);
	if(l>r) return inf;
	int k=__lg(r-l+1);
	return min(mn[k][l],mn[k][r-(1<<k)+1]);
}
struct BIT
{
	ll s[N];
	void add(int x,int k){for(int i=x;i<=n;i+=(i&-i)) s[i]+=k;}
	ll query(int x)
	{
		ll ret=0;
		for(int i=x;i;i-=(i&-i)) ret+=s[i];
		return ret;
	}
} B;
signed main()
{
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
//	cerr<<(&St-&Ed)/1024.0/1024.0<<"\n";
	fin>>n>>m;
	for(int i=1;i<=n;i++) mn[0][i]=inf;
	for(int i=1;i<=m;i++)
	{
		fin>>q[i].op>>q[i].x;
		if(q[i].op==2) fin>>q[i].y;
		else mn[0][q[i].x]=min(mn[0][q[i].x],i);
	}
	for(int i=1;i<=16;i++)
	{
		for(int j=1;j+(1<<i)-1<=n;j++)
		{
			mn[i][j]=min(mn[i-1][j],mn[i-1][j+(1<<(i-1))]);
		}
	}
	for(int i=1;i<=n;i++)
	{
		int mx=1;
		for(int j=i;j-i<=n;j+=i)
		{
			if(mx==inf) break;
			b[mx].push_back({i,j/i});
			b[mx].push_back({i,-(j/i-1)});
			mx=max(mx,get_min(j-i+1,j));
		}
		if(mx!=inf)
		{
			b[mx].push_back({i,n/i+1});
			b[mx].push_back({i,-n/i});
		}
	}
	for(int i=1;i<=m;i++)
	{
		for(auto j:b[i]) B.add(j.first,j.second);
		if(q[i].op==2) fout<<B.query(q[i].y)-B.query(q[i].x-1)<<"\n";
	}
	return 0;
}

P5355 [Ynoi Easy Round 2017] 由乃的玉米田

首先可以考虑莫队,我们维护两个 bitset ,一个代表所有区间内的数 \(A\),一个代表所有区间内的数的相反数 \(B\) (B里面的数都是加了 \(10^5\) 的)。然后分别讨论四种操作的做法。

  • 操作 \(1\) :可以直接判断 \(A\&(A<<x)\) 里是否有值,有值的话就代表有差值为 \(x\) 的,否则就代表没有

  • 操作 \(2\) :可以直接判断 \(A\&(B>>(10^5-x))\) 里是否有值(可以理解成 \(a+b=a-(-b)\) ),有值的话就代表有和为 \(x\) ,否则就代表没有。

  • 操作 \(3\) :这个更简单,我们发现两个数乘积如果等于 \(x\) ,那么一定有一个数小于等于 \(\sqrt{x}\) ,所以我们只需要枚举这个数 \(y\) ,然后在 \(A\) 中判断有没有 \(y\)\(\frac{x}{y}\) 即可。

  • 操作 \(4\) :这个是最难的,莫队只能处理其中一部分,一部分是 \(x\)\(0\) 的部分,另一部分是 \(x>\sqrt{n}\) 的部分。这一部分可以直接暴力枚举被除数 \(y\) 然后判断 \(y\)\(\frac{y}{x}\) 有没有在 \(A\) 中出现过。

    另一部分就是 \(1\le x\le \sqrt{n}\) ,这一部分我们可以先枚举 \(x\) ,再直接对区间右端点进行扫描线,对于每一个右端点上的值,更新出其左边最靠右满足两数商为 \(x\) 的位置,然后判断所有位置最大值与区间左端点的关系即可。

代码:

#include<bits/stdc++.h>
using namespace std;
#define il inline
#define ll long long
bool St;
struct IO
{
	static const int Size=(1<<21);
	char buf[Size],*p1,*p2;
	int st[105],Top;
	~IO(){clear();}
	il void clear(){fwrite(buf,1,Top,stdout);Top=0;}
	il char gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,Size,stdin),p1==p2)?EOF:*p1++;}
	il void pc(const char c){Top==Size&&(clear(),0);buf[Top++]=c;}
	il IO& operator >>(char& c){while(c=gc(),c==' ' || c=='\n' || c=='\r');return *this;}
	template<typename T>il IO& operator >>(T& x)
	{
		x=0;bool f=0;char c=gc();
		while(!isdigit(c)){if(c=='-') f=1;c=gc();}
		while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();}
		f?x=-x:0;
		return *this;
	}
	il IO& operator >>(string& s)
	{
		s="";char c=gc();
		while(c==' ' || c=='\n' || c=='\r') c=gc();
		while(c!=' ' && c!='\n' && c!='\r' && c!=EOF) s+=c,c=gc();
		return *this;
	}
	il IO& operator <<(const char c){pc(c);return *this;}
	template<typename T> il IO& operator <<(T x)
	{
		if(x<0) pc('-'),x=-x;
		do st[++st[0]]=x%10,x/=10;while(x);
		while(st[0]) pc(st[st[0]--]+'0');
		return *this;
	}
	il IO& operator <<(const string s){for(auto c:s) pc(c);return *this;}
	il IO& operator <<(const char* c){for(int i=0;c[i];i++) pc(c[i]);return *this;}
} fin,fout;
const int N=100010,B=330,V=100000;
int a[N],b[N],cnt[N],tot,lst[N];
bool ans[N];
struct node
{
	int l,r,op,x,idx;
} q[N],q2[N];
bitset<100010> Z,F;
bool Ed;
il bool cmp(node x,node y)
{
	if(b[x.l]!=b[y.l]) return b[x.l]<b[y.l];
	if(b[x.l]&1) return x.r<y.r;
	return x.r>y.r;
}
il bool cmp2(node x,node y){return x.r<y.r;}
il void add(int x)
{
	cnt[a[x]]++;
	if(cnt[a[x]]==1) Z[a[x]]=1,F[V-a[x]]=1;
}
il void del(int x)
{
	cnt[a[x]]--;
	if(!cnt[a[x]]) Z[a[x]]=0,F[V-a[x]]=0;
}
signed main()
{
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	cerr<<(&St-&Ed)/1024.0/1024.0<<"\n";
	int n,m;fin>>n>>m;
	for(int i=1;i<=n;i++) fin>>a[i];
	for(int i=1;i<=m;i++) fin>>q[i].op>>q[i].l>>q[i].r>>q[i].x,q[i].idx=i;
	for(int i=1;i<=n;i++) b[i]=(i-1)/B+1;
	sort(q+1,q+1+m,cmp);
	int L=1,R=0;
	for(int i=1;i<=m;i++)
	{
		while(R<q[i].r) add(++R);
		while(L>q[i].l) add(--L);
		while(R>q[i].r) del(R--);
		while(L<q[i].l) del(L++);
		if(q[i].op==1) ans[q[i].idx]=(Z&(Z<<q[i].x)).count();
		if(q[i].op==2) ans[q[i].idx]=(Z&(F>>(V-q[i].x))).count();
		if(q[i].op==3)
		{
			if(q[i].op==3)
			{
				if(q[i].x==0) ans[q[i].idx]=Z[0];
				else
				{
					for(int j=1;j*j<=q[i].x;j++)
					{
						if(q[i].x%j==0) ans[q[i].idx]|=Z[j]&Z[q[i].x/j];
					}
				}
			}
		}
		if(q[i].op==4)
		{
			if(q[i].x==0)
			{
				if(Z[0])
				{
					if(Z.count()!=1) ans[q[i].idx]=1;
				}
			}
			else
			{
				if(q[i].x>B)
				{
					for(int j=q[i].x;j<=V;j+=q[i].x)
					{
						ans[q[i].idx]|=Z[j/q[i].x]&Z[j];
					}
				}
				else q2[++tot]=q[i];
			}
		}
	}
	sort(q2+1,q2+1+tot,cmp2);
	for(int x=1;x<=B;x++)
	{
		int now=0,mx=0;
		for(int i=1;i<=V;i++) lst[i]=0;
		for(int i=1;i<=tot;i++)
		{
			if(q2[i].x==x)
			{
				while(now<q2[i].r)
				{
					now++;
					lst[a[now]]=now;
					if(a[now]%x==0) mx=max(mx,lst[a[now]/x]);
					if(1ll*a[now]*x<=V) mx=max(mx,lst[a[now]*x]);
				}
				ans[q2[i].idx]=(mx>=q2[i].l);
			}
		}
	}
	for(int i=1;i<=m;i++) fout<<(ans[i]?"yuno":"yumi")<<"\n";
	return 0;
}

P5356 [Ynoi Easy Round 2017] 由乃打扑克

考虑分块大雾,设块长为 \(B\) 对于修改,我们可以整块打 \(tag\) ,散块重构,但是重构时肯定不能直接 sort ,我么发现加一段区间以后区间内的顺序不变,所以我们可以直接将修改的部分与不修改的部分进行归并排序,这样修改的复杂度就是 \(O(B+\frac{n}{B})\) ,对于查询第 \(k\) 大,我们可以先进行二分,然后查询 \([1,mid]\) 之间的数的个数,这个东西我们发现整块可以直接二分求解,散块可以先在二分外将左右散块归并,然后内部直接二分。复杂度是 \(O(\frac{n}{B}log_2nlog_2V+B)\) 发现块长取 \(B=\sqrt{n}log_2n\) 时有最优复杂度 \(O(n\sqrt{n}log_2n)\)

代码:

#include<bits/stdc++.h>
using namespace std;
#define il inline
#define ll long long
bool St;
struct IO
{
	static const int Size=(1<<21);
	char buf[Size],*p1,*p2;
	int st[105],Top;
	~IO(){clear();}
	il void clear(){fwrite(buf,1,Top,stdout);Top=0;}
	il char gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,Size,stdin),p1==p2)?EOF:*p1++;}
	il void pc(const char c){Top==Size&&(clear(),0);buf[Top++]=c;}
	il IO& operator >>(char& c){while(c=gc(),c==' ' || c=='\n' || c=='\r');return *this;}
	template<typename T>il IO& operator >>(T& x)
	{
		x=0;bool f=0;char c=gc();
		while(!isdigit(c)){if(c=='-') f=1;c=gc();}
		while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();}
		f?x=-x:0;
		return *this;
	}
	il IO& operator >>(string& s)
	{
		s="";char c=gc();
		while(c==' ' || c=='\n' || c=='\r') c=gc();
		while(c!=' ' && c!='\n' && c!='\r' && c!=EOF) s+=c,c=gc();
		return *this;
	}
	il IO& operator <<(const char c){pc(c);return *this;}
	template<typename T> il IO& operator <<(T x)
	{
		if(x<0) pc('-'),x=-x;
		do st[++st[0]]=x%10,x/=10;while(x);
		while(st[0]) pc(st[st[0]--]+'0');
		return *this;
	}
	il IO& operator <<(const string s){for(auto c:s) pc(c);return *this;}
	il IO& operator <<(const char* c){for(int i=0;c[i];i++) pc(c[i]);return *this;}
} fin,fout;
const int N=100010,B=5600;
int a[N],fl[N/B+10],id[N],n,m,b[N],tmp[N],tot;
bool Ed;
il bool cmp(int x,int y)
{
	return a[x]<a[y];
}
il void init()
{
	for(int i=1;i<=n;i++) b[i]=(i-1)/B+1;
	for(int i=1;i<=n;i++) id[i]=i;
	for(int i=1;i<=b[n];i++) sort(id+(i-1)*B+1,id+1+min(i*B,n),cmp);
}
il void rebuild(int x,int idx)
{
	int i=(x-1)*B+1,j=(x-1)*B+1,k=(x-1)*B+1;
	while(i<=min(n,x*B) && id[i]>=idx) i++;
	while(j<=min(n,x*B) && id[j]<idx) j++;
	while(i<=min(n,x*B) && j<=min(n,x*B))
	{
		if(a[id[i]]<=a[id[j]])
		{
			tmp[k++]=id[i++];
			while(i<=min(n,x*B) && id[i]>=idx) i++;
		}
		else
		{
			tmp[k++]=id[j++];
			while(i<=min(n,x*B) && id[j]<idx) j++;
		}
	}
	while(i<=min(n,x*B))
	{
		tmp[k++]=id[i++];
		while(i<=min(n,x*B) && id[i]>=idx) i++;
	}
	while(j<=min(n,x*B))
	{
		tmp[k++]=id[j++];
		while(j<=min(n,x*B) && id[j]<idx) j++;
	}
	for(int f=(x-1)*B+1;f<=min(n,x*B);f++) id[f]=tmp[f];
}
il void add(int l,int r,int k)
{
	for(int i=l;i<=min(b[l]*B,n);i++) a[i]+=k;
	rebuild(b[l],l);
	if(b[l]==b[r])
	{
		for(int i=r+1;i<=min(b[l]*B,n);i++) a[i]-=k;
		rebuild(b[l],r+1);
		return;
	}
	for(int i=(b[r]-1)*B+1;i<=r;i++) a[i]+=k;
	rebuild(b[r],r+1);
	for(int i=b[l]+1;i<b[r];i++) fl[i]+=k;
}
il int query_sum(int l,int r,int k)
{
	int sum=upper_bound(tmp+1,tmp+1+tot,k)-tmp-1;
	for(int i=b[l]+1;i<b[r];i++)
	{
		int L=(i-1)*B+1,R=i*B,ret=(i-1)*B;
		while(L<=R)
		{
			int mid=(L+R)>>1;
			if(a[id[mid]]+fl[i]<=k) L=mid+1,ret=mid;
			else R=mid-1;
		}
		sum+=ret-(i-1)*B;
	}
	return sum;
}
il int query_kth(int l,int r,int k)
{
	tot=0;
	if(b[l]==b[r])
	{
		for(int i=(b[l]-1)*B+1;i<=min(n,b[l]*B);i++)
		{
			if(id[i]>=l && id[i]<=r) tmp[++tot]=a[id[i]]+fl[b[l]];
		}
	}
	else
	{
		int i=(b[l]-1)*B+1,j=(b[r]-1)*B+1,fl1=fl[b[l]],fl2=fl[b[r]];
		while(id[i]<l) i++;
		while(id[j]>r) j++;
		while(i<=b[l]*B && j<=min(n,b[r]*B))
		{
			if(a[id[i]]+fl1<=a[id[j]]+fl2)
			{
				tmp[++tot]=a[id[i++]]+fl1;
				while(i<=b[l]*B && id[i]<l) i++;
			}
			else
			{
				tmp[++tot]=a[id[j++]]+fl2;
				while(j<=min(n,b[r]*B) && id[j]>r) j++;
			}
		}
		while(i<=b[l]*B)
		{
			tmp[++tot]=a[id[i++]]+fl1;
			while(i<=b[l]*B && id[i]<l) i++;
		}
		while(j<=min(n,b[r]*B))
		{
			tmp[++tot]=a[id[j++]]+fl2;
			while(j<=min(n,b[r]*B) && id[j]>r) j++;
		}
	}
	ll L=-2e9,R=2e9,ret=-2e9;
	while(L<=R)
	{
		int mid=(L+R)>>1;
		if(query_sum(l,r,mid)>=k) R=mid-1,ret=mid;
		else L=mid+1;
	}
	return ret;
}
signed main()
{
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	cerr<<(&St-&Ed)/1024.0/1024.0<<"\n";
	fin>>n>>m;
	for(int i=1;i<=n;i++) fin>>a[i];
	init();
	while(m--)
	{
		int op,l,r,k;fin>>op>>l>>r>>k;
		if(op==1) fout<<query_kth(l,r,k)<<"\n";
		else add(l,r,k);
	}
	return 0;
}

P5070 [Ynoi Easy Round 2015] 即便看不到未来

我们设后面没有数的连续段为单侧极长值域连续段首先我们发现长度为 \(x\) 的极长值域连续段个数就是长度为 \(x\) 的单侧极长值域连续段个数减去长度为 \(x+1\) 的单侧极长值域连续段个数,我们现在考虑对区间右端点进行扫描线,我们发现每加入一个数 \(v\) 其实只会影响到 \(O(10^2)\) 个单侧极长值域连续段的最晚出现位置,我们只有在 \([l,r]\) 这个连续段出现,但是 \([l,r+1]\) 这个连续段还未出现时,\([l,r]\) 才是一个单侧极长值域连续段,所以我们只对一段区间左端点范围进行长度为 \(r-l+1\) 进行贡献,我们可以考虑开 \(11\) 个树状数组来维护每一个左端点的范围上的贡献,每次查询时用第 \(x\) 个树状数组上的答案减去第 \(x+1\) 个树状数组上的答案就能求出长度为 \(x\) 的极长值域连续段即可。

代码:

#include<bits/stdc++.h>
using namespace std;
#define il inline
#define ll long long
bool St;
struct IO
{
	static const int Size=(1<<21);
	char buf[Size],*p1,*p2;
	int st[105],Top;
	~IO(){clear();}
	il void clear(){fwrite(buf,1,Top,stdout);Top=0;}
	il char gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,Size,stdin),p1==p2)?EOF:*p1++;}
	il void pc(const char c){Top==Size&&(clear(),0);buf[Top++]=c;}
	il IO& operator >>(char& c){while(c=gc(),c==' ' || c=='\n' || c=='\r');return *this;}
	template<typename T>il IO& operator >>(T& x)
	{
		x=0;bool f=0;char c=gc();
		while(!isdigit(c)){if(c=='-') f=1;c=gc();}
		while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();}
		f?x=-x:0;
		return *this;
	}
	il IO& operator >>(string& s)
	{
		s="";char c=gc();
		while(c==' ' || c=='\n' || c=='\r') c=gc();
		while(c!=' ' && c!='\n' && c!='\r' && c!=EOF) s+=c,c=gc();
		return *this;
	}
	il IO& operator <<(const char c){pc(c);return *this;}
	template<typename T> il IO& operator <<(T x)
	{
		if(x<0) pc('-'),x=-x;
		do st[++st[0]]=x%10,x/=10;while(x);
		while(st[0]) pc(st[st[0]--]+'0');
		return *this;
	}
	il IO& operator <<(const string s){for(auto c:s) pc(c);return *this;}
	il IO& operator <<(const char* c){for(int i=0;c[i];i++) pc(c[i]);return *this;}
} fin,fout;
const int N=1000010,inf=1e9;
int n,m,mn[15][N],lst[N],a[N];
string ans[N];
bool Ed;
vector<pair<int,int> > b[N];
struct BIT
{
	int s[N];
	il void add(int l,int r,int k)
	{
		for(int i=l;i<=n;i+=(i&(-i))) s[i]+=k;
		for(int i=r+1;i<=n;i+=i&-i) s[i]-=k;
	}
	il int query(int x)
	{
		int ret=0;
		for(int i=x;i;i-=i&-i) ret+=s[i];
		return ret;
	}
} B[15];
signed main()
{
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	cerr<<(&St-&Ed)/1024.0/1024.0<<"\n";
	fin>>n>>m;
	for(int i=1;i<=n;i++) fin>>a[i];
	for(int i=1;i<=m;i++)
	{
		int l,r;fin>>l>>r;
		b[r].push_back({l,i});
	}
	for(int i=1;i<=N-10;i++) mn[0][i]=inf;
	for(int i=1;i<=n;i++)
	{
		int x=a[i];lst[x]=i;
		for(int j=x;j>=max(1,x-11);j--)
		{
			if(mn[x-j+1][j]==mn[x-j][j]) continue;
			if(j!=x) B[x-j].add(mn[x-j+1][j]+1,mn[x-j][j],-1);
			int Lst=mn[x-j+1][j];
			for(int k=x;k<=min(1000001,j+11);k++)
			{
				if(mn[k-j+1][j]!=Lst) B[k-j].add(mn[k-j+1][j]+1,Lst,-1);
				Lst=mn[k-j+1][j];
				mn[k-j+1][j]=min(mn[k-j][j],lst[k]);
				if(k!=j && mn[k-j+1][j]!=mn[k-j][j]) B[k-j].add(mn[k-j+1][j]+1,mn[k-j][j],1);
			}
		}
		for(auto j:b[i])
		{
			int l=j.first;
			int lst=B[1].query(l);
			for(int k=2;k<=11;k++)
			{
				int now=B[k].query(l);
				ans[j.second]+=char('0'+(lst-now)%10);
				lst=now;
			}
		}
	}
	for(int i=1;i<=m;i++) fout<<ans[i]<<"\n";
	return 0;
}

P5069 [Ynoi Easy Round 2015] 纵使日薄西山

假设我们某一步选择了一个位置 \(x\) ,那我们发现 \(x-1\) 这个位置和 \(x+1\) 这个位置就永远都不会被选中,并且 \(x\) 会操作 \(a_x\) 次。证明如下:

我们现在选择了 \(x\) ,那么证明现在 \(a_{x-1}<a_x\land a_{x+1}\le a_x\) ,后面会进行一些不包括 \(x-1,x,x+1\) 的操作,这些操作只会让 \(a_{x-1}\)\(a_{x+1}\) 减小,而不会影响 \(a_x\) ,所以下一次要对 \(a_{x-1},a_{x},a_{x+1}\) 做操作,一定会满足 \(a_{x-1}<a_x\land a_{x+1}\le a_x\) 而满足这两个条件就必然只会对 \(x\) 进行操作,而其它操作都影响不到 \(a_x\) ,所以让 \(a_x\) 变为 \(0\) 就只能让 \(x\) 进行 \(a_x\) 次操作。

然后我们发现只需要维护会被选中的位置的权值之和。这个信息明显是可合并的,又由于题目有单点修改,所以我们可以直接考虑线段树。具体的 \(push\_up\) 方法是记四个值以及若干个 \(vis\) ,分别是整个区间的答案以及左右端点选不选、整个区间忽略左端点的答案以及右端点选不选、整个区间忽略右端点的答案以及左端点选不选、整个区间忽略左右端点的答案。具体转移时可以分类讨论,一种是左儿子的右端点和右儿子的左端点都选了,那么我们通过比较大小来确定哪一个会被选,然后从被选的那个完整的答案加不被选的那个忽略不被选的端点的答案来更新当前区间的四个答案以及对应 \(vis\) 。另一种是没有都选,那就可以直接合并转移。

代码:

#include<bits/stdc++.h>
using namespace std;
#define il inline
#define ll long long
#define lid id<<1
#define rid id<<1|1
bool St;
struct IO
{
	static const int Size=(1<<21);
	char buf[Size],*p1,*p2;
	int st[105],Top;
	~IO(){clear();}
	il void clear(){fwrite(buf,1,Top,stdout);Top=0;}
	il char gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,Size,stdin),p1==p2)?EOF:*p1++;}
	il void pc(const char c){Top==Size&&(clear(),0);buf[Top++]=c;}
	il IO& operator >>(char& c){while(c=gc(),c==' ' || c=='\n' || c=='\r');return *this;}
	template<typename T>il IO& operator >>(T& x)
	{
		x=0;bool f=0;char c=gc();
		while(!isdigit(c)){if(c=='-') f=1;c=gc();}
		while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();}
		f?x=-x:0;
		return *this;
	}
	il IO& operator >>(string& s)
	{
		s="";char c=gc();
		while(c==' ' || c=='\n' || c=='\r') c=gc();
		while(c!=' ' && c!='\n' && c!='\r' && c!=EOF) s+=c,c=gc();
		return *this;
	}
	il IO& operator <<(const char c){pc(c);return *this;}
	template<typename T> il IO& operator <<(T x)
	{
		if(x<0) pc('-'),x=-x;
		do st[++st[0]]=x%10,x/=10;while(x);
		while(st[0]) pc(st[st[0]--]+'0');
		return *this;
	}
	il IO& operator <<(const string s){for(auto c:s) pc(c);return *this;}
	il IO& operator <<(const char* c){for(int i=0;c[i];i++) pc(c[i]);return *this;}
} fin,fout;
const int N=100010;
int a[N];
struct node
{
	ll ans,ansql,ansqr,ansqlr;
	int l,r;
	bool visl,visr,flr,fll;
} t[N<<2];
bool Ed;
il void Upd(int id,int k)
{
	t[id].ans=k;
	t[id].visl=t[id].visr=1;
	t[id].ansql=t[id].flr=t[id].ansqr=t[id].fll=t[id].ansqlr=0;
}
il void push_up(int id)
{
	t[id].l=t[lid].l;
	t[id].r=t[rid].r;
	if(t[lid].visr && t[rid].visl)
	{
		if(a[t[lid].r]>=a[t[rid].l])
		{
			t[id].ans=t[lid].ans+t[rid].ansql;
			t[id].visl=t[lid].visl;
			t[id].visr=t[rid].flr;
		}
		else
		{
			t[id].ans=t[lid].ansqr+t[rid].ans;
			t[id].visl=t[lid].fll;
			t[id].visr=t[rid].visr;
		}
	}
	else
	{
		t[id].ans=t[lid].ans+t[rid].ans;
		t[id].visl=t[lid].visl;
		t[id].visr=t[rid].visr;
	}
	if(t[lid].flr && t[rid].visl)
	{
		if(a[t[lid].r]>=a[t[rid].l])
		{
			t[id].ansql=t[lid].ansql+t[rid].ansql;
			t[id].flr=t[rid].flr;
		}
		else
		{
			t[id].ansql=t[lid].ansqlr+t[rid].ans;
			t[id].flr=t[rid].visr;
		}
	}
	else
	{
		t[id].ansql=t[lid].ansql+t[rid].ans;
		t[id].flr=t[rid].visr;
	}
	if(t[lid].visr && t[rid].fll)
	{
		if(a[t[lid].r]>=a[t[rid].l])
		{
			t[id].ansqr=t[lid].ans+t[rid].ansqlr;
			t[id].fll=t[lid].visl;
		}
		else
		{
			t[id].ansqr=t[lid].ansqr+t[rid].ansqr;
			t[id].fll=t[lid].fll;
		}
	}
	else
	{
		t[id].ansqr=t[lid].ans+t[rid].ansqr;
		t[id].fll=t[lid].visl;
	}
	if(t[lid].flr && t[rid].fll)
	{
		if(a[t[lid].r]>=a[t[rid].l])
		{
			t[id].ansqlr=t[lid].ansql+t[rid].ansqlr;
		}
		else
		{
			t[id].ansqlr=t[lid].ansqlr+t[rid].ansqr;
		}
	}
	else t[id].ansqlr=t[lid].ansql+t[rid].ansqr;
}
il void build(int id,int l,int r)
{
	if(l==r)
	{
		Upd(id,a[l]);
		t[id].l=t[id].r=l;
		return;
	}
	int mid=(l+r)>>1;
	build(lid,l,mid);
	build(rid,mid+1,r);
	push_up(id);
}
il void upd(int id,int l,int r,int x,int k)
{
	if(l==r)
	{
		a[x]=k;
		Upd(id,k);
		return;
	}
	int mid=(l+r)>>1;
	if(x<=mid) upd(lid,l,mid,x,k);
	else upd(rid,mid+1,r,x,k);
	push_up(id);
}
signed main()
{
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	cerr<<(&St-&Ed)/1024.0/1024.0<<"\n";
	int n;fin>>n;
	for(int i=1;i<=n;i++) fin>>a[i];
	build(1,1,n);
	int q;fin>>q;
	while(q--)
	{
		int x,y;fin>>x>>y;
		upd(1,1,n,x,y);
		fout<<t[1].ans<<"\n";
	}
	return 0;
}

Level 4:

P4690 [Ynoi Easy Round 2016] 镜中的昆虫

我们先考虑一个经典的在线区间数颜色的做法,就是对于每一个位置 \(i\) 找到上一个和它颜色相同的位置 \(lst_i\) (没有出现过就设为 \(-1\) ),然后区间 \([l,r]\) 数颜色就是区间 \([l,r]\)\(lst\) 小于 \(l\) 的数量,我们发现这是一个二维数点问题,由于这题空间卡得很紧,不能用主席树,所以可以考虑 \(cdq\) 分治离线解决二维数点问题,然后我们现在考虑如何动态维护每一个节点的 \(lst\)

由于是区间赋值,所以我们可以考虑使用颜色段均摊,我们会发现段内的 \(lst_i=i-1\) ,而被覆盖的段原先段内的 \(lst_i\) 也为 \(i-1\) 所以被覆盖的段内的 \(lst\) 是不用管的,需要修改的只有被覆盖的段的第一个位置(修改为 \(i-1\) )以及当前段的开头和结尾的下一个位置,而这个我们需要再开一个 set ,维护每一个段的右端点,至于为什么只需要维护右端点,因为我们上一次出现的位置一定是区间右端点,而这个 set 是随着 odt 一起维护的。然后我们就可以在均摊 \(O(nlog_2n)\) 的时间复杂度以及 \(O(n)\) 的空间复杂度解决维护 \(lst\) 的问题了,加上 \(cdq\) 分治以后复杂度不变。

代码:

#include<bits/stdc++.h>
using namespace std;
#define il inline
bool St;
struct IO
{
	static const int Size=(1<<21);
	char buf[Size],*p1,*p2;
	int st[105],Top;
	~IO(){clear();}
	il void clear(){fwrite(buf,1,Top,stdout);Top=0;}
	il char gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,Size,stdin),p1==p2)?EOF:*p1++;}
	il void pc(const char c){Top==Size&&(clear(),0);buf[Top++]=c;}
	il IO& operator >>(char& c){while(c=gc(),c==' ' || c=='\n' || c=='\r');return *this;}
	template<typename T>il IO& operator >>(T& x)
	{
		x=0;bool f=0;char c=gc();
		while(!isdigit(c)){if(c=='-') f=1;c=gc();}
		while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();}
		f?x=-x:0;
		return *this;
	}
	il IO& operator >>(string& s)
	{
		s="";char c=gc();
		while(c==' ' || c=='\n' || c=='\r') c=gc();
		while(c!=' ' && c!='\n' && c!='\r' && c!=EOF) s+=c,c=gc();
		return *this;
	}
	il IO& operator <<(const char c){pc(c);return *this;}
	template<typename T> il IO& operator <<(T x)
	{
		if(x<0) pc('-'),x=-x;
		do st[++st[0]]=x%10,x/=10;while(x);
		while(st[0]) pc(st[st[0]--]+'0');
		return *this;
	}
	il IO& operator <<(const string s){for(auto c:s) pc(c);return *this;}
	il IO& operator <<(const char* c){for(int i=0;c[i];i++) pc(c[i]);return *this;}
} fin,fout;
const int N=100010;
int a[N],pre[N],totq,ans[N],n,lsh[N<<1];
set<pair<pair<int,int>,int> > odt;
multiset<int> st[N<<1];
struct node
{
	bool op;
	int l,r,val;
} www[N];
vector<node> q;
bool Ed;
struct BIT
{
	int s[N];
	void add(int x,int k)
	{
		for(int i=x;i<=n;i+=i&-i) s[i]+=k;
	}
	int query(int x)
	{
		int ret=0;
		for(int i=x;i;i-=i&-i) ret+=s[i];
		return ret;
	}
} B;
il bool cmp(node x,node y)
{
	return x.l<y.l;
}
il void cdq(int l,int r)
{
	if(l==r) return;
	int mid=(l+r)>>1;
	cdq(l,mid);cdq(mid+1,r);
	int i=l,j=mid+1;
	while(i<=mid && j<=r)
	{
		if(q[i].l<=q[j].l)
		{
			if(!q[i].op) B.add(q[i].r,q[i].val);
			i++;
		}
		else
		{
			if(q[j].op) ans[q[j].val]+=B.query(q[j].r);
			j++;
		}
	}
	while(i<=mid)
	{
		if(!q[i].op) B.add(q[i].r,q[i].val);
		i++;
	}
	while(j<=r)
	{
		if(q[j].op) ans[q[j].val]+=B.query(q[j].r);
		j++;
	}
	for(int f=l;f<=mid;f++) if(!q[f].op) B.add(q[f].r,-q[f].val);
	sort(q.begin()+l,q.begin()+1+r,cmp);
}
il void splitl(int x)
{
	auto tmp=--odt.upper_bound({{x,n+1},0});
	int l=(*tmp).first.first,r=(*tmp).first.second,val=(*tmp).second;
	if(l==x) return;
	odt.erase(tmp);
	odt.insert({{l,x-1},val});
	odt.insert({{x,r},val});
	st[val].insert(x-1);
	st[val].insert(x);
}
il void splitr(int x)
{
	auto tmp=--odt.upper_bound({{x,n+1},0});
	int l=(*tmp).first.first,r=(*tmp).first.second,val=(*tmp).second;
	if(r==x) return;
	odt.erase(tmp);
	odt.insert({{l,x},val});
	odt.insert({{x+1,r},val});
	st[val].insert(x);
	st[val].insert(x+1);
}
il void add(int l,int r,int k)
{
	splitl(l);splitr(r);
	auto tmp1=odt.lower_bound({{l,0},k}),tmp2=odt.lower_bound({{r+1,0},k});
	while(tmp1!=tmp2)
	{
		int ll=(*tmp1).first.first,rr=(*tmp1).first.second;
		int val=(*tmp1).second;
		q.push_back({0,pre[ll],ll,-1});
		pre[ll]=ll-1;
		q.push_back({0,pre[ll],ll,1});
		st[val].erase(st[val].find(ll));
		st[val].erase(st[val].find(rr));
		auto tmp=st[val].upper_bound(rr);
		if(tmp!=st[val].end())
		{
			int idx=(*tmp);
			q.push_back({0,pre[idx],idx,-1});
			if(tmp==st[val].begin()) pre[idx]=0;
			else pre[idx]=*(--tmp);
			q.push_back({0,pre[idx],idx,1});
		}
		tmp1++;
	}
	tmp1=odt.lower_bound({{l,0},k});
	odt.erase(tmp1,tmp2);
	odt.insert({{l,r},k});
	st[k].insert(l);
	st[k].insert(r);
	q.push_back({0,pre[l],l,-1});
	auto tmp=st[k].lower_bound(l);
	if(tmp!=st[k].begin())
	{
		pre[l]=*(--tmp);
		q.push_back({0,pre[l],l,1});
	}
	else
	{
		pre[l]=0;
		q.push_back({0,pre[l],l,1});
	}
	tmp=st[k].upper_bound(r);
	if(tmp!=st[k].end())
	{
		int idx=(*tmp);
		q.push_back({0,pre[idx],idx,-1});
		pre[idx]=r;
		q.push_back({0,pre[idx],idx,1});
	}
}
signed main()
{
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	cerr<<(&St-&Ed)/1024.0/1024.0<<"\n";
	int m;fin>>n>>m;
	int tot=0;
	for(int i=1;i<=n;i++) fin>>a[i],lsh[++tot]=a[i];
	for(int i=1;i<=m;i++)
	{
		int op,l,r;fin>>op>>l>>r;
		www[i].op=op-1;
		www[i].l=l;www[i].r=r;
		if(www[i].op==0) fin>>www[i].val,lsh[++tot]=www[i].val;
	}
	sort(lsh+1,lsh+1+tot);
	int cnt=unique(lsh+1,lsh+1+tot)-lsh-1;
	for(int i=1;i<=n;i++) a[i]=lower_bound(lsh+1,lsh+1+cnt,a[i])-lsh;
	for(int i=1;i<=m;i++)
	{
		if(www[i].op==0) www[i].val=lower_bound(lsh+1,lsh+1+cnt,www[i].val)-lsh;
	}
	for(int i=1;i<=n;i++)
	{
		odt.insert({{i,i},a[i]});
		if(st[a[i]].empty()) pre[i]=0;
		else pre[i]=*(--st[a[i]].end());
		st[a[i]].insert(i);
		st[a[i]].insert(i);
		q.push_back({0,pre[i],i,1});
	}
	for(int i=1;i<=m;i++)
	{
		int op=www[i].op+1,l=www[i].l,r=www[i].r;
		if(op==1)
		{
			int k=www[i].val;
			add(l,r,k);
		}
		else
		{
			ans[++totq]=-(l-1);
			q.push_back({1,l-1,r,totq});
		}
	}
	cdq(0,(int)q.size()-1);
	for(int i=1;i<=totq;i++) fout<<ans[i]<<"\n";
	return 0;
}

Level5:

P5065 [Ynoi Easy Round 2014] 不归之人与望眼欲穿的人们

我们先考虑一种有前途的暴力,就是一个左端点对应的所有区间或值本质只有 \(log_2V\) 种。具体实现方法就是从右到左枚举区间左端点 \(l\) ,同时维护一个 \(dp_{l,i}\) 表示从 \(l\) 往后第一次出现的二进制下第 \(i\) 位上有值的位置。然后我们对于所有 \(i\) 将一个二元组 \((dp_{l,i},i)\) 按照 \(dp_{l,i}\) 排序,然后依次枚举每一个二元组 \((x,y)\) 所有的 \(x\) 就是以 \(l\) 为左端点的区间 \([l,r]\) 的或值会发生变化的所有位置。然后将前缀 \(2^y\) 或起来就是区间 \([l,x]\) 的或值。

然后对于这个排序的 \(loglogV\) 我们也可以把它弄掉,具体方法就是 \(l\) 相对于 \(l+1\)\(dp\) 更新的位置其实只有所有 \(a_l\) 的二进制下为 \(1\) 的位置,并且修改为了 \(l\) ,一定比其余值小,所以我们可以将所有这样的位置移到序列最前面,其余值相对位置不变即可。然后我们就获得了一个 \(O(qnlog_2V)\) 的暴力。

接下来我们考虑上数据结构,线段树应该是不行的,这一坨子东西一眼就没办法快速合并,所以我们可以考虑分块。每个块里面维护的信息有以块 \(idx\) 左端点为 \(l\)\((l,dp_{l,i})\) 排序过后的序列 \(pre_{idx}\),以块 \(idx\) 右端点为 \(r\)\((r,dp_{r,i})\) 排过序后的序列 \(suf_{idx}\) 其中 \(dp_{r,i}\) 是从 \(r\) 往前第一次出现的二进制下第 \(i\) 位上有值的位置,要求在块内,在块外的话就赋值为 \(-1\)\(dp_{l,i}\) 同理,不过是将从 \(r\) 往前改为从 \(l\) 往后。还有表示块 \(idx\) 内每一种长度的区间的或的最大值的数组 \(mx_{idx,len}\),这个可以在处理 \(pre\) 的时候顺手处理。

接下来考虑查询,首先对于块内的我们可以直接通过 \(mx\) 数组二分求出,然后我们考虑跨越块的贡献我们可以考虑右端点在每一个块内情况,设每一个块 \(idx\) 的左端点为 \(L_{idx}\) ,右端点为 \(R_{idx}\) ,然后我们可以通过一段前缀块的 \(suf\) 数组处理出来一个以 \(L_{idx}-1\)\(r\) 的对 \((dp_{r,i},i)\) 排好序的序列 \(tmp\),这里的 \(dp\) 是对整段前缀的。更新方式类似于 \(suf\) 的更新方式,不过不再是用 \(a_i\) 来更新,而是使用 \(suf\) 来更新。

然后我们可以枚举每一个 \(pre_{idx}\) 里的值,然后随着右端点的增加,在 \(tmp\) 中的左端点也一直在增加,我们可以直接使用双指针求解所有或值 \(\ge k\) 的区间的长度。然后再结合块内的长度,这个题就做完了。时间复杂度 \(O(q\sqrt{n}logV)\) 。可能需要通过查询多还是修改多来判断使用小块长还是大块长。

代码:

#include<bits/stdc++.h>
using namespace std;
#define il inline
#define ll long long
bool St;
struct IO
{
	static const int Size=(1<<21);
	char buf[Size],*p1,*p2;
	int st[105],Top;
	~IO(){clear();}
	il void clear(){fwrite(buf,1,Top,stdout);Top=0;}
	il char gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,Size,stdin),p1==p2)?EOF:*p1++;}
	il void pc(const char c){Top==Size&&(clear(),0);buf[Top++]=c;}
	il IO& operator >>(char& c){while(c=gc(),c==' ' || c=='\n' || c=='\r');return *this;}
	template<typename T>il IO& operator >>(T& x)
	{
		x=0;bool f=0;char c=gc();
		while(!isdigit(c)){if(c=='-') f=1;c=gc();}
		while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();}
		f?x=-x:0;
		return *this;
	}
	il IO& operator >>(string& s)
	{
		s="";char c=gc();
		while(c==' ' || c=='\n' || c=='\r') c=gc();
		while(c!=' ' && c!='\n' && c!='\r' && c!=EOF) s+=c,c=gc();
		return *this;
	}
	il IO& operator <<(const char c){pc(c);return *this;}
	template<typename T> il IO& operator <<(T x)
	{
		if(x<0) pc('-'),x=-x;
		do st[++st[0]]=x%10,x/=10;while(x);
		while(st[0]) pc(st[st[0]--]+'0');
		return *this;
	}
	il IO& operator <<(const string s){for(auto c:s) pc(c);return *this;}
	il IO& operator <<(const char* c){for(int i=0;c[i];i++) pc(c[i]);return *this;}
} fin,fout;
const int N=50010;
int n,m,a[N];
struct node
{
	int op,x,y;
} Q[N];
namespace case1
{
	const int inf=2e9,B=1200;
	int b[N],mx[N/B+10][B+10],vis[31];
	pair<int,int> q[2][N],pre[31][N/B+10],suf[31][N/B+10];
	void rebuild(int x)
	{
		int l=(x-1)*B+1,r=min(n,x*B),Pre=0,now=1;
		for(int i=0;i<=30;i++) q[Pre][i]={i,-1};
		for(int i=1;i<=B;i++) mx[x][i]=-1;
		for(int i=r;i>=l;i--)
		{
			int tt=0;
			for(int j=0;j<=30;j++) if(a[i]>>j&1) q[now][tt++]={j,i};
			for(int j=0;j<=30;j++) if(!(a[i]>>q[Pre][j].first&1)) q[now][tt++]=q[Pre][j];
			int w=0;
			for(int j=0;j<=30;j++)
			{
				if(q[now][j].second==-1) break;
				w|=(1<<q[now][j].first);
				mx[x][q[now][j].second-i+1]=max(mx[x][q[now][j].second-i+1],w);
			}
			swap(Pre,now);
		}
		for(int i=1;i<=B;i++) mx[x][i]=max(mx[x][i],mx[x][i-1]);
		for(int i=0;i<=30;i++) pre[i][x]=q[Pre][i];
		for(int i=0;i<=30;i++) q[Pre][i]={i,-1};
		for(int i=l;i<=r;i++)
		{
			int tt=0;
			for(int j=0;j<=30;j++) if(a[i]>>j&1) q[now][tt++]={j,i};
			for(int j=0;j<=30;j++) if(!(a[i]>>q[Pre][j].first&1)) q[now][tt++]=q[Pre][j];
			swap(Pre,now);
		}
		for(int i=0;i<=30;i++) suf[i][x]=q[Pre][i];
	}
	void init()
	{
		for(int i=1;i<=n;i++) b[i]=(i-1)/B+1;
		for(int i=1;i<=b[n];i++) rebuild(i);
	}
	void upd(int x,int y)
	{
		a[x]=y;
		rebuild(b[x]);
	}
	int query(int x)
	{
		int ans=inf;
		for(int i=1;i<=b[n];i++)
		{
			int l=1,r=B,ret=inf;
			while(l<=r)
			{
				int mid=(l+r)>>1;
				if(mx[i][mid]>=x) r=mid-1,ret=mid;
				else l=mid+1;
			}
			ans=min(ans,ret);
		}
		int Pre=0,now=1;
		for(int i=0;i<=30;i++) q[now][i]=suf[i][1];
		for(int i=2;i<=b[n];i++)
		{
			int idx=0,w1=0,w2=0;
			for(int j=0;j<=30;j++)
			{
				if(q[now][j].second==-1) break;
				idx=j;w1|=(1<<q[now][j].first);
			}
			for(int j=0;j<=30;j++)
			{
				if(pre[j][i].second==-1) break;
				w2|=(1<<pre[j][i].first);
				while(idx>=0 && (w2|(w1^(1<<q[now][idx].first)))>=x) w1^=(1<<q[now][idx].first),idx--;
				if((w1|w2)>=x && idx!=-1) ans=min(ans,pre[j][i].second-q[now][idx].second+1);
			}
			swap(Pre,now);
			int tt=0;
			for(int j=0;j<=30;j++) vis[j]=0;
			for(int j=0;j<=30;j++) if(suf[j][i].second!=-1) q[now][tt++]=suf[j][i],vis[suf[j][i].first]=1;
			for(int j=0;j<=30;j++) if(vis[q[Pre][j].first]==0) q[now][tt++]=q[Pre][j];
		}
		return ans==inf?-1:ans;
	}
	void solve()
	{
		init();
		for(int i=1;i<=m;i++)
		{
			int op=Q[i].op,x=Q[i].x;
			if(op==1)
			{
				int y=Q[i].y;
				upd(x,y);
			}
			else fout<<query(x)<<"\n";
		}
	}
}
namespace case2
{
	const int inf=2e9,B=30;
	int b[N],mx[N/B+10][B+10],vis[31];
	pair<int,int> q[2][N],pre[31][N/B+10],suf[31][N/B+10];
	void rebuild(int x)
	{
		int l=(x-1)*B+1,r=min(n,x*B),Pre=0,now=1;
		for(int i=0;i<=30;i++) q[Pre][i]={i,-1};
		for(int i=1;i<=B;i++) mx[x][i]=-1;
		for(int i=r;i>=l;i--)
		{
			int tt=0;
			for(int j=0;j<=30;j++) if(a[i]>>j&1) q[now][tt++]={j,i};
			for(int j=0;j<=30;j++) if(!(a[i]>>q[Pre][j].first&1)) q[now][tt++]=q[Pre][j];
			int w=0;
			for(int j=0;j<=30;j++)
			{
				if(q[now][j].second==-1) break;
				w|=(1<<q[now][j].first);
				mx[x][q[now][j].second-i+1]=max(mx[x][q[now][j].second-i+1],w);
			}
			swap(Pre,now);
		}
		for(int i=1;i<=B;i++) mx[x][i]=max(mx[x][i],mx[x][i-1]);
		for(int i=0;i<=30;i++) pre[i][x]=q[Pre][i];
		for(int i=0;i<=30;i++) q[Pre][i]={i,-1};
		for(int i=l;i<=r;i++)
		{
			int tt=0;
			for(int j=0;j<=30;j++) if(a[i]>>j&1) q[now][tt++]={j,i};
			for(int j=0;j<=30;j++) if(!(a[i]>>q[Pre][j].first&1)) q[now][tt++]=q[Pre][j];
			swap(Pre,now);
		}
		for(int i=0;i<=30;i++) suf[i][x]=q[Pre][i];
	}
	void init()
	{
		for(int i=1;i<=n;i++) b[i]=(i-1)/B+1;
		for(int i=1;i<=b[n];i++) rebuild(i);
	}
	void upd(int x,int y)
	{
		a[x]=y;
		rebuild(b[x]);
	}
	int query(int x)
	{
		int ans=inf;
		for(int i=1;i<=b[n];i++)
		{
			int l=1,r=B,ret=inf;
			while(l<=r)
			{
				int mid=(l+r)>>1;
				if(mx[i][mid]>=x) r=mid-1,ret=mid;
				else l=mid+1;
			}
			ans=min(ans,ret);
		}
		int Pre=0,now=1;
		for(int i=0;i<=30;i++) q[now][i]=suf[i][1];
		for(int i=2;i<=b[n];i++)
		{
			int idx=0,w1=0,w2=0;
			for(int j=0;j<=30;j++)
			{
				if(q[now][j].second==-1) break;
				idx=j;w1|=(1<<q[now][j].first);
			}
			for(int j=0;j<=30;j++)
			{
				if(pre[j][i].second==-1) break;
				w2|=(1<<pre[j][i].first);
				while(idx>=0 && (w2|(w1^(1<<q[now][idx].first)))>=x) w1^=(1<<q[now][idx].first),idx--;
				if((w1|w2)>=x && idx!=-1) ans=min(ans,pre[j][i].second-q[now][idx].second+1);
			}
			swap(Pre,now);
			int tt=0;
			for(int j=0;j<=30;j++) vis[j]=0;
			for(int j=0;j<=30;j++) if(suf[j][i].second!=-1) q[now][tt++]=suf[j][i],vis[suf[j][i].first]=1;
			for(int j=0;j<=30;j++) if(vis[q[Pre][j].first]==0) q[now][tt++]=q[Pre][j];
		}
		return ans==inf?-1:ans;
	}
	void solve()
	{
		init();
		for(int i=1;i<=m;i++)
		{
			int op=Q[i].op,x=Q[i].x;
			if(op==1)
			{
				int y=Q[i].y;
				upd(x,y);
			}
			else fout<<query(x)<<"\n";
		}
	}
}
bool Ed;
signed main()
{
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	cerr<<(&St-&Ed)/1024.0/1024.0<<"\n";
	fin>>n>>m;
	for(int i=1;i<=n;i++) fin>>a[i];
	int cnt=0;
	for(int i=1;i<=m;i++)
	{
		fin>>Q[i].op>>Q[i].x;
		if(Q[i].op==1) fin>>Q[i].y,cnt++;
	}
	if(cnt>=m/2) case2::solve();
	else case1::solve();
	return 0;
}

P5046 [Ynoi2019 模拟赛] Yuno loves sqrt technology I

首先直接考虑分块,首先忽略掉两个点在一个块内的情况,只考虑其余情况,然后直接开始分讨。

  1. 散块与散块之间的贡献,可以直接处理出两个数组 \(suf_{i,j}\)\(pre_{i,j}\) 表示将块 \(i\) 的前 \(j\) 个数与后 \(j\) 个数排好序后的数组,可以使用归并来处理出这两个数组,再顺便求出来块 \(i\) 前缀 \(j\) 的逆序对个数 \(pcnt_{i,j}\) 和后缀 \(scnt_{i,j}\)\(pre\)\(suf\) 两个数组可以用来归并求出两端散块的贡献,\(pcnt\)\(scnt\) 可以用来求出两边散块内部分别的贡献。

  2. 散块和整块之间的贡献可以处理出一个前缀和数组 \(cnt_{i,j}\) 表示前 \(i\) 个块中小于等于 \(j\) 的数个数,然后可以枚举散块内的每一个数来求出它对答案的贡献。

  3. 连续的一段整块,可以直接预处理出 \(val_{l,r}\) 表示第 \(l\) 个块到第 \(r\) 个块之间的逆序对个数,具体预处理方法就是在。然后 \(val_{l,r}\) 就可以先枚举 \(l\) ,再枚举 \(r\) 然后对于每一个新加入的块,通过枚举其中的每一个数用 \(cnt\) 来计算增量,然后就做完了。

现在有一个遗留的问题,就是如何求左右端点在同一个块内的答案。我们假设左端点是 \(l\) ,右端点是 \(r\) ,他们所在块为 \(idx\) ,块 \(idx\) 的左端点是 \(bl\) 右端点是 \(br\) ,那么我们发现这个区间的答案就是 \([bl,r]\) 的答案加上 \([l,br]\) 的答案加上 \([bl,l-1]\)\([r+1,br]\) 的贡献减去整个块 \(idx\) 的答案,其中第 \(1,2,4\) 项都是预处理的东西,而第 \(3\) 项可以用一段前缀和后缀归并求出。

代码:

#include<bits/stdc++.h>
using namespace std;
#define il inline
#define ll unsigned long long
#define int unsigned
bool St;
struct IO
{
	static const int Size=(1<<21);
	char buf[Size],*p1,*p2;
	int st[105],Top;
	~IO(){clear();}
	il void clear(){fwrite(buf,1,Top,stdout);Top=0;}
	il char gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,Size,stdin),p1==p2)?EOF:*p1++;}
	il void pc(const char c){Top==Size&&(clear(),0);buf[Top++]=c;}
	il IO& operator >>(char& c){while(c=gc(),c==' ' || c=='\n' || c=='\r');return *this;}
	template<typename T>il IO& operator >>(T& x)
	{
		x=0;bool f=0;char c=gc();
		while(!isdigit(c)){if(c=='-') f=1;c=gc();}
		while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();}
		f?x=-x:0;
		return *this;
	}
	il IO& operator >>(string& s)
	{
		s="";char c=gc();
		while(c==' ' || c=='\n' || c=='\r') c=gc();
		while(c!=' ' && c!='\n' && c!='\r' && c!=EOF) s+=c,c=gc();
		return *this;
	}
	il IO& operator <<(const char c){pc(c);return *this;}
	template<typename T> il IO& operator <<(T x)
	{
		if(x<0) pc('-'),x=-x;
		do st[++st[0]]=x%10,x/=10;while(x);
		while(st[0]) pc(st[st[0]--]+'0');
		return *this;
	}
	il IO& operator <<(const string s){for(auto c:s) pc(c);return *this;}
	il IO& operator <<(const char* c){for(int i=0;c[i];i++) pc(c[i]);return *this;}
} fin,fout;
const int N=100010,B=160;
int a[N],n,q,cnt[N/B+10][N],b[N],pre[N][B+10],suf[N][B+10],pcnt[N],scnt[N];
ll val[N/B+10][N/B+10];
bool Ed;
il void init()
{
	for(int i=1;i<=n;i++) b[i]=(i-1)/B+1;
	for(int i=1;i<=b[n];i++)
	{
		int l=(i-1)*B+1,r=min(i*B,n);
		for(int j=1;j<=n;j++) cnt[i][j]=cnt[i-1][j];
		for(int j=l;j<=r;j++)
		{
			cnt[i][a[j]]++;
			if(pre[j-1][j-l]<=a[j])
			{
				for(int k=1;k<=j-l;k++) pre[j][k]=pre[j-1][k];
				pre[j][j-l+1]=a[j];
				if(j!=l) pcnt[j]=pcnt[j-1];
			}
			else
			{
				for(int k=1;k<=j-l;k++)
				{
					if(pre[j-1][k]>a[j])
					{
						for(int f=1;f<k;f++) pre[j][f]=pre[j-1][f];
						pre[j][k]=a[j];
						for(int f=k;f<=j-l;f++) pre[j][f+1]=pre[j-1][f];
						pcnt[j]=pcnt[j-1]+j-l-k+1;
						break;
					}
				}
			}
		}
		for(int j=r;j>=l;j--)
		{
			if(suf[j+1][r-j]<=a[j])
			{
				for(int k=1;k<=r-j;k++) suf[j][k]=suf[j+1][k];
				suf[j][r-j+1]=a[j];
				if(j!=r) scnt[j]=scnt[j+1]+r-j;
			}
			else
			{
				for(int k=1;k<=r-j;k++)
				{
					if(suf[j+1][k]>a[j])
					{
						for(int f=1;f<k;f++) suf[j][f]=suf[j+1][f];
						suf[j][k]=a[j];
						for(int f=k;f<=r-j;f++) suf[j][f+1]=suf[j+1][f];
						scnt[j]=scnt[j+1]+k-1;
						break;
					}
				}
			}
		}
	}
	for(int i=1;i<=b[n];i++) for(int j=1;j<=n;j++) cnt[i][j]+=cnt[i][j-1];
	for(int i=1;i<=b[n];i++)
	{
		val[i][i]=pcnt[min(n,i*B)];
		for(int j=i+1;j<=b[n];j++)
		{
			val[i][j]=val[i][j-1]+pcnt[min(n,j*B)];
			for(int k=(j-1)*B+1;k<=min(j*B,n);k++)
			{
				val[i][j]=val[i][j]+(j-i)*B-(cnt[j-1][a[k]]-cnt[i-1][a[k]]);
			}
		}
	}
}
il ll query(int l,int r)
{
	int bl=b[l],br=b[r];
	if(bl==br)
	{
		ll ret=pcnt[r]+scnt[l];
		int i=1,j=1,n1=l-1-(bl-1)*B,n2=min(n,bl*B)-r;
		while(i<=n1 && j<=n2)
		{
			if(pre[l-1][i]>suf[r+1][j]) j++;
			else
			{
				i++;
				ret+=j-1;
			}
		}
		if(i<=n1) ret+=1ll*(n1-i+1)*n2;
		ret-=pcnt[min(n,bl*B)];
		return ret;
	}
	ll ret=val[bl+1][br-1];
	for(int i=l;i<=bl*B;i++) ret+=cnt[br-1][a[i]-1]-cnt[bl][a[i]-1];
	for(int i=(br-1)*B+1;i<=r;i++) ret+=(br-bl-1)*B-(cnt[br-1][a[i]]-cnt[bl][a[i]]);
	int i=1,j=1,n1=bl*B-l+1,n2=r-(br-1)*B;
	ret+=scnt[l]+pcnt[r];
	while(i<=n1 && j<=n2)
	{
		if(suf[l][i]>pre[r][j]) j++;
		else
		{
			i++;
			ret+=j-1;
		}
	}
	if(i<=n1) ret+=(n1-i+1)*n2;
	return ret;
}
signed main()
{
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	cerr<<(&St-&Ed)/1024.0/1024.0<<"\n";
	fin>>n>>q;
	for(int i=1;i<=n;i++) fin>>a[i];
	init();
	ll lstans=0,l,r;
	while(q--)
	{
		fin>>l>>r;
		l^=lstans,r^=lstans;
		fout<<(lstans=query(l,r))<<"\n";
	}
	return 0;
}

P5073 [Ynoi Easy Round 2015] 世上最幸福的女孩

要写这个题要先学习一个全新的数据结构叫做 \(KTT\) 。它用来维护这类问题:

给定 \(n\) 个一次函数 \(y=k_ix_i+b_i\) 要支持以下操作:

  1. \(i\in[l,r],x_i\rightarrow x_i+p\)
  2. \(i\in[l,r],b_i\rightarrow b_i+w\)
  3. 查询 \(max_{i=l}^rkx_i+b_i\)

其中满足 \(p\ge 0\) ,如果只有 \(2,3\) 操作就很简单了,但是加上了 \(1\) 操作以后我们在区间修改时的最大值位置就可能会发生变化,我们就不能迅速地求出最大值的变化量了,所以我们要维护一个新的值 \(intr\) 代表 \(x_i\) 还要增加多少最大值的位置会发生改变,我们称之为阈值。然后我们只需要维护一个新的函数 \(rebuild\) ,用来在达到阈值以后进行最大值位置以及新的阈值的更改。它的具体实现如下:

void rebuild(int id)
{
	if(t[id].intr>=0) return;
	push_down(id);
	rebuild(lid);
	rebuild(rid);
	push_up(id);
}

我们只需要在每次 \(1\) 操作区间修改的 \(lazy\_add\) 以后调用这个函数重新更新最大值就行了,具体复杂度证明不是很会,但肯定是 \(O(npoly(\log n))\) 级别的 ,一般情况下都是 \(O(n\log^2n)\) 的。

然后我们可以做一下P5693 EI 的第六分块 ,简要题意就是区间加正数,区间查询最大子段和。

我们发现其实我们可以利用那个阈值的思想,因为我们的 \(lmx,rmx,mx\) 的更新本质都是若干种形如 \(kx+b\) 的决策,其中 \(k\) 是区间长度,\(b\) 是什么其实不太重要,只需要知道由于 \(k\) 的不同,随着 \(x\) 的变化,答案的决策也可能随之发生变化即可。我们可以让 \(intr\) 设置为三个值每一种决策的阈值与左右儿子的阈值的 \(min\) 。其余函数就和最大子段和无异,复杂度大概是 \(O(n\log^2n)\)

然后回到这个题,由于我们必须让增量 \(\ge 0\) ,由于是全局修改,所以我们可以将所有的全局修改的总值排序以后从小到大做,保证每一次增量不负。还必须让所有值先加上总值最小的那个,因为这样才能保证第一次的增量非负。

代码:

#include<bits/stdc++.h>
using namespace std;
#define il inline
#define ll long long
#define lid id<<1
#define rid id<<1|1
bool St;
struct IO
{
	static const int Size=(1<<21);
	char buf[Size],*p1,*p2;
	int st[105],Top;
	~IO(){clear();}
	il void clear(){fwrite(buf,1,Top,stdout);Top=0;}
	il char gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,Size,stdin),p1==p2)?EOF:*p1++;}
	il void pc(const char c){Top==Size&&(clear(),0);buf[Top++]=c;}
	il IO& operator >>(char& c){while(c=gc(),c==' ' || c=='\n' || c=='\r');return *this;}
	template<typename T>il IO& operator >>(T& x)
	{
		x=0;bool f=0;char c=gc();
		while(!isdigit(c)){if(c=='-') f=1;c=gc();}
		while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();}
		f?x=-x:0;
		return *this;
	}
	il IO& operator >>(string& s)
	{
		s="";char c=gc();
		while(c==' ' || c=='\n' || c=='\r') c=gc();
		while(c!=' ' && c!='\n' && c!='\r' && c!=EOF) s+=c,c=gc();
		return *this;
	}
	il IO& operator <<(const char c){pc(c);return *this;}
	template<typename T> il IO& operator <<(T x)
	{
		if(x<0) pc('-'),x=-x;
		do st[++st[0]]=x%10,x/=10;while(x);
		while(st[0]) pc(st[st[0]--]+'0');
		return *this;
	}
	il IO& operator <<(const string s){for(auto c:s) pc(c);return *this;}
	il IO& operator <<(const char* c){for(int i=0;c[i];i++) pc(c[i]);return *this;}
} fin,fout;
//#define fin cin
//#define fout cout
const int N=300010;
const ll inf=1e18;
ll ans[N<<1],a[N];
vector<pair<int,pair<int,int> > > b[N<<1];
struct line
{
	ll k,b;
};
line operator +(line x,line y)
{
	return {x.k+y.k,x.b+y.b};
}
struct node
{
	line s,lmx,rmx,mx;
	ll lazy,intr;
} t[N<<2];
struct qwq
{
	ll s,lmx,rmx,mx;
};
struct Node
{
	int idx;
	ll x;
} q[N<<1];
il pair<line,ll> chkmax(line x,line y)
{
	if(x.b<y.b || (x.b==y.b && x.k<y.k)) swap(x,y);
	if(x.k>=y.k) return {x,inf};
	return {x,(x.b-y.b)/(y.k-x.k)};
}
il void push_up(int id)
{
	t[id].intr=min(t[lid].intr,t[rid].intr);
	t[id].s=t[lid].s+t[rid].s;
	auto tmp=chkmax(t[lid].lmx,t[lid].s+t[rid].lmx);
	t[id].lmx=tmp.first,t[id].intr=min(t[id].intr,tmp.second);
	tmp=chkmax(t[rid].rmx,t[rid].s+t[lid].rmx);
	t[id].rmx=tmp.first,t[id].intr=min(t[id].intr,tmp.second);
	if(t[lid].mx.b>t[rid].mx.b && t[lid].mx.b>t[lid].rmx.b+t[rid].lmx.b)
	{
		t[id].mx=t[lid].mx;
		tmp=chkmax(t[lid].mx,t[rid].mx);
		t[id].intr=min(t[id].intr,tmp.second);
		tmp=chkmax(tmp.first,t[lid].rmx+t[rid].lmx);
		t[id].intr=min(t[id].intr,tmp.second);
	}
	else
	{
		tmp=chkmax(t[lid].rmx+t[rid].lmx,t[rid].mx);
		t[id].intr=min(t[id].intr,tmp.second);
		tmp=chkmax(tmp.first,t[lid].mx);
		t[id].intr=min(t[id].intr,tmp.second);
		t[id].mx=tmp.first;
	}
}
il void lazy_add(int id,ll k)
{
	t[id].lazy+=k;
	t[id].intr-=k;
	t[id].s.b+=t[id].s.k*k;
	t[id].lmx.b+=t[id].lmx.k*k;
	t[id].rmx.b+=t[id].rmx.k*k;
	t[id].mx.b+=t[id].mx.k*k;
}
il void push_down(int id)
{
	lazy_add(lid,t[id].lazy);
	lazy_add(rid,t[id].lazy);
	t[id].lazy=0;
}
il void build(int id,int l,int r)
{
	if(l==r)
	{
		t[id].s.k=1,t[id].s.b=a[l];
		t[id].lmx.k=1,t[id].lmx.b=a[l];
		t[id].rmx.k=1,t[id].rmx.b=a[l];
		t[id].mx.k=1,t[id].mx.b=a[l];
		t[id].intr=inf;
		return;
	}
	int mid=(l+r)>>1;
	build(lid,l,mid);
	build(rid,mid+1,r);
	push_up(id);
}
il void rebuild(int id)
{
	if(t[id].intr>=0) return;
	push_down(id);
	rebuild(lid);
	rebuild(rid);
	push_up(id);
}
il void add(int id,int l,int r,int L,int R,int k)
{
	if(L<=l && r<=R)
	{
		lazy_add(id,k);
		rebuild(id);
		return;
	}
	push_down(id);
	int mid=(l+r)>>1;
	if(L<=mid) add(lid,l,mid,L,R,k);
	if(R>mid) add(rid,mid+1,r,L,R,k);
	push_up(id);
}
il qwq merge(qwq x,qwq y)
{
	qwq ret;
	ret.s=x.s+y.s;
	ret.lmx=max(x.lmx,x.s+y.lmx);
	ret.rmx=max(y.rmx,y.s+x.rmx);
	ret.mx=max(max(x.mx,y.mx),x.rmx+y.lmx);
	return ret;
}
il qwq query(int id,int l,int r,int L,int R)
{
	if(L<=l && r<=R) return {t[id].s.b,t[id].lmx.b,t[id].rmx.b,t[id].mx.b};
	push_down(id);
	int mid=(l+r)>>1;
	if(R<=mid) return query(lid,l,mid,L,R);
	else if(L>mid) return query(rid,mid+1,r,L,R);
	else return merge(query(lid,l,mid,L,R),query(rid,mid+1,r,L,R));
}
il bool cmp(Node x,Node y)
{
	return x.x<y.x;
}
bool Ed;
signed main()
{
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	cerr<<(&St-&Ed)/1024.0/1024.0<<"\n";
	int n,m;fin>>n>>m;
	for(int i=1;i<=n;i++) fin>>a[i];
	int tot=0,totq=0;
	ll now=0;
	for(int i=1;i<=m;i++)
	{
		int op;fin>>op;
		if(op==2)
		{
			int l,r;fin>>l>>r;totq++;
			b[tot].push_back({totq,{l,r}});
		}
		else
		{
			int x;fin>>x;tot++;
			now+=x;q[tot]={tot,now};
		}
	}
	sort(q,q+1+tot,cmp);
	for(int i=1;i<=n;i++) a[i]+=q[0].x;
	build(1,1,n);
	for(auto i:b[q[0].idx]) ans[i.first]=max(query(1,1,n,i.second.first,i.second.second).mx,0ll);
	for(int i=1;i<=tot;i++)
	{
		add(1,1,n,1,n,q[i].x-q[i-1].x);
		for(auto j:b[q[i].idx]) ans[j.first]=max(query(1,1,n,j.second.first,j.second.second).mx,0ll);
	}
	for(int i=1;i<=totq;i++) fout<<ans[i]<<"\n";
	return 0;
}

P5313 [Ynoi2011] WBLT

首先这道题和区间和值域都有关系,所以线段树不太可能,由于是离线,所以可以考虑莫队。

首先不难想到一个 bitset 的做法,就是在莫队移动指针时可以用一个 bitset 统计当前区间内所有存在的数。然后每次查询时可以遍历 \(x\) 然后维护一个长度为 \(b\) 的 bitset 表示对于当前 \(x\) 哪些 \(a\) 还是可行的,最后的答案就是第一个 bitset 中的所有数全部为 \(0\) 的时候。 bitset 的话建议手写 bitset 因为使用的 bitset 长度不一样,没法直接使用 STL 自带的 bitset 。时间复杂度是 \(O(n\sqrt{m}+\frac{nm}{w})\) 的吗?

但是我们发现这种方法在 \(b\) 特别小的情况下会退化为 \(O(nm)\) 。原因是我们的时间复杂度实际上是 \(O(n\sqrt{m}+\sum\left \lceil \frac{b}{w} \right \rceil\left \lceil \frac{n}{b} \right \rceil)\) 的。而后者在 \(b\)\(1\) 的时候会退化为 \(O(n^2)\) ,所以当 \(b<w\) 的时候我们需要一些其他方法。

我们发现我们实际上可以对于每一个小于 \(w\)\(b\) 都跑一遍莫队,每一次可以对所有 \(x\) 分块来维护最大的答案 \(x\) 。具体维护方法就是先跳一些至少有一个 \(a\) 合法的大块,然后找到第一个没有任何一个 \(a\) 合法的整块。然后在这个整块中暴力寻找最后一个有 \(a\) 合法的位置。假设每一个 \(b=i\) 的询问个数是 \(cnt_i\) 则这部分复杂度是 \(O(\sum_{i=1}^{w-1} (n\sqrt{cnt_i}+cnt_i\sqrt{V})=O(n\sqrt{wm}+m\sqrt{V})\) 。现在总复杂度能过,瓶颈在于 \(b\ge w\) 中的 bitset 。

一些提示:

  1. \(1ull<<64\) 是神秘 UB 行为,不能直接使用。
  2. 有点卡常,所以两部分的阈值可能得进行微调。

代码:

#include<bits/stdc++.h>
using namespace std;
#define il inline
#define ull unsigned long long
bool St;
struct IO
{
	static const int Size=(1<<21);
	char buf[Size],*p1,*p2;
	int st[105],Top;
	~IO(){clear();}
	il void clear(){fwrite(buf,1,Top,stdout);Top=0;}
	il char gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,Size,stdin),p1==p2)?EOF:*p1++;}
	il void pc(const char c){Top==Size&&(clear(),0);buf[Top++]=c;}
	il IO& operator >>(char& c){while(c=gc(),c==' ' || c=='\n' || c=='\r');return *this;}
	template<typename T>il IO& operator >>(T& x)
	{
		x=0;bool f=0;char c=gc();
		while(!isdigit(c)){if(c=='-') f=1;c=gc();}
		while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();}
		f?x=-x:0;
		return *this;
	}
	il IO& operator >>(string& s)
	{
		s="";char c=gc();
		while(c==' ' || c=='\n' || c=='\r') c=gc();
		while(c!=' ' && c!='\n' && c!='\r' && c!=EOF) s+=c,c=gc();
		return *this;
	}
	il IO& operator <<(const char c){pc(c);return *this;}
	template<typename T> il IO& operator <<(T x)
	{
		if(x<0) pc('-'),x=-x;
		do st[++st[0]]=x%10,x/=10;while(x);
		while(st[0]) pc(st[st[0]--]+'0');
		return *this;
	}
	il IO& operator <<(const string s){for(auto c:s) pc(c);return *this;}
	il IO& operator <<(const char* c){for(int i=0;c[i];i++) pc(c[i]);return *this;}
} fin,fout;
const int N=100010,W=50,V=100000;
int n,m,b[N],B,cnt[N],ans[N],a[N];
struct node
{
	int l,r,b;
} q[N];
vector<int> w[W];
il bool cmp(int x,int y)
{
	if(b[q[x].l]!=b[q[y].l]) return b[q[x].l]<b[q[y].l];
	if(b[q[x].l]&1) return q[x].r<q[y].r;
	return q[x].r>q[y].r;
}
ull vis[N],tmp[N];
il int query(int iiiii,int x)
{
	int ccc=V/x+1;
	for(int i=1;i<=x/64+1;i++) tmp[i]=-1;
	for(int i=1;i<=ccc;i++)
	{
		int ll=(i-1)*x,rr=min(V,i*x-1),now=0,fl=0;
		for(int j=ll;j<=rr;j+=64)
		{
			now++;
			int l=j,r=min(j+63,rr);
			if(rr==r)
			{
				if((l>>6)!=(r>>6))
				{
					ull val=0;
					val|=(vis[l>>6]>>(l&63));
					int len=r-((r>>6)<<6)+1;
					val|=((vis[r>>6]&((1ull<<len)-1ull))<<(r-l+1-len));
					tmp[now]&=val;
				}
				else
				{
					if(r-l+1!=64) tmp[now]&=((vis[l>>6]>>(l&63))&((1ull<<(r-l+1))-1ull));
					else tmp[now]&=(vis[l>>6]>>(l&63));
				}
			}
			else
			{
				ull val=0;
				val|=(vis[l>>6]>>(l&63));
				if((l>>6)!=(r>>6))
				{
					int len=r-((r>>6)<<6)+1;
					val|=((vis[r>>6]&((1ull<<len)-1ull))<<(r-l+1-len));
				}
				tmp[now]&=val;
			}
			if(tmp[now]) fl=1;
		}
		for(int j=now+1;j<=x/64+1;j++) tmp[j]=0;
		if(!fl) return i-1;
	}
	return ccc;
}
int ccc[W][N],ccc2[W][N];
ull fl[N],val[N];
il void add(int idx,int x)
{
	x=a[x];
	int X1=x%idx,X2=x/idx;
	ccc2[X1][X2]++;
	if(ccc2[X1][X2]==1)
	{
		val[X2]^=(1ull<<X1);
		ccc[X1][X2/B]++;
		if(ccc[X1][X2/B]==B) fl[X2/B]^=(1ull<<X1);
	}
}
il void del(int idx,int x)
{
	x=a[x];
	int X1=x%idx,X2=x/idx;
	ccc2[X1][X2]--;
	if(!ccc2[X1][X2])
	{
		val[X2]^=(1ull<<X1);
		ccc[X1][X2/B]--;
		if(ccc[X1][X2/B]==B-1) fl[X2/B]^=(1ull<<X1);
	}
}
il int query2(int idx)
{
	ull now=-1;
	for(int i=0;i<=(V/idx)/B;i++)
	{
		if(!(now&fl[i]))
		{
			for(int j=i*B;j<(i+1)*B;j++)
			{
				if(!(now&val[j])) return j;
				now&=val[j];
			}
		}
		now&=fl[i];
	}
	return V/idx+1;
}
il void solve(int idx)
{
	if(!w[idx].size()) return;
	B=n/sqrt(w[idx].size());
	if(!B) B++;
	for(int i=1;i<=n;i++) b[i]=(i-1)/B+1;
	sort(w[idx].begin(),w[idx].end(),cmp);
	int L=1,R=0;
	B=sqrt(V/idx);
	for(int i=0;i<idx;i++) for(int j=0;j<=(V/idx)/B+1;j++) ccc[i][j]=fl[j]=0;
	for(int i=0;i<idx;i++) for(int j=0;j<=V/idx+1;j++) ccc2[i][j]=val[j]=0;
	for(int i=0;i<(int)w[idx].size();i++)
	{
		while(L>q[w[idx][i]].l) add(idx,--L);
		while(R<q[w[idx][i]].r) add(idx,++R);
		while(L<q[w[idx][i]].l) del(idx,L++);
		while(R>q[w[idx][i]].r) del(idx,R--);
		ans[w[idx][i]]=query2(idx);
	}
}
bool Ed;
signed main()
{
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	cerr<<(&St-&Ed)/1024.0/1024.0<<"\n";
	fin>>n;
	for(int i=1;i<=n;i++) fin>>a[i];
	fin>>m;
//	fout<<((1ull<<64)-1ull)<<"\n";
	for(int i=1;i<=m;i++)
	{
		fin>>q[i].l>>q[i].r>>q[i].b;
		if(q[i].b>=W) w[0].push_back(i);
		else w[q[i].b].push_back(i); 
	}
	if(w[0].size())
	{
		B=n/sqrt(w[0].size());
		if(!B) B++;
		for(int i=1;i<=n;i++) b[i]=(i-1)/B+1;
		sort(w[0].begin(),w[0].end(),cmp);
		int L=1,R=0;
		for(int i=0;i<(int)w[0].size();i++)
		{
			while(L>q[w[0][i]].l)
			{
				L--;
				cnt[a[L]]++;
				if(cnt[a[L]]==1) vis[a[L]>>6]^=(1ull<<(a[L]&63));
			}
			while(R<q[w[0][i]].r)
			{
				R++;
				cnt[a[R]]++;
				if(cnt[a[R]]==1) vis[a[R]>>6]^=(1ull<<(a[R]&63));
			}
			while(L<q[w[0][i]].l)
			{
				cnt[a[L]]--;
				if(!cnt[a[L]]) vis[a[L]>>6]^=(1ull<<(a[L]&63));
				L++;
			}
			while(R>q[w[0][i]].r)
			{
				cnt[a[R]]--;
				if(!cnt[a[R]]) vis[a[R]>>6]^=(1ull<<(a[R]&63));
				R--;
			}
			ans[w[0][i]]=query(w[0][i],q[w[0][i]].b);
		}
	}
	for(int i=1;i<W;i++) solve(i);
	for(int i=1;i<=m;i++) fout<<ans[i]<<"\n";
	return 0;
}
posted @ 2026-04-30 15:50  stuart220_chen  阅读(19)  评论(1)    收藏  举报