『DS』做题记录

CF1491H

这题感觉没有 *3400,虚高。

分块,维护每个点第一次跳出这个块的祖先。

查询容易做到 \(O(\sqrt{n})\),考虑修改。

这个想要做每次严格的 \(O(\sqrt{n})\) 有点难,不妨想一下做什么类似均摊复杂度 \(O(n \sqrt{n})\)

这题的特殊祖辈关系方式和修改操作十分有启发意义。

不难发现一个整块进行超过 \(O(\sqrt{n})\) 次修改每个点都会一步跳出这个块,那么只需要一个整块被修改超过 \(O(\sqrt{n})\) 次记 \(tag\) \(O(1)\) 修改即可,而之前的 \(O(\sqrt{n})\) 修改直接保留更新整块,散块正常修改即可。

复杂度 \(O(n \sqrt{n})\)

CF1746F

维护的信息是什么元素出现次数为 \(k\) 的倍数,一眼很难维护,什么都想不到。

毕竟这个信息合并复杂,那么有没有什么办法让他信息合并容易呢?

试试求和?发现和是 \(k\) 的倍数是必要的,那么想一下为啥不是充分的,显然的求和可能会使得和是 \(k\) 的倍数,而存在没有出现 \(k\) 次的元素。

那么怎么办呢?仔细思考,这个判断方式和原序列一个元素具体值是啥无关,考虑随机赋权,大概随个 \(30\) 次就能过了。

可用树状数组维护,\(O(30n \log n)\)

CF464E

感觉有点意思。

考虑正常权值就是一个普通的最短路,思考通过高精度维护是否可行。

发现这样做复杂度是 \(O(mx\log n\log _{10} 2)\) 的,显然不能通过。

考虑这部分可否用数据结构维护出来,答案是可以的。

不难我们需要支持一下操作:

  1. 继承上一个 dis 到一个新的变量上
  2. 给一个变量加上 \(2^x\)
  3. 比较两个 dis

考虑在二进制下做上述三种操作,维护一个线段树,在线段树分治找到后驱连续 \(1\) 段,和查询 \(lcp\),然后 \(2,3\) 操作就是容易的。

最后继承直接套个主席树上去,复杂度 \(O(n \log^2 n)\),可以通过。

Code:

#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,avx2")
#pragma GCC optimize("Ofast","unroll-loops","inline")
#include<bits/stdc++.h>
#define ll long long
#define ull unsigned ll
#define pb push_back
#define pii pair<int,int>
#define fi first
#define se second
#define MP make_pair
//#define int ll
using namespace std;
const int N=2e5+20,LGN=40,Mx=1e5+30,mod=1e9+7;
const ull bas=137;
int rt[N],f[N];
ull pw[Mx+1];
struct Pseg{
	ull t[N*LGN];
	int s[N*LGN],lc[N*LGN],rc[N*LGN],tot;
	inline void ph(int rt,int l,int r){
		int mid=(l+r)>>1;
		t[rt]=(t[lc[rt]]+pw[mid-l+1]*t[rc[rt]]);
		s[rt]=s[lc[rt]]+s[rc[rt]];
	}
	inline void build(int &rt,int l,int r){
		rt=++tot;if(l==r) return ;
		int mid=(l+r)>>1;
		build(lc[rt],l,mid);build(rc[rt],mid+1,r);
	}
	void upd(int &rt,int lst,int L,int l,int r,int ql,int qr){
		rt=++tot;s[rt]=s[lst],t[rt]=t[lst],lc[rt]=lc[lst],rc[rt]=rc[lst];
		if(ql<=l&&r<=qr) return s[rt]=s[L],t[rt]=t[L],lc[rt]=lc[L],rc[rt]=rc[L],void();
		int mid=(l+r)>>1;
		if(ql<=mid) upd(lc[rt],lc[lst],lc[L],l,mid,ql,qr);
		if(qr>mid) upd(rc[rt],rc[lst],rc[L],mid+1,r,ql,qr);
		ph(rt,l,r);
	}
	void modify(int &rt,int lst,int l,int r,int p){
		rt=++tot;s[rt]=s[lst],t[rt]=t[lst],lc[rt]=lc[lst],rc[rt]=rc[lst];
		if(l==r) return s[rt]=1,t[rt]=1ull,void();
		int mid=(l+r)>>1;
		if(p<=mid) modify(lc[rt],lc[lst],l,mid,p);
		else modify(rc[rt],rc[lst],mid+1,r,p);
		ph(rt,l,r);
	}
	int qry(int rt,int l,int r,int ql,int qr){
		if(ql<=l&&r<=qr) return s[rt];
		int mid=(l+r)>>1,res=0;
		if(ql<=mid) res=qry(lc[rt],l,mid,ql,qr);
		if(qr>mid) res+=qry(rc[rt],mid+1,r,ql,qr);
		return res;
	}
	int pos(int rt,int l,int r,int w){
		if(l==r) return l;
		int mid=(l+r)>>1;
		if(mid<w) return pos(rc[rt],mid+1,r,w);
		if(qry(rt,l,r,w,mid)==mid-w+1) return pos(rc[rt],mid+1,r,mid+1);
		else return pos(lc[rt],l,mid,w);
	}
	int cmp(int rt,int lst,int l,int r){
		if(l==r) return t[rt]==t[lst]?0:(t[rt]>t[lst]?1:-1);
		int mid=(l+r)>>1;
		if(t[rc[rt]]==t[rc[lst]]) return cmp(lc[rt],lc[lst],l,mid);
		else return cmp(rc[rt],rc[lst],mid+1,r);
	}
	void ins(int &rt,int lst,int e,int w){
		int p=pos(lst,0,Mx,w);
		if(p==w) return modify(rt,lst,0,Mx,w),void();
		upd(rt,lst,e,0,Mx,w,p-1);
		modify(rt,rt,0,Mx,p);
	}
	void print(int rt){
		bool fl=0;
		for(int i=Mx;~i;i--){
			int u=qry(rt,0,Mx,i,i);
			fl|=u;if(fl) cout<<u<<' ';
		} 
		if(!fl) cout<<0;
		cout<<'\n';
	}
}tr;
struct Node{
	int x,id;
	bool operator <(const Node &a)const {return tr.cmp(x,a.x,0,Mx)>0;}
};
vector<int> g;
vector<pii> e[N];bool vis[N];
priority_queue<Node> q;
int n,m,s,t,ans,p[N];
void dij(){
	tr.build(rt[s],0,Mx);
	q.push((Node){rt[s],s});
//	tr.print(rt[s]);
	while(!q.empty()){
		Node x=q.top();q.pop();
		if(vis[x.id]) continue;vis[x.id]=1;
		for(auto y:e[x.id]){
			int p=0;tr.ins(p,rt[x.id],rt[s],y.se);
			if(rt[y.fi]==0||tr.cmp(p,rt[y.fi],0,Mx)<0){
				rt[y.fi]=p;f[y.fi]=x.id;
				if(!vis[y.fi]) q.push((Node){p,y.fi});
			}
		}
	}
	if(!rt[t]) return ans=-1,void();
	for(int i=0;i<=Mx;i++) if(tr.qry(rt[t],0,Mx,i,i)) ans=(ans+p[i])%mod;
	for(int i=t;i!=s;i=f[i]) g.pb(i);g.pb(s);
}
signed main(){
	pw[0]=1ull;for(int i=1;i<=Mx;i++) pw[i]=pw[i-1]*bas;
	p[0]=1;for(int i=1;i<=Mx;i++) p[i]=p[i-1]*2%mod;
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	cin>>n>>m;
	for(int i=1,u,v,w;i<=m;i++){
		cin>>u>>v>>w;
		e[u].pb(MP(v,w));e[v].pb(MP(u,w));
	}
	cin>>s>>t;
	dij();
	if(!~ans) return cout<<ans<<'\n',0;
	cout<<ans<<'\n';
	cout<<g.size()<<'\n';
	for(int i=g.size()-1;~i;i--) cout<<g[i]<<' ';cout<<'\n';
	return 0;
}

P9747

好题。

首先原序列是好的条件是难以快速判断的,那么首先该做的第一件事就是序列是好的充要条件。

直接找充要条件显然不现实,考虑手玩多个必要条件,然后考虑能否拼出一个充要的。

接下来我将说明哪些条件是充要的。

我们称超集数为满足在这个序列上,其二进制或这个序列上任意数仍然是其本身。

  1. 好的序列中必须包含至少一个超集数

容易发现的是,如果希望一个序列最终变成一个数,那个数显然是所有数的二进制或,而覆盖操作必须要在原序列存在才能使得最终变成一个数,固必然存在一个超集数

  1. 一个序列拥有两个超集数,则这个序列是好的,否则若经过若干次操作仍然无法使得原序列拥有两个超集数,则该序列不是好的

首先可以发现,若两个超集数相邻,那么通过往左右两边覆盖一定可以使得序列变成全部都是超集数,若两个超集数不相邻,设在序列上两个超集数分别在 \(x,y\),由于 \([x,y-1]\)\([x+1,y]\) 的二进制或相等(都有超集数),那么使用 \([x,y-1]\) 覆盖 \([x+1,y]\) 可以使得两个超集数落在 \(x,x+1\) 上,从而相邻。

那么如果一个序列无论如何也无法得到两个超集数,那么考虑一个序列最终被覆盖成全部相等前的操作,一定是用一个全部相等的序列去覆盖另一个序列(否则最终序列一定不是全部相等),而又由于之前理论,最后全部数都是超集数,那么又由于这个序列无论怎么操作都无法得出两个超集数,那么这个序列一定无法全部覆盖成一个数。

  1. 一个序列无论怎么操作,都无法得到两个超集数当且仅当,唯一那个超集数,左边所有数二进制或不等于这个超集数,右边所有数二进制或不等于这个超集数

观察一次操作是什么情况,我将超集数以及其左(右)边的一段向右(左)覆盖,右(左)边的二进制或不增,左(右)边的二进制或不变,想要通过一次操作使得序列有两个超集数必须满足的是选择的两个区间不交,一个包含超集数,另一个没有(否则原本拥有的超集数会被另一个区间覆盖),那么就不难发现上述条件满足。

那么这个好的序列的充要条件就可以简单描述了,现在问题是快速求解。

考虑一个数 \(a_i\) 作为超集数,对所有查询的贡献。

首先可以求出 \(L_{1,i},R_{1,i},L_{2,i},R_{2,i}\) 分别表示 \([L_{1,i},i]\) 二进制或为 \(a_{i}\)\([L_{1,i}-1,i]\) 二进制或不为 \(a_{i}\)\([R_{1,i},i-1]\) 二进制或为 \(a_{i}\)\([R_{1,i}+1,i-1]\) 二进制或不为 \(a_{i}\)\([i+1,L_{2,i}]\) 二进制或为 \(a_{i}\)\([i+1,L_{2,i}-1]\) 二进制或不为 \(a_{i}\)\([i,R_{2,i}]\) 二进制或为 \(a_{i}\)\([i,R_{1,i}+1]\) 二进制或不为 \(a_{i}\)

那么对于原序列为 \(a[l:r]\)\(a_i\) 作为超集数的充要条件又可以形式得描述为:

\(L_{1,i}\leq l \leq R_{1,i}\ \text{or}\ L_{2,i}\leq r \leq R_{2,i}\)

那么求一个区间的最长合法子区间就形如一个二位数点,稍加分讨即可。

复杂度 \(O(n \log n)\)

#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,avx2")
#include<bits/stdc++.h>
#define ll long long
#define pii pair<int,int>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
//#define int ll
using namespace std;
const int N=2e6+20,M=1e6+20,mod=998244353,LGN=22;
int n,q;
struct Bit1{
	int s[N];
	inline int lb(int x){return x&(-x);}
	inline void upd(int x,int v){for(;x<=n;x+=lb(x)) s[x]+=v;}
	inline void upd(int l,int r,int v){upd(l,v),upd(r+1,-v);}
	inline int qry(int x){int res=0;for(;x;x-=lb(x)) res+=s[x];return res;}
}t;
struct Bit2{
	int s[N];
	inline int lb(int x){return x&(-x);}
	inline void upd(int x,int v){while(x) s[x]=max(s[x],v),x-=lb(x);}
	inline int qry(int x){int res=0;for(;x<=n;x+=lb(x)) res=max(res,s[x]);return res;} 
}tr;
struct Seg2{
	int s[N<<2];
	inline void init(){for(int i=0;i<n<<2;i++) s[i]=0;}
	void upd(int rt,int l,int r,int ql,int qr,int v){
		if(ql<=l&&r<=qr) return s[rt]=max(s[rt],v),void();
		int mid=(l+r)>>1;
		if(ql<=mid) upd(rt<<1,l,mid,ql,qr,v);
		if(qr>mid) upd(rt<<1|1,mid+1,r,ql,qr,v);
	}
	int qry(int rt,int l,int r,int p){
		if(l==r) return s[rt];
		int mid=(l+r)>>1;
		if(p<=mid) return max(s[rt],qry(rt<<1,l,mid,p));
		else return max(s[rt],qry(rt<<1|1,mid+1,r,p));
	}
}s;
int a[N],L1[N],R1[N],L2[N],R2[N],st[N][LGN],lg[N],ans[N];
inline int qry(int l,int r){
	int k=lg[r-l+1];
	return st[l][k]|st[r-(1<<k)+1][k];
}
struct Node{int l,r;bool fl;};
vector<pii> vec[N],vc[N];vector<int> v[N];vector<Node> ve[N],e[N],v1[N],v2[N];
bool fl1[N],fl2[N];
void solve(){
	cin>>n>>q;
	for(int i=1;i<=n;i++) cin>>a[i],st[i][0]=a[i];
	int res=0;
	for(int j=1;j<LGN;j++){
		for(int i=1;i+(1<<j)-1<=n;i++) st[i][j]=st[i][j-1]|st[i+(1<<j-1)][j-1];
	}
	for(int i=1;i<=n;i++) fl1[i]=fl2[i]=1;
	for(int i=1,l,r,res;i<=n;i++){
		l=1,r=i,res=i;
		while(l<=r){
			int mid=(l+r)>>1;
			if(qry(mid,i)==a[i]) r=mid-1,res=mid; 
			else l=mid+1;
		}
		L1[i]=res;
		if(L1[i]==i||qry(L1[i],i-1)!=a[i]) fl1[i]=0;
		l=i,r=n,res=i;
		while(l<=r){
			int mid=(l+r)>>1;
			if(qry(i,mid)==a[i]) l=mid+1,res=mid; 
			else r=mid-1;
		}
		R2[i]=res;
		if(R2[i]==i||qry(i+1,R2[i])!=a[i]) fl2[i]=0;
		l=L1[i],r=i-1,res=L1[i];
		while(l<=r){
			int mid=(l+r)>>1;
			if(qry(mid,i-1)==a[i]) l=mid+1,res=mid;
			else r=mid-1;
		}R1[i]=res;
		l=i+1,r=R2[i],res=R2[i];
		while(l<=r){
			int mid=(l+r)>>1;
			if(qry(i+1,mid)==a[i]) r=mid-1,res=mid;
			else l=mid+1;
		}L2[i]=res;
	}
	for(int i=1;i<=q;i++) ans[i]=1;
	for(int i=1;i<=n;i++) if(fl1[i]||fl2[i]) v[R2[i]].pb(L1[i]);
	for(int i=1;i<=n;i++){
		if(fl1[i]) v1[R2[i]].pb((Node){L1[i],R1[i],0}),v1[i-1].pb((Node){L1[i],R1[i],1});
		if(fl2[i]) v2[L1[i]].pb((Node){L2[i],R2[i],0}),v2[i+1].pb((Node){L2[i],R2[i],1});
		if(fl2[i]) ve[R2[i]].pb((Node){L1[i],i,1});
		if(fl1[i]) ve[R2[i]].pb((Node){L1[i],R1[i],1});
		if(fl1[i]) e[L1[i]].pb((Node){i,R2[i],0});
		if(fl2[i]) e[L1[i]].pb((Node){L2[i],R2[i],0});
	}
	for(int i=1,l,r;i<=q;i++) cin>>l>>r,vec[r].pb(MP(l,i)),vc[l].pb(MP(r,i));
	for(int i=1;i<=n;i++){
		for(int u:v[i]) tr.upd(u,i-u+1);
		for(auto u:vec[i]) ans[u.se]=max(ans[u.se],tr.qry(u.fi));
	}
	for(int i=1;i<=n;i++){
		for(Node u:ve[i]) s.upd(1,1,n,u.l,u.r,i);
		for(auto u:vec[i]) ans[u.se]=max(ans[u.se],s.qry(1,1,n,u.fi)-u.fi+1);
	}s.init();
	for(int i=n;i;i--){
		for(Node u:e[i]) s.upd(1,1,n,u.l,u.r,n-i+1);
		for(auto u:vc[i]){
			int p=s.qry(1,1,n,u.fi);
			if(p) ans[u.se]=max(ans[u.se],u.fi-(n-p+1)+1);
		}
	}s.init();
	for(int i=1;i<=n;i++){
		for(Node u:v2[i]){
			if(!u.fl) t.upd(u.l,u.r,1);
			else t.upd(u.l,u.r,-1);
		}
		for(auto u:vc[i]) if(t.qry(u.fi)) ans[u.se]=max(ans[u.se],u.fi-i+1);
	}
	for(int i=1;i<=n;i++) t.s[i]=0;
	for(int i=n;i;i--){
		for(Node u:v1[i]){
			if(!u.fl) t.upd(u.l,u.r,1);
			else t.upd(u.l,u.r,-1);
		}
		for(auto u:vec[i]) if(t.qry(u.fi)) ans[u.se]=max(ans[u.se],i-u.fi+1);
	}
	for(int i=1;i<=n;i++) t.s[i]=0;
	for(int i=1;i<=q;i++) cout<<ans[i]<<'\n';
	for(int i=1;i<=n;i++) tr.s[i]=0,vector<pii>().swap(vc[i]),vector<pii>().swap(vec[i]),vector<Node>().swap(v1[i]),vector<Node>().swap(v2[i]);
	for(int i=1;i<=n;i++) vector<Node>().swap(ve[i]),vector<Node>().swap(e[i]),vector<int>().swap(v[i]);
}
signed main(){
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	lg[0]=-1;for(int i=1;i<N;i++) lg[i]=lg[i>>1]+1;
	int T,id;cin>>T>>id;
	while(T--) solve();
	return 0;
}

CF1301E

发现一个点作为矩阵左上角至多一个合法的矩阵。

通过前缀和技巧计算出 \(A_{i,j}\) 表示 \((i,j)\) 位置作为矩阵左上角的合法矩阵长度/2的值。

发现若矩阵内存在一个长度为 \(2a\) 的合法矩阵,那么必然存在长度 \(2(a-i)\) 的合法矩阵。

所以考虑二分答案,查询矩阵内是否存在长度为 \(2mid\) 的合法矩阵,这可以用前缀和优化到 \(O(1)\) 查询。

复杂度 \(O(n^3+q \log n)\)

#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,avx2")
#pragma GCC optimize("Ofast","unroll-loops","inline")
#include<bits/stdc++.h>
#define ll long long
//#define int ll
using namespace std;
const int N=5e2+20,M=1e6+20,mod=998244353;
int n,m,q;
struct Col{
	int a[N][N];
	inline int qry(int l1,int r1,int l2,int r2){return a[l2][r2]+a[l1-1][r1-1]-a[l1-1][r2]-a[l2][r1-1];}
}c[4];
int col[N][N],A[N][N];
int B[N/2][N][N];
inline bool chk(int col,int l1,int r1,int l2,int r2){return c[col].qry(l1,r1,l2,r2)==(l2-l1+1)*(r2-r1+1);}
inline bool qry(int p,int l1,int r1,int l2,int r2){
	if(l1>l2||r1>r2) return 0;
	return (B[p][l2][r2]+B[p][l1-1][r1-1]-B[p][l1-1][r2]-B[p][l2][r1-1])>0;
}
char s[N];
signed main(){
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	cin>>n>>m>>q;
	for(int i=1;i<=n;i++){
		cin>>s;
		for(int j=1;j<=m;j++){
			if(s[j-1]=='R') col[i][j]=0;
			if(s[j-1]=='G') col[i][j]=1;
			if(s[j-1]=='Y') col[i][j]=2;
			if(s[j-1]=='B') col[i][j]=3;
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			for(int k=0;k<4;k++) c[k].a[i][j]=c[k].a[i][j-1]+c[k].a[i-1][j]-c[k].a[i-1][j-1];
			c[col[i][j]].a[i][j]++;
		}
	}
	int mx=0;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			for(int a=1;i+2*a-1<=n&&j+2*a-1<=m;a++){
				if(chk(0,i,j,i+a-1,j+a-1)&&chk(1,i,j+a,i+a-1,j+2*a-1)&&chk(2,i+a,j,i+2*a-1,j+a-1)&&chk(3,i+a,j+a,i+2*a-1,j+2*a-1)){
					mx=max(mx,a);A[i][j]=a;break;
				}
			}
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			for(int k=1;k<=mx;k++) B[k][i][j]=B[k][i][j-1]+B[k][i-1][j]-B[k][i-1][j-1];
			B[A[i][j]][i][j]++;
		}
	}
	for(int i=1,l1,r1,l2,r2;i<=q;i++){
		cin>>l1>>r1>>l2>>r2;
		int l=1,r=mx,ans=0;
		while(l<=r){
			int mid=(l+r)>>1;
			if(qry(mid,l1,r1,l2-2*mid+1,r2-2*mid+1)) l=mid+1,ans=mid;
			else r=mid-1;
		}
		cout<<4*ans*ans<<'\n';
	}
	return 0;
}

CF1060G

出在模拟赛被打爆了。/kk

首先考虑一个位置是从哪里过来的是难的,那么考虑一下一个点一直操作会最终到哪里。

考虑一个点也没啥前途,考虑一下 \((a_n,a_n+n]\) 这段区间的点,经过一系列操作会到哪,而 \((a_n+n,+\infty)\) 的点显然通过有限的操作可以到 \((a_n,a_n+n]\) 内。

仔细思考这些点往前跳是一个什么样的形式。

这个区间会往前跳在这个区间左端点之前的洞的个数步数以及区间长度会变成之前的洞的个数步数,考虑一下区间长度变化具体删除的是哪些原本在 \((a_n,a_n+n]\) 上的点。

考虑一个洞在这个区间内,假设与在区间内相对位置为第 \(i\) 个,这会导致删除这个区间剩下的原本在 \((a_n,a_n+n]\) 上的点的第 \(i\) 小的点。

而这样的操作次数不会超过 \(n\) 次,可以用树状数组维护加上树状数组上二分。

接下来考虑已经求出了一个查询对应的 \((a_n,a_n+n]\) 上的点,以及跳到这个点的步数。

  1. \(k \geq T\),这个直接跳过去,然后当前位置加上 \(n(T-k)\) 就是答案。

  2. \(k < T\),考虑跳到对应点上,然后往回跳 \(k^{\prime} =k-T\) 步,然后把这类查询全部离线按照 \(k^{\prime}\) 递增排序,模拟之前跳区间的方法即可。

复杂度 \(O(n \log n)\)

#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,avx2")
#pragma GCC optimize("Ofast","unroll-loops","inline")
#include<bits/stdc++.h>
#define ll long long
//#define int ll
using namespace std;
const int N=2e5+20,M=1e6+20,mod=998244353,LGN=21;
int n,m,a[N],tot,t[N],cnt;ll ans[N];
struct Node{int x,k,id;bool a;}q[N<<1];
struct Qry{int k,x,id;}v[N<<1];
inline bool cmp(Node u,Node v){return u.x==v.x?u.a<v.a:u.x>v.x;}
inline bool Cmp(Qry u,Qry v){return u.k<v.k;}
struct Bit{
	int s[N];
	inline void init(){memset(s,0,sizeof(s));}
	inline int lb(int x){return x&(-x);}
	inline void add(int x,int v){for(;x<=n;x+=lb(x)) s[x]+=v;}
	inline int q(int x){int res=0;for(;x;x-=lb(x)) res+=s[x];return res;}
	inline int qry(int l,int r){return q(r)-q(l-1);}
	inline int kth(int k){
		int res=0,p=0,b;
		for(int i=LGN-1;~i;i--) if((b=(p|(1<<i)))<n){
			if(res+s[b]<k) res+=s[b],p=b;
		}
		return p+1;
	}
}tr;
signed main(){
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	cin>>m>>n;
	for(int i=1;i<=n;i++) cin>>q[i].x,q[i].a=1,a[i]=q[i].x;
	for(int i=1;i<=n;i++) tr.add(i,1);
	int j=a[n]+1;tot=n;
	for(int i=1,x,k;i<=m;i++){
		cin>>x>>k;
		if(x>=j) ans[i]=x+1ll*k*n;
		else if(x<q[1].x) ans[i]=x;
		else q[++tot]=(Node){x,k,i,0};
	}
	sort(q+1,q+tot+1,cmp);
	for(int i=1,T=0,p=n;i<=tot;i++){
		int pos=q[i].x;
		if(pos<j){
			int d=upper_bound(a+1,a+n+1,j+p-1)-a-1,s=0;
			while(d&&a[d]>=j){
				int o=tr.kth(a[d]-j+1);
				tr.add(o,-1);
				d--;s++;
			}
			p-=s;
			int u=(j-pos+p-1)/p;T+=u;j-=u*p;
		}
		if(!q[i].a){
			int u=(pos-j+1);
			if(q[i].k>=T) ans[q[i].id]=a[n]+tr.kth(u)+1ll*(q[i].k-T)*n;
			else v[++cnt]=(Qry){T-q[i].k,tr.kth(u),q[i].id};
		}
	}
	tr.init();
	for(int i=1;i<=n;i++) tr.add(i,1);
	sort(v+1,v+cnt+1,Cmp);
	int e=1;j=a[n]+1;
	for(int i=1,T=0,p=n;i<=tot;i++){
		int pos=q[i].x;
		if(pos<j){
			int d=upper_bound(a+1,a+n+1,j+p-1)-a-1,s=0;
			while(d&&a[d]>=j){
				int o=tr.kth(a[d]-j+1);tr.add(o,-1);
				d--;s++;
			}
			p-=s;
			int u=(j-pos+p-1)/p;T+=u;
			while(e<=cnt&&v[e].k<=T){
				int y=v[e].k-T+u;
				int o=tr.q(v[e].x);
				ans[v[e].id]=j-y*p+o-1;
				e++;
			}
			j-=u*p;
		}
	}
	for(int i=1;i<=m;i++) cout<<ans[i]<<'\n';
	return 0;
}

CF1706E

题意转化为是每个时刻增加一边,判断某个连通块内是否存在 \([l,r]\) 全部点。

建出重构树,然后 \([l,r]\) 区间内所有点的 lca 上的权值就是答案。

查询一段区间的 lca,考虑拿出 dfn 序最大的和 dfn 序最小的两个点求 lca 即可。

复杂度 \(O(n \log n)\)

当然我赛事写了个整体二分套线段树合并加可撤销数据结构做到 \(O(n \log^2 n)\)

#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,avx2")
#pragma GCC optimize("Ofast","unroll-loops","inline")
#include<bits/stdc++.h>
#define ll long long
//#define int ll
#define pb push_back
using namespace std;
const int N=2e5+20,LGN=21;
vector<int> e[N];
int u[N],v[N],F[N],lg[N],w[N],g[LGN][N/2],f[LGN][N/2];
int tot,n,m,Q;
inline int find(int x){return x==F[x]?x:F[x]=find(F[x]);}
int fa[N],sz[N],son[N],top[N],dep[N],dfn[N],id[N],dfc;
void dfs1(int u,int F){
	dep[u]=dep[F]+1;fa[u]=F;sz[u]=1;
	for(int v:e[u]){
		dfs1(v,u);sz[u]+=sz[v];
		if(sz[v]>sz[son[u]]) son[u]=v;
	}
}
void dfs2(int u,int tp){
	top[u]=tp;
	if(son[u]) dfs2(son[u],tp);
	for(int v:e[u]) if(v!=son[u]) dfs2(v,v); 
}
void dfs(int u){
	dfn[id[u]=++dfc]=u;
	for(int v:e[u]) dfs(v);
}
int lca(int x,int y){
	while(top[x]!=top[y]){
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		x=fa[top[x]];
	}
	return dep[x]<dep[y]?x:y;
}
int qry(int l,int r,bool op){
	int k=lg[r-l+1];
	return !op?min(f[k][l],f[k][r-(1<<k)+1]):max(g[k][l],g[k][r-(1<<k)+1]);
}
void solve(){
	cin>>n>>m>>Q;dfc=0;
	for(int i=1;i<=tot;i++) son[i]=F[i]=w[i]=0,e[i].clear();
	for(int i=1;i<=n;i++) F[i]=i;
	for(int i=1;i<=m;i++) cin>>u[i]>>v[i];
	tot=n;
	for(int i=1;i<=m;i++){
		int fu=find(u[i]),fv=find(v[i]);
		if(fu!=fv) ++tot,F[fu]=F[fv]=F[tot]=tot,w[tot]=i,e[tot].pb(fu),e[tot].pb(fv);
	}
	dfs(tot);dfs1(tot,0);dfs2(tot,tot);
	for(int i=1;i<=n;i++) f[0][i]=id[i],g[0][i]=id[i];
	for(int j=1;j<LGN;j++) for(int i=1;i+(1<<j)-1<=n;i++){
		f[j][i]=min(f[j-1][i],f[j-1][i+(1<<j-1)]);
		g[j][i]=max(g[j-1][i],g[j-1][i+(1<<j-1)]);
	}
	int res=0;
	for(int i=1,l,r;i<=Q;i++){
		cin>>l>>r;
		int u=qry(l,r,0),v=qry(l,r,1);
		cout<<w[lca(dfn[u],dfn[v])]<<' ';
	}
	cout<<'\n';
}
signed main(){
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	lg[0]=-1;for(int i=1;i<N;i++) lg[i]=lg[i>>1]+1;
	int T;cin>>T;
	while(T--) solve();
	return 0;
}

以下是整体二分套线段树合并加可撤销数据结构,当然过不了。

#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,avx2")
#pragma GCC optimize("Ofast","unroll-loops","inline")
#include<bits/stdc++.h>
#define ll long long
//#define int ll
using namespace std;
const int N=1e6+20,LGN=23;
namespace FastIO{
	struct IO {
	    char ibuf[(1 << 20) + 1], *iS, *iT, obuf[(1 << 20) + 1], *oS;
	    IO() : iS(ibuf), iT(ibuf), oS(obuf) {} ~IO() { fwrite(obuf, 1, oS - obuf, stdout); }
		#define gh() (iS == iT ? iT = (iS = ibuf) + fread(ibuf, 1, (1 << 20) + 1, stdin), (iS == iT ? EOF : *iS++) : *iS++)
		inline bool eof (const char &ch) { return ch == ' ' || ch == '\n' || ch == '\r' || ch == 't' || ch == EOF; }
	    inline long long read() {
	        char ch = gh();
	        long long x = 0;
	        bool t = 0;
	        while (ch < '0' || ch > '9') t |= ch == '-', ch = gh();
	        while (ch >= '0' && ch <= '9') x = (x << 1) + (x << 3) + (ch ^ 48), ch = gh();
	        return t ? ~(x - 1) : x;
	    }
	    inline void read (char *s) {
	    	char ch = gh(); int l = 0;
	    	while (eof(ch)) ch = gh();
	    	while (!eof(ch)) s[l++] = ch, ch = gh();
	    }
	    inline void read (double &x) {
	    	char ch = gh(); bool t = 0;
	    	while (ch < '0' || ch > '9') t |= ch == '-', ch = gh();
	    	while (ch >= '0' && ch <= '9') x = x * 10 + (ch ^ 48), ch = gh();
	    	if (ch != '.') return t && (x = -x), void(); ch = gh();
	    	for (double cf = 0.1; '0' <= ch && ch <= '9'; ch = gh(), cf *= 0.1) x += cf * (ch ^ 48);
	    	t && (x = -x);
	    }
	    inline void pc (char ch) {
	    	#ifdef ONLINE_JUDGE
	    	if (oS == obuf + (1 << 20) + 1) fwrite(obuf, 1, oS - obuf, stdout), oS = obuf; 
	    	*oS++ = ch;
	    	#else
	    	putchar(ch);
	    	#endif
		}
		template<typename _Tp>
	    inline void write (_Tp x) {
	    	static char stk[64], *tp = stk;
	    	if (x < 0) x = ~(x - 1), pc('-');
			do *tp++ = x % 10, x /= 10;
			while (x);
			while (tp != stk) pc((*--tp) | 48);
	    }
	    inline void write (char *s) {
	    	int n = strlen(s);
	    	for (int i = 0; i < n; i++) pc(s[i]);
	    }
	} io;
	inline long long read () { return io.read(); }
	template<typename Tp>
	inline void read (Tp &x) { io.read(x); }
	template<typename _Tp>
	inline void write (_Tp x) { io.write(x); }
}
using namespace FastIO;
struct Backds{
	struct Node{int v,*x;}stk[(N<<6)+(N<<5)];
	int tp;
	inline void ins(int &x){stk[++tp]=(Node){x,&x};}
	inline void re(int t){while(tp>t) *stk[tp].x=stk[tp].v,tp--;}
}t;
int fa[N],rt[N];
inline int find(int x){return x==fa[x]?x:(t.ins(fa[x]),fa[x]=find(fa[x]));}
struct Seg{
	int s[N*LGN];int lc[N*LGN],rc[N*LGN],tot;
	void init(){for(int i=1;i<=tot;i++) s[i]=lc[i]=rc[i]=0;tot=0;}
	inline void upd(int &rt,int l,int r,int p){
		if(!rt) rt=++tot;
		if(l==r) return s[rt]=1,void();
		int mid=(l+r)>>1;
		if(p<=mid) upd(lc[rt],l,mid,p);
		else upd(rc[rt],mid+1,r,p);
	}
	int merge(int x,int y,int l,int r){
		if(!x||!y) return x|y;
		if(l==r) return t.ins(s[x]),s[x]|=s[y],x;
		int mid=(l+r)>>1;
		t.ins(lc[x]),lc[x]=merge(lc[x],lc[y],l,mid);
		t.ins(rc[x]),rc[x]=merge(rc[x],rc[y],mid+1,r);
		t.ins(s[x]),s[x]=s[lc[x]]&s[rc[x]];
		return x;
	}
	int qry(int rt,int l,int r,int ql,int qr){
		if(!rt) return 0;
		if(ql<=l&&r<=qr) return s[rt];
		int mid=(l+r)>>1,res=1;
		if(ql<=mid) res&=qry(lc[rt],l,mid,ql,qr);
		if(qr>mid) res&=qry(rc[rt],mid+1,r,ql,qr);
		return res;
	}
}tr;
struct Edge{int u,v;}e[N<<1];
struct Qry{int l,r,id;}q[N],g[2][N];
int ans[N],n,m,Q;
inline void solve(int l,int r,int L,int R){
	if(L==R){
		if(L){
			int u=e[L].u,v=e[L].v;
			if(u>v) swap(u,v);
			int fu=find(u),fv=find(v);
			if(fu!=fv) fa[fu]=fv;
			if(fu!=fv) rt[fv]=tr.merge(rt[fv],rt[fu],1,n);
		}
		for(int i=l;i<=r;i++) ans[i]=L;
		return ;
	}
	int mid=(L+R)>>1,tp=t.tp,tot0=0,tot1=0;
	for(int i=L;i<=mid;i++){
		int u=e[i].u,v=e[i].v;
		if(u>v) swap(u,v);
		int fu=find(u),fv=find(v);
		if(fu!=fv) t.ins(fa[fu]),fa[fu]=fv;
		if(fu!=fv) t.ins(rt[fv]),rt[fv]=tr.merge(rt[fv],rt[fu],1,n);
	}
	for(int i=l;i<=r;i++){
		if(tr.qry(rt[find(q[i].l)],1,n,q[i].l,q[i].r)) g[0][++tot0]=q[i];
		else g[1][++tot1]=q[i]; 
	}
	t.re(tp);
	for(int i=1;i<=tot0;i++) q[i+l-1]=g[0][i];
	for(int i=tot0+1;i<=tot1+tot0;i++) q[i+l-1]=g[1][i-tot0];
	solve(l,tot0+l-1,L,mid);
	solve(tot0+l,r,mid+1,R);
}
void solve(){
	n=read();m=read();Q=read();t.tp=0;
	for(int i=1;i<=n;i++) rt[i]=0;tr.init();
	for(int i=1;i<=m;i++) e[i].u=read(),e[i].v=read();
	for(int i=1;i<=Q;i++) q[i].l=read(),q[i].r=read(),q[i].id=i;
	for(int i=1;i<=n;i++) tr.upd(rt[i],1,n,i),fa[i]=i;
	solve(1,Q,0,m);
	for(int i=1;i<=Q;i++) write(ans[i]),io.pc(' ');io.pc('\n');
}
signed main(){
	int T;T=read();
	while(T--) solve();
	return 0;
}

CF1693E

神仙题!

首先有个贪心是这样的,从大到小枚数,然后将相同数扣掉,每个位置重新填当前扣完后序列的前缀最大值和后缀最大值的最小值。

难道是神必数据结构维护吗?否,考虑给每个扣掉的数一个状态,\(0,1,2\) 分别表示未确定填入前缀最大值还是后缀最大值,填前缀最大值,填后缀最大值。

扫到某个数的时候,如果其左边有 \(0\) 状态,更改为 \(2\) 状态,如果右边有 \(0\) 状态,更改为 \(1\) 状态。

如果左边有 \(1\) 状态,算入答案,然后又变回 \(0\) 状态,如果左边有 \(2\) 状态,算入答案,然后又变回 \(0\) 状态。

上述所有操作均可通过树状数组优化,复杂度 \(O(n \log n)\)

#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,avx2")
#pragma GCC optimize("Ofast","unroll-loops","inline")
#include<bits/stdc++.h>
#define ll long long
//#define int ll
#define pb push_back
using namespace std;
const int N=2e5+20,M=1e6+20,mod=998244353;
int n,a[N];
vector<int> v[N];
struct Bit{
	int s[N];
	inline void init(){memset(s,0,sizeof(s));}
	inline int lb(int x){return x&(-x);}
	inline void add(int x,int v){for(;x<=n;x+=lb(x)) s[x]+=v;}
	inline int q(int x){int res=0;for(;x;x-=lb(x)) res+=s[x];return res;}
	inline int qry(int l,int r){return q(r)-q(l-1);}
}tr;
signed main(){
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i],v[a[i]].pb(i);
	ll ans=0;
	for(int i=n,l=1,r=n;i;i--) if(!v[i].empty()){
		int L=v[i][0],R=v[i].back();
		ans+=(int)v[i].size();
		ans+=tr.qry(L,R);
		if(r<L) ans+=tr.qry(r+1,L),l=r+1,r=R;
		else if(R<l) ans+=tr.qry(R,l-1),r=l-1,l=L;
		else l=L,r=R;
		for(int x:v[i]) tr.add(x,1);
	}
	cout<<ans<<'\n';
	return 0;
}

P6578 [Ynoi2019] 魔法少女网站

操作分块。

对于每块,修改 \(O(\sqrt{n})\),查询 \(O(\sqrt{n})\)

先将序列更新为修改后的可能最大值,然后每次查询前暴力进行 \(O(\sqrt{n})\) 次修改。

此时有 \(O(n\sqrt{n})\)次修改,\(O(n)\) 次查询。

考虑维护一个 \(O(1)-O(\sqrt{n})\) 的 ds 来 balance 一下复杂度。

维护 \(01\) 序列 \(b\),设当前查询 \(k\)\(b_{i}=0\) 表示 \(a_{i} \leq k\)\(b_{i}=1\) 表示 \(a_{i} > k\)

不难发现这 \(O(n \sqrt{n})\) 次修改都是 \(0\rightarrow 1\)

那序列分块维护如下信息:

  1. \(1\) 极长连续段的开头存下结尾,结尾存下开头。

  2. \(1\) 极长连续段在块内的长度平方和。

这些都是不难维护的,复杂度 \(O(n \sqrt n)\)

卡常未果的代码:

#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,avx2")
//#pragma GCC optimize("Ofast")
//#pragma GCC optimize("inline")
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<set>
#include<queue>
#include<bits/stdc++.h>
#define ll long long
#define pb push_back
//#define int ll
using namespace std;
const int N=3e5+20;
int B,C;
namespace FastIO {
	struct IO {
	    char ibuf[(1 << 20) + 1], *iS, *iT, obuf[(1 << 20) + 1], *oS;
	    IO() : iS(ibuf), iT(ibuf), oS(obuf) {} ~IO() { fwrite(obuf, 1, oS - obuf, stdout); }
		#if ONLINE_JUDGE
		#define gh() (iS == iT ? iT = (iS = ibuf) + fread(ibuf, 1, (1 << 20) + 1, stdin), (iS == iT ? EOF : *iS++) : *iS++)
		#else
		#define gh() getchar()
		#endif
		inline bool eof (const char &ch) { return ch == ' ' || ch == '\n' || ch == '\r' || ch == 't' || ch == EOF; }
	    inline int read() {
	        char ch = gh();
	        int x = 0;
	        bool t = 0;
	        while (ch < '0' || ch > '9') t |= ch == '-', ch = gh();
	        while (ch >= '0' && ch <= '9') x = (x << 1) + (x << 3) + (ch ^ 48), ch = gh();
	        return t ? ~(x - 1) : x;
	    }
	    inline void read (char *s) {
	    	char ch = gh(); int l = 0;
	    	while (eof(ch)) ch = gh();
	    	while (!eof(ch)) s[l++] = ch, ch = gh();
	    }
	    inline void read (double &x) {
	    	char ch = gh(); bool t = 0;
	    	while (ch < '0' || ch > '9') t |= ch == '-', ch = gh();
	    	while (ch >= '0' && ch <= '9') x = x * 10 + (ch ^ 48), ch = gh();
	    	if (ch != '.') return t && (x = -x), void(); ch = gh();
	    	for (double cf = 0.1; '0' <= ch && ch <= '9'; ch = gh(), cf *= 0.1) x += cf * (ch ^ 48);
	    	t && (x = -x);
	    }
	    inline void pc (char ch) {
	    	#ifdef ONLINE_JUDGE
	    	if (oS == obuf + (1 << 20) + 1) fwrite(obuf, 1, oS - obuf, stdout), oS = obuf; 
	    	*oS++ = ch;
	    	#else
	    	putchar(ch);
	    	#endif
		}
		template<typename _Tp>
	    inline void write (_Tp x) {
	    	static char stk[64], *tp = stk;
	    	if (x < 0) x = ~(x - 1), pc('-');
			do *tp++ = x % 10, x /= 10;
			while (x);
			while (tp != stk) pc((*--tp) | 48);
	    }
	    inline void write (char *s) {
	    	int n = strlen(s);
	    	for (int i = 0; i < n; i++) pc(s[i]);
	    }
	} io;
	inline int read () { return io.read(); }
	template<typename Tp>
	inline void read (Tp &x) { io.read(x); }
	template<typename _Tp>
	inline void write (_Tp x) { io.write(x); }
}
using namespace FastIO;

struct backstk{
	struct nd{int *w,v;}t[N];int tp;
	inline void ins(int& x){t[++tp]=(nd){&x,x};}
	void re(){while(tp) *(t[tp].w)=t[tp].v,tp--;}
}st;
struct backstk2{
	struct nd{ll *w,v;}t[N];int tp;
	inline void ins(ll& x){t[++tp]=(nd){&x,x};}
	void re(){while(tp) *(t[tp].w)=t[tp].v,tp--;}
}sp;
struct Node{int x,y,id;}u[N];
struct node{int l,r,x,id;}qry[N];
inline bool cmp1(Node a,Node b){return a.id<b.id;}
inline bool cmp2(node a,node b){return a.x<b.x;}
int n,m,a[N],id[N],ucnt,qcnt,bk;ll ans[N],sqr[N];bool vis[N];
struct Qry{int op,l,r,x,y,id;}q[N];
vector<int> vec[N];
struct Block{
	int c[N],t[N],f[N];ll s[N];
	void init(){memset(c,0,sizeof(int)*(n+1));memset(t,0,sizeof(int)*(n+1));memset(f,0,sizeof(int)*(n+1));memset(s,0,sizeof(s));}
	void upd(int x,bool fl){
		if(!fl){
			int bx=id[x];c[x]=1;
			if(!c[x-1]&&!c[x+1]) s[bx]+=1,f[x]=t[x]=x;
			else if(!c[x-1]){
				if((x+B)/B==bx) s[bx]+=t[x+1]-x+1,f[t[x+1]]=x,t[x]=t[x+1];
				else s[bx]+=1,f[x]=t[x]=x;
			}
			else if(!c[x+1]){
				if((x+B-2)/B==bx) s[bx]+=x-f[x-1]+1,t[f[x-1]]=x,f[x]=f[x-1];
				else s[bx]+=1,f[x]=t[x]=x;
			}
			else{
				if((x+B)/B!=bx&&(x+B-2)/B!=bx) s[bx]+=1,f[x]=t[x]=x;
				else if((x+B)/B!=bx) s[bx]+=x-f[x-1]+1,t[f[x-1]]=x,f[x]=f[x-1];
				else if((x+B-2)/B!=bx) s[bx]+=t[x+1]-x+1,f[t[x+1]]=x,t[x]=t[x+1];
				else s[bx]+=1ll*(t[x+1]-x+1)*(x-f[x-1]+1),t[f[x-1]]=t[x+1],f[t[x+1]]=f[x-1];
			}
		}
		else{
			int bx=id[x];st.ins(c[x]);sp.ins(s[bx]);c[x]=1;
			if(!c[x-1]&&!c[x+1]) st.ins(f[x]),st.ins(t[x]),s[bx]+=1,f[x]=t[x]=x;
			else if(!c[x-1]){
				if((x+B)/B==bx) st.ins(f[t[x+1]]),st.ins(t[x+1]),s[bx]+=t[x+1]-x+1,f[t[x+1]]=x,t[x]=t[x+1];
				else st.ins(f[x]),st.ins(t[x]),s[bx]+=1,f[x]=t[x]=x;
			}
			else if(!c[x+1]){
				if((x+B-2)/B==bx) st.ins(t[f[x-1]]),st.ins(f[x-1]),s[bx]+=x-f[x-1]+1,t[f[x-1]]=x,f[x]=f[x-1];
				else st.ins(f[x]),st.ins(t[x]),s[bx]+=1,f[x]=t[x]=x;
			}
			else{
				if((x+B)/B!=bx&&(x+B-2)/B!=bx) st.ins(f[x]),st.ins(t[x]),s[bx]+=1,f[x]=t[x]=x;
				else if((x+B)/B!=bx) st.ins(t[f[x-1]]),st.ins(f[x-1]),s[bx]+=x-f[x-1]+1,t[f[x-1]]=x,f[x]=f[x-1];
				else if((x+B-2)/B!=bx) st.ins(f[t[x+1]]),st.ins(t[x+1]),s[bx]+=t[x+1]-x+1,f[t[x+1]]=x,t[x]=t[x+1];
				else{
					st.ins(t[f[x-1]]),st.ins(f[t[x+1]]);
					s[bx]+=1ll*(t[x+1]-x+1)*(x-f[x-1]+1),t[f[x-1]]=t[x+1],f[t[x+1]]=f[x-1];
				}
			}
		}
	}
	ll qry(int l,int r){
		int bl=id[l],br=id[r];
		if(bl==br){
			ll res=0;int g=0;
			for(int i=l;i<=r;i++){
				if(c[i]) g++;
				else res+=sqr[g],g=0;
			}
			res+=sqr[g];return res;
		}
		ll res=0;int g=0;
		for(int i=l;i<=bl*B;i++){
			if(c[i]) g++;
			else res+=sqr[g],g=0;
		}
		for(int i=bl+1;i<br;i++){
			if(c[(i-1)*B+1]){
				if(t[(i-1)*B+1]==i*B){g+=B;continue;}
				res+=s[i]+1ll*g*(g+2*(t[(i-1)*B+1]-(i-1)*B)+1)/2,g=0;
			}
			else res+=s[i]+sqr[g],g=0;
			if(c[i*B]) res-=sqr[i*B-f[i*B]+1],g+=i*B-f[i*B]+1;
		}
		for(int i=(br-1)*B+1;i<=r;i++){
			if(c[i]) g++;
			else res+=sqr[g],g=0;
		}
		res+=sqr[g];
		return res;
	}
}b;
signed main(){
	n=read();m=read();B=sqrt(0.81*n)+1,C=sqrt(17*m)+1;
	for(int i=1;i<=n;i++) a[i]=read(),sqr[i]=1ll*i*(i+1)/2,id[i]=(i+B-1)/B;
	for(int i=1;i<=m;i++){
		q[i].op=read();q[i].id=i;
		if(q[i].op==1) q[i].x=read(),q[i].y=read();
		else q[i].l=read(),q[i].r=read(),q[i].x=read();
	}
	for(;;bk++) if(bk*C>=n) break;
	for(int t=1;t<=bk;t++){
		int l=(t-1)*C+1,r=min(n,t*C);ucnt=qcnt=0;b.init();
		for(int i=l;i<=r;i++){
			if(q[i].op==1) u[++ucnt]=(Node){q[i].x,q[i].y,q[i].id};
			else qry[++qcnt]=(node){q[i].l,q[i].r,q[i].x,q[i].id};
		}
		int tot=ucnt;for(int i=1;i<=tot;i++) u[++ucnt]=(Node){u[i].x,a[u[i].x],0};
		sort(u+1,u+ucnt+1,cmp1);sort(qry+1,qry+qcnt+1,cmp2);
		for(int i=1;i<=ucnt;i++) a[u[i].x]=max(a[u[i].x],u[i].y);
		for(int i=1;i<=n;i++) vec[a[i]].pb(i);
		for(int i=1,j=1;i<=qcnt;i++){
			for(;j<=qry[i].x;j++) for(int p=0;p<vec[j].size();p++) b.upd(vec[j][p],0);
			for(int k=ucnt;k;k--){
				if(u[k].id>qry[i].id) continue;
				if(vis[u[k].x]) continue;vis[u[k].x]=1;
				if(a[u[k].x]>qry[i].x&&u[k].y<=qry[i].x) b.upd(u[k].x,1);
			}
			ans[qry[i].id]=b.qry(qry[i].l,qry[i].r);
			st.re();sp.re();for(int k=ucnt;k;k--) vis[u[k].x]=0;
		}
		for(int i=1;i<=n;i++) vec[a[i]].clear();
		for(int i=1;i<=ucnt;i++) a[u[i].x]=u[i].y;
	}
	for(int i=1;i<=m;i++) if(q[i].op==2) write(ans[i]),io.pc('\n');
	return 0;
}

P8337 [Ynoi2004] rsxc

考虑合法区间的形式,设区间构成的集合为 \(S\)

不难发现需满足 \(span(S)=S,0\in S\)

所以 \(|S|=2^k\) 且其线性基大小为 \(k\)

我们分为两部分求解:

  1. 找出所有右端点对应的合法左端点

通过往线性基逐渐加入元素维护前缀线性基,维护时间戳动态排序。

不难发现一个右端点 \(r\) 对应合法左端点构成 \(O(\log n)\) 个合法左端点区间。

  1. 在线查询

考虑记这些求出的合法区间为 \((l,r,R^{\prime})\) 的一个三元组。

有一个显然的性质:\(k\) 固定时,随着 \(R^{\prime}\) 的增大,\(l,r\) 单调不降,那么可以。

考虑枚举 \(k\),将合法区间按 \(R^{\prime}\) 排序,查询 \([L,R]\)

考虑一种计算答案的方式,对于左端点在查询区间左边的算一类,左端点在查询区间右边的算一类。

我们需要计算出 \(l^{\prime},r^{\prime},R^{\prime\prime}\) 分别表示最前一个三元组满足 \(R^{\prime}\geq L\),最后一个三元组满足 \(l < L\),最后一个三元组满足 \(R^{\prime}\leq R\)

然后可以通过前缀和技巧求解,这是容易做的。

时空复杂度 \(O(n \log V)\),精细卡常可过。

#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,avx2")
//#pragma GCC optimize("Ofast")
//#pragma GCC optimize("inline")
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<set>
#include<queue>
#include<bits/stdc++.h>
#define ll long long
#define pb push_back
//#define int ll
using namespace std;
const int N=6e5+20,M=1e6+20,S=30,V=20;
namespace FastIO {
	struct IO {
	    char ibuf[(1 << 20) + 1], *iS, *iT, obuf[(1 << 20) + 1], *oS;
	    IO() : iS(ibuf), iT(ibuf), oS(obuf) {} ~IO() { fwrite(obuf, 1, oS - obuf, stdout); }
		#if ONLINE_JUDGE
		#define gh() (iS == iT ? iT = (iS = ibuf) + fread(ibuf, 1, (1 << 20) + 1, stdin), (iS == iT ? EOF : *iS++) : *iS++)
		#else
		#define gh() getchar()
		#endif
		inline bool eof (const char &ch) { return ch == ' ' || ch == '\n' || ch == '\r' || ch == 't' || ch == EOF; }
	    inline int read() {
	        char ch = gh();
	        int x = 0;
	        while (ch < '0' || ch > '9') ch = gh();
	        while (ch >= '0' && ch <= '9') x = (x << 1) + (x << 3) + (ch ^ 48), ch = gh();
	        return x;
	    }
	    inline void read (char *s) {
	    	char ch = gh(); int l = 0;
	    	while (eof(ch)) ch = gh();
	    	while (!eof(ch)) s[l++] = ch, ch = gh();
	    }
	    inline void read (double &x) {
	    	char ch = gh(); bool t = 0;
	    	while (ch < '0' || ch > '9') t |= ch == '-', ch = gh();
	    	while (ch >= '0' && ch <= '9') x = x * 10 + (ch ^ 48), ch = gh();
	    	if (ch != '.') return t && (x = -x), void(); ch = gh();
	    	for (double cf = 0.1; '0' <= ch && ch <= '9'; ch = gh(), cf *= 0.1) x += cf * (ch ^ 48);
	    	t && (x = -x);
	    }
	    inline void pc (char ch) {
	    	#ifdef ONLINE_JUDGE
	    	if (oS == obuf + (1 << 20) + 1) fwrite(obuf, 1, oS - obuf, stdout), oS = obuf; 
	    	*oS++ = ch;
	    	#else
	    	putchar(ch);
	    	#endif
		}
		template<typename _Tp>
	    inline void write (_Tp x) {
	    	static char stk[64], *tp = stk;
			do *tp++ = x % 10, x /= 10;
			while (x);
			while (tp != stk) pc((*--tp) | 48);
	    }
	    inline void write (char *s) {
	    	int n = strlen(s);
	    	for (int i = 0; i < n; i++) pc(s[i]);
	    }
	} io;
	inline int read () { return io.read(); }
	template<typename Tp>
	inline void read (Tp &x) { io.read(x); }
	template<typename _Tp>
	inline void write (_Tp x) { io.write(x); }
}
using namespace FastIO;
int b[N],a[N],cnt,n,m;int *rk,sz;
struct PLB{
	int p[S+2],pos[S+2];
	PLB(){memset(a,0,sizeof(a));memset(pos,0,sizeof(pos));}
	void getst(int x,int y){
		int i=0,tp=0;bool fl=1;
		int *vec=new int[sz+(!x)];
		while(i<sz&&fl){
			if(rk[i]==x) i++;
			else if(rk[i]>y) vec[tp++]=rk[i++];
			else vec[tp++]=y,fl=0;
		}
		while(i<sz){
			if(rk[i]==x) i++;
			else vec[tp++]=rk[i++];
		}
		if(fl) vec[tp++]=y;swap(rk,vec);
		if(!x) sz++;
	}
	void ins(int v,int t){
		const int u=t;
		for(int i=S-1;~i;i-=3){
			if((v>>i)&1){
				if(!p[i]){p[i]=v;pos[i]=t;t=0;break;}
				else if(pos[i]<t) swap(pos[i],t),swap(p[i],v);
				v^=p[i];
			}
			int I=i-1;if(!~I) break;
			if((v>>I)&1){
				if(!p[I]){p[I]=v;pos[I]=t;t=0;break;}
				else if(pos[I]<t) swap(pos[I],t),swap(p[I],v);
				v^=p[I];
			}
			I=i-2;if(!~I) break;
			if((v>>I)&1){
				if(!p[I]){p[I]=v;pos[I]=t;t=0;break;}
				else if(pos[I]<t) swap(pos[I],t),swap(p[I],v);
				v^=p[I];
			}
		}
		if(u!=t) getst(t,u);
	}
}B;
int col1[V][N],tot1[V],L[V];
int col2[V][N],tot2[V],R[V];
ll sr[V][N],ss[V][N];int so[V][N],len[V];
int sufr[V][N],prel[V][N],sufl[V][N],preR[V][N];
int lstl[V],lstr[V],lstR[V],lstid[V];bool vis[N];int mx;
inline void Init(){
	int lst0=-1,lstn0=-1;for(int j=0;j<V;j++) L[j]=R[j]=1,tot1[j]=tot2[j]=0;
	for(int i=1;i<=n;i++){
		if(!vis[a[i]]) mx++;vis[a[i]]=1;
		if(!b[a[i]]) lst0=i;else lstn0=i;
		for(int j=0;j<V;j++){
			if(!col1[j][a[i]]++) tot1[j]++;
			if(!col2[j][a[i]]++) tot2[j]++;
			while(tot1[j]>(1<<j)) if(!--col1[j][a[L[j]++]]) tot1[j]--;
			bool fl=0;
			while(tot2[j]>=(1<<j)){if(!--col2[j][a[R[j]++]]) tot2[j]--;fl=1;}
			if(fl) tot2[j]++,R[j]--,col2[j][a[R[j]]]++;
		}
		B.ins(b[a[i]],i);
		if(lst0!=-1){
			const int lim=min(V-1,sz);
			for(int j=1;j<=lim&&(1<<j)<=mx;j++){
				if(tot1[j]>=(1<<j)){
					int l=L[j],r=R[j],pl,pr,gl,gr;
					if(j>=sz) pl=1,pr=rk[j-1];
					else pl=rk[j]+1,pr=rk[j-1];
					pr=min(lst0,pr);if(pl>pr) continue;
					gl=max(l,pl),gr=min(r,pr);if(gl>gr) continue;
					const int u=++len[j];
					sr[j][u]=sr[j][u-1]+gr,so[j][u]=so[j][u-1]+1,ss[j][u]=ss[j][u-1]+(gr-gl+1);
					for(int k=lstr[j]+1;k<=gr;k++) sufr[j][k]=u;
					for(int k=lstl[j]+1;k<=gl;k++) prel[j][k]=lstid[j];
					for(int k=lstl[j]+1;k<=gl;k++) sufl[j][k]=u;
					for(int k=lstR[j];k<i;k++) preR[j][k]=lstid[j];
					lstl[j]=gl,lstr[j]=gr,lstR[j]=i,lstid[j]=u;
				}
			}
		}
		if(!b[a[i]]){
			int gl=lstn0+1,gr=i;const int u=++len[0];
			sr[0][u]=sr[0][u-1]+gr,so[0][u]=so[0][u-1]+1,ss[0][u]=ss[0][u-1]+(gr-gl+1);
			for(int k=lstr[0]+1;k<=gr;k+=3){
				sufr[0][k]=u;
				if(k<gr) sufr[0][k+1]=u;else break;
				if(k+1<gr) sufr[0][k+2]=u;else break;
			}
			for(int k=lstl[0]+1;k<=gl;k+=3){
				prel[0][k]=lstid[0];
				if(k<gl) prel[0][k+1]=lstid[0];else break;
				if(k+1<gl) prel[0][k+2]=lstid[0];else break;
			}
			for(int k=lstl[0]+1;k<=gl;k+=3){
				sufl[0][k]=u;
				if(k<gl) sufl[0][k+1]=u;else break;
				if(k+1<gl) sufl[0][k+2]=u;else break;
			}
			for(int k=lstR[0];k<i;k++){
				preR[0][k]=lstid[0];
				if(k+1<i) preR[0][k+1]=lstid[0];else break;
				if(k+2<i) preR[0][k+2]=lstid[0];else break;
			}
			lstl[0]=gl,lstr[0]=gr,lstR[0]=i,lstid[0]=u;
		}
		if(i==n){
			for(int j=min(V-1,sz);~j;j--) if(tot1[j]>=(1<<j)){
				for(int k=lstl[j]+1;k<=n;k++) prel[j][k]=lstid[j];
				for(int k=lstR[j];k<=n;k++) preR[j][k]=lstid[j];
			}
		}
	}
}
void init(int _n,int _q,const vector<int> &A){
	n=_n,m=_q;
	for(int i=1;i<=n;i++) b[i]=a[i]=A[i-1];
	sort(b+1,b+n+1);cnt=unique(b+1,b+n+1)-b-1;
	for(int i=1;i<=n;i++) a[i]=lower_bound(b+1,b+cnt+1,a[i])-b;
	int l=1,r=cnt,p=0;
	Init();
}
ll solve(int l,int r){
	l++,r++;ll ans=0;int ql,qr,len=r-l+1;
	for(int k=0;k<V&&(1<<k)<=len;k+=3){
		ql=sufr[k][l],qr=min(prel[k][l],preR[k][r]);
		if(ql&&qr&&ql<=qr) ans+=(sr[k][qr]-sr[k][ql-1])+1ll*(so[k][ql-1]-so[k][qr])*l+(so[k][qr]-so[k][ql-1]);
		ql=sufl[k][l],qr=preR[k][r];
		if(ql&&qr&&ql<=qr) ans+=ss[k][qr]-ss[k][ql-1];
		int K=k+1;if((1<<K)>len||K>=V) break;
		ql=sufr[K][l],qr=min(prel[K][l],preR[K][r]);
		if(ql&&qr&&ql<=qr) ans+=(sr[K][qr]-sr[K][ql-1])+1ll*(so[K][ql-1]-so[K][qr])*l+(so[K][qr]-so[K][ql-1]);
		ql=sufl[K][l],qr=preR[K][r];
		if(ql&&qr&&ql<=qr) ans+=ss[K][qr]-ss[K][ql-1];
		K=k+2;if((1<<K)>len||K>=V) break;
		ql=sufr[K][l],qr=min(prel[K][l],preR[K][r]);
		if(ql&&qr&&ql<=qr) ans+=(sr[K][qr]-sr[K][ql-1])+1ll*(so[K][ql-1]-so[K][qr])*l+(so[K][qr]-so[K][ql-1]);
		ql=sufl[K][l],qr=preR[K][r];
		if(ql&&qr&&ql<=qr) ans+=ss[K][qr]-ss[K][ql-1];
	}
	return ans;
}
signed main() {
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0); 
  int n, q;
  uint64_t s;
  cin >> n >> q >> s;
  string r;
  cin >> r;
  __uint128_t brt=((__uint128_t)1<<64)/n;
	auto getmod=[&](uint64_t now){
		now=now-n*(brt*now>>64);
		while(now>=n)now-=n;return now;
	};
  vector<int> a(n);
  for (int i = 0; i < n; i++)
    for (int s = 5 * i; s < 5 * i + 5; s++)
      a[i] = (a[i] * 64 + (int)(r[s]) - (int)('0'));
  init(n, q, a);
  uint64_t state = 0;
  auto splitmix64 = [&](uint64_t v) {
    uint64_t z = (v + 0x9e3779b97f4a7c15);
    z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9;
    z = (z ^ (z >> 27)) * 0x94d049bb133111eb;
    return z ^ (z >> 31);
  };
  mt19937_64 rng(s);
  for (int i = 0; i < q; i++) {
    int l = getmod(rng() ^ state) ;
    int r = getmod(rng() ^ state) ;
    long long ans = solve(min(l, r), max(l, r));
    state = splitmix64(state + ans);
    if (!((i + 1) &((1<<15)-1))) write(state),io.pc('\n');
  }
  write(state);io.pc('\n');
}

P5070 [Ynoi2015] 即便看不到未来

挺经典的维护方法。

考虑扫描线,动态维护右端点\(\leq r\) 的区间所贡献的答案。

\(lst_i\) 表示上一个数 \(i\) 出现的位置。

考虑增加一个右端点 \(r\) 的答案,不难发现左端点 \(\leq lst_{a_r}\) 没有贡献。

考虑将 \([a_r-11,a_r+11]\) 的数 \(lst_i\) 拎出来排序递减更新答案。

使用树状数组维护即可,复杂度 \(O(10 n \log n)\)

#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,avx2")
//#pragma GCC optimize("Ofast")
//#pragma GCC optimize("inline")
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<set>
#include<queue>
#include<bits/stdc++.h>
#define ll long long
#define pb push_back
//#define int ll
using namespace std;
const int N=1e6+20,M=1e6+20;
inline int read(){
	int d=0,f=1;char x=getchar();
	while(x<'0'||x>'9'){if(x=='-'){f=-1;}x=getchar();}
	while(x>='0'&&x<='9'){d=(d<<1)+(d<<3)+(x-48);x=getchar();}
	return d*f;
}
struct Qry{int l,id;};
vector<Qry> q[N];
struct Node{int v,p;};
inline bool operator < (Node a,Node b){return a.p>b.p;}
vector<Node> vec;
int n,m,a[N],lst[N],ans[N][12];
queue<int> del;bool vis[N];
struct Bit{
	int _[N];
	inline void add(int x,int v){for(;x;x-=x&-x) _[x]+=v;}
	inline int q(int x){int res=0;for(;x<=n;x+=x&-x) res+=_[x];return res;}
}s[30];
signed main(){
	n=read();m=read();
	for(int i=1;i<=n;i++) a[i]=read();
	for(int i=1;i<=m;i++){
		int l=read(),r=read();
		q[r].pb((Qry){l,i});
	}
	for(int i=1;i<=n;i++){
		vec.clear();
		for(int j=max(1,a[i]-11);j<=a[i]+11;j++) if(lst[j]>lst[a[i]]) vec.pb((Node){j,lst[j]});
		vec.pb((Node){a[i],lst[a[i]]});sort(vec.begin(),vec.end());vis[a[i]]=1;
		s[1].add(i,1),s[1].add(vec[0].p,-1);
		for(int j=0,L=0,R=0;j<vec.size()-1;j++){
			vis[vec[j].v]=1;del.push(vec[j].v);
			while(vis[a[i]-L-1]) L++;while(vis[a[i]+R+1]) R++;
			if(1<=L&&L<=10) s[L].add(vec[j].p,-1),s[L].add(vec[j+1].p,1);
			if(1<=R&&R<=10) s[R].add(vec[j].p,-1),s[R].add(vec[j+1].p,1);
			if(1<=L+R+1&&L+R+1<=10) s[L+R+1].add(vec[j].p,1),s[L+R+1].add(vec[j+1].p,-1);
		}
		vis[a[i]]=0;while(!del.empty()) vis[del.front()]=0,del.pop();
		for(auto u:q[i]) for(int j=1;j<=10;j++) ans[u.id][j]=s[j].q(u.l);
		lst[a[i]]=i; 
	}
	for(int i=1;i<=m;i++){
		for(int j=1;j<=10;j++) putchar(ans[i][j]%10+48);
		putchar('\n');
	}
	return 0;
}

P5072 [Ynoi2015] 盼君勿忘

莫队,考虑经典结论,\(\sum p_i=n\),本质不同 \(p\) 个数为 \(O(\sqrt{n})\)

动态维护不同 \(p\) ,可用双向链表。

设一个数在区间出现次数为 \(b\),序列长度为 \(a\),则去掉没有包含这个数的子序列,包含这个数的子序列个数为 \(2^a-2^{a-b}\)

维护每个 \(b\) 的数和,由上面所述,本质不同 \(b\)\(O(\sqrt{n})\)

使用快速幂实现则有 \(O(n \sqrt{n} \log n)\) 的做法了。

如果对于每个 \(p\) ,光速幂预处理 \(O(\sqrt{n})\)\(O(1)\) 查询。

则可以做到 \(O(n \sqrt{n})\)

#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,avx2")
//#pragma GCC optimize("Ofast")
//#pragma GCC optimize("inline")
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<set>
#include<queue>
#include<bits/stdc++.h>
#define ll long long
//#define int ll
using namespace std;
const int N=2e5+20,M=1e6+20,B=320;
inline int read(){
	int d=0,f=1;char x=getchar();
	while(x<'0'||x>'9'){if(x=='-'){f=-1;}x=getchar();}
	while(x>='0'&&x<='9'){d=(d<<1)+(d<<3)+(x-48);x=getchar();}
	return d*f;
}
struct Qry{int l,r,p,id;}q[N];
int pre[N],nxt[N],n,m,p,bl[N],a[N],c[N],ans[N],pw1[N],pw2[N];ll s[N];
inline bool cmp(Qry a,Qry b){return bl[a.l]==bl[b.l]?(bl[a.l]&1?a.r<b.r:a.r>b.r):bl[a.l]<bl[b.l];}
inline void Add(int x){nxt[x]=nxt[0];pre[x]=0;if(nxt[0]!=-1) pre[nxt[0]]=x;nxt[0]=x;}
inline void Del(int x){
	if(nxt[x]!=-1) pre[nxt[x]]=pre[x];
	nxt[pre[x]]=nxt[x];nxt[x]=pre[x]=-1;
}
inline void add(int x){
	s[c[x]]-=x;if(!s[c[x]]&&c[x]) Del(c[x]);c[x]++;
	if(!s[c[x]]) Add(c[x]);s[c[x]]+=x;
}
inline void del(int x){
	s[c[x]]-=x;if(!s[c[x]]) Del(c[x]);c[x]--;
	if(!s[c[x]]&&c[x]) Add(c[x]);s[c[x]]+=x;
}
void initp(int _p){
	p=_p;
	pw1[0]=pw2[0]=1;for(int i=1;i<=B;i++) pw1[i]=2*pw1[i-1]%p;
	pw2[1]=pw1[B];for(int i=2;i<=n/B;i++) pw2[i]=1ll*pw1[B]*pw2[i-1]%p;
}
inline int qpow(int x){return 1ll*pw1[x%B]*pw2[x/B]%p;}
signed main(){
//	freopen("a.in","r",stdin);
//	freopen("mine.out","w",stdout);
	n=read();m=read();
	for(int i=1;i<=n;i++) a[i]=read(),nxt[i]=pre[i]=-1;nxt[0]=pre[0]=-1;
	for(int i=1;i<=n;i++) bl[i]=(i+B-1)/B;
	for(int i=1;i<=m;i++) q[i].l=read(),q[i].r=read(),q[i].p=read(),q[i].id=i;
	sort(q+1,q+m+1,cmp);
	for(int i=1,l=1,r=0;i<=m;i++){
		int ql=q[i].l,qr=q[i].r;initp(q[i].p);
		while(l>ql) add(a[--l]);
		while(r<qr) add(a[++r]);
		while(l<ql) del(a[l++]);
		while(r>qr) del(a[r--]);
		int res=0;
		for(int j=nxt[0];j!=-1;j=nxt[j]) res=(res+1ll*(qpow(r-l+1)-qpow(r-l+1-j)+p)%p*(s[j]%p)%p)%p;
		ans[q[i].id]=res;
	}
	for(int i=1;i<=m;i++) printf("%d\n",ans[i]);
	return 0;
}

P6107 [Ynoi2010] Worst Case Top Tree

笛卡尔树。

发现答案为笛卡尔树大小为 \(4\) 连通块个数减去包含根的大小为 \(4\) 的连通块个数。

容易 dp 的。

但是修改操作很烦。

考虑一次修改相当于将 \(x\) 旋上去,通过势能分析,可以发现旋转方向发生改变的次数是 \(O((n+q) \log n)\) 的,对于一次定方向的旋转,可以影响的点和 dp 值是 \(O(1)\) 的,然后每次在线段树上二分找点即可。

复杂度 \(O((n+q) \log ^2 n)\)

应该是为数不多不卡常的 ynoi。

#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,avx2")
//#pragma GCC optimize("Ofast")
//#pragma GCC optimize("inline")
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<set>
#include<queue>
#include<bits/stdc++.h>
#define ll long long
//#define int ll
using namespace std;
const int N=5e5+20,M=1e6+20;
inline int read(){
	int d=0,f=1;char x=getchar();
	while(x<'0'||x>'9'){if(x=='-'){f=-1;}x=getchar();}
	while(x>='0'&&x<='9'){d=(d<<1)+(d<<3)+(x-48);x=getchar();}
	return d*f;
}
ll a[N];int n,m;
struct Seg{
	int M;ll s[N<<2];
	inline void init(int n){
		for(M=1;M<=n+1;M<<=1) ;
		for(int i=1;i<=n;i++) s[i+M]=a[i];
		for(int i=M-1;i;i--) s[i]=max(s[i<<1],s[i<<1|1]);
	}
	inline void upd(int x,int v){
		s[x+M]+=v;for(int i=x+M>>1;i;i>>=1) s[i]=max(s[i<<1],s[i<<1|1]);
	}
	inline int qryl(int x,ll k){
		for(x+=M+1;x>1;x>>=1) if((x&1)&&s[x^1]>=k) break;
		if(x==1) return 0;
		for(x^=1;x<=M;) x=(s[x<<1|1]>=k)?(x<<1|1):(x<<1);
		return x-M;
	}
	inline int qryr(int x,ll k){
		for(x+=M-1;x>1;x>>=1) if((~x&1)&&s[x^1]>=k) break;
		if(x==1) return n+1;
		for(x^=1;x<=M;) x=(s[x<<1]>=k)?(x<<1):(x<<1|1);
		return x-M;
	}
}t;
ll f[N][5],ans=0;int ch[N][2],fa[N],rt;
inline bool cmp(int x,int y){return x<y?a[x]<a[y]:a[x]<=a[y];}
inline void upd(int u){
	if(!u) return ;
	ans-=f[u][4];bool fl=0;int lc=ch[u][0],rc=ch[u][1],t;
	t=f[lc][1]+f[rc][1];fl|=t!=f[u][2],f[u][2]=t;
	t=f[lc][2]+f[lc][1]*f[rc][1]+f[rc][2];fl|=t!=f[u][3],f[u][3]=t;
	t=f[lc][3]+f[lc][2]*f[rc][1]+f[lc][1]*f[rc][2]+f[rc][3];fl|=t!=f[u][4],f[u][4]=t;
	ans+=f[u][4];if(fl) upd(fa[u]);
}
int id[N],stk[N];
void init(){
	for(int i=1,tp=0;i<=n;i++){
		bool fl=0;
		while(tp&&cmp(stk[tp],i)) tp--,fl=1;
		if(tp) ch[stk[tp]][1]=i;
		if(fl) ch[i][0]=stk[tp+1];
		stk[++tp]=i;
	}
	for(int i=1;i<=n;i++) id[i]=fa[ch[i][0]]=fa[ch[i][1]]=i,f[i][1]=1;
	sort(id+1,id+n+1,cmp);rt=id[n];fa[0]=0;
	for(int i=1;i<=n;i++) upd(id[i]);
}
inline void link(int f,int x,int c){ch[f][c]=x,fa[x]=x?f:0;}
signed main(){
	n=read();
	for(int i=1;i<=n;i++) a[i]=read();t.init(n);init();
	m=read();
	for(int i=1;i<=m;i++){
		int x=read(),val=read();a[x]+=val;
		while(x!=rt){
			int f=fa[x];
			if(cmp(x,f)) break;
			if(x==ch[f][0]){
				int u=t.qryl(f-1,a[f]),v=u?ch[u][1]:rt;
				if(cmp(v,x)){
					link(f,ch[x][1],0);link(x,v,1);
					if(u) link(u,x,1);else rt=x,fa[x]=0; 
				}else{
					u=t.qryr(x+1,a[x]+1),v=ch[u][0];
					link(f,ch[x][1],0);link(x,v,1);link(u,x,0);
				}
			}else{
				int u=t.qryr(f+1,a[f]+1),v=u<=n?ch[u][0]:rt;
				if(cmp(v,x)){
					link(f,ch[x][0],1);link(x,v,0);
					if(u<=n) link(u,x,0);else rt=x,fa[x]=0;
				}else{
					u=t.qryl(x-1,a[x]),v=ch[u][1];
					link(f,ch[x][0],1);link(x,v,0);link(u,x,1);
				}
			}
			upd(f);upd(x);upd(fa[x]);
		}
		t.upd(x,val);
		printf("%lld\n",ans-f[rt][4]);
	}
	return 0;
}

P8987 [北大集训 2021] 简单数据结构

不难发现如果没有初值那这题太蠢了。

莫事,大致思路是一样的,本质上就是单调序列很好维护。

不妨被取 \(min\) 更新过的集合设为 \(T\)

不难发现这个集合只增不减,且为单调不降序列。

那么就简单了,我们需要算出每个点的进入 \(T\) 集合时间。

不难发现要求 \(a_i+ci\geq v\),移项 \(a_i \geq v-ci\)

考虑用斜率为 \(-c\) 的线去切由 \((i,a_i)\) 构成的凸包。

不难发现 \(c\) 单调,用个分块凸包加双指针即可。

复杂度 \(O(n \sqrt{n})\)

#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,avx2")
//#pragma GCC optimize("Ofast")
//#pragma GCC optimize("inline")
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<set>
#include<queue>
#include<bits/stdc++.h>
#define ll long long
#define pb push_back
//#define int ll
using namespace std;
const int N=2e5+20,B=422;
const ll Inf=1e14;
inline ll read(){
	ll d=0;char x=getchar();
	while(x<'0'||x>'9') x=getchar();
	while(x>='0'&&x<='9'){d=(d<<1)+(d<<3)+(x-48);x=getchar();}
	return d;
}
int n,m,t;ll a[N];
vector<int> d[N];
struct Block{
	ll y[B+10];
	int x[B+10],l,r,sz,p[B+10];
	inline ll crs(int a,int b,int c){return (x[b]-x[a])*(y[c]-y[b])-(x[c]-x[b])*(y[b]-y[a]);}
	inline ll get(int k,int i){return y[p[i]]-1ll*x[p[i]]*k;}
	void re(){
		l=1,r=0;
		for(int i=1;i<=sz;i++){
			if(y[i]<0) continue;
			while(l<r&&crs(p[r-1],p[r],i)>=0) r--;
			p[++r]=i;
		}
	}
	void solve(int k,int t,ll v){
		while(l<=r){
			while(l<r&&get(k,l)<=get(k,l+1)) l++;
			if(get(k,l)<v) break;
			else d[t].pb(x[p[l]]),y[p[l]]=-Inf,re();
		}
	}
	void init(int bk){
		for(int i=(bk-1)*B+1;i<=min(n,bk*B);i++) x[i-(bk-1)*B]=i,y[i-(bk-1)*B]=a[i];
		sz=min(n,bk*B)-(bk-1)*B;re();
	}
}b[N/B+10];
struct Bit{
	ll s[N];
	inline void add(int x,ll v){for(;x<=n;x+=x&-x) s[x]+=v;}
	inline ll qry(int x){ll res=0;for(;x;x-=x&-x) res+=s[x];return res;}
	inline ll q(int l,int r){return qry(r)-qry(l-1);}
}b1,b2;
struct Lazy{ll val,add;};
struct Node{ll r,vr,sum,c,p;};
inline Lazy operator + (Lazy a,Lazy b){
	if(b.val!=-1) return b;
	return (Lazy){a.val,a.add+b.add};
}
inline Node operator + (Node a,Node b){
	if(!a.r) return b;if(!b.r) return a;
	return (Node){b.r,b.vr,a.sum+b.sum,a.c+b.c,a.p+b.p};
}
inline Node operator + (Node a,Lazy b){
	if(b.val!=-1) a.vr=b.val,a.sum=a.c*b.val;
	if(b.add) a.vr+=a.r*b.add,a.sum+=a.p*b.add;
	return a;
}
struct Seg{
	Lazy t[N<<2];Node s[N<<2];
	inline void ph(int x){s[x]=s[x<<1]+s[x<<1|1];}
	inline void pd(int x){
		if(t[x].add||t[x].val!=-1){
			s[x<<1]=s[x<<1]+t[x],t[x<<1]=t[x<<1]+t[x];
			s[x<<1|1]=s[x<<1|1]+t[x],t[x<<1|1]=t[x<<1|1]+t[x];
			t[x]=(Lazy){-1,0};
		}
	}
	inline void build(int rt,int l,int r){
		t[rt]=(Lazy){-1,0};if(l==r) return ;
		int mid=(l+r)>>1;
		build(rt<<1,l,mid);build(rt<<1|1,mid+1,r); 
	}
	void upd(int rt,int l,int r,int p,ll x){
		if(l==r) return s[rt]=(Node){r,x,x,1,l},void();
		int mid=(l+r)>>1;pd(rt);
		if(p<=mid) upd(rt<<1,l,mid,p,x);
		else upd(rt<<1|1,mid+1,r,p,x);ph(rt);
	}
	void upd(int rt,int l,int r,int ql,int qr,Lazy x){
		if(ql<=l&&r<=qr) return s[rt]=s[rt]+x,t[rt]=t[rt]+x,void();
		int mid=(l+r)>>1;pd(rt);
		if(ql<=mid) upd(rt<<1,l,mid,ql,qr,x);
		if(qr>mid) upd(rt<<1|1,mid+1,r,ql,qr,x);ph(rt);
	}
	int pos(int rt,int l,int r,ll x){
		if(s[rt].vr<x) return -1;
		if(l==r) return l;
		int mid=(l+r)>>1;pd(rt);
		int res=pos(rt<<1,l,mid,x);
		if(res==-1) res=pos(rt<<1|1,mid+1,r,x);
		return res;
	}
	ll qry(int rt,int l,int r,int ql,int qr){
		if(ql<=l&&r<=qr) return s[rt].sum;
		int mid=(l+r)>>1;pd(rt);
		if(qr<=mid) return qry(rt<<1,l,mid,ql,qr);
		if(ql>mid) return qry(rt<<1|1,mid+1,r,ql,qr);
		return qry(rt<<1,l,mid,ql,qr)+qry(rt<<1|1,mid+1,r,ql,qr);
	}
}s;
struct Qry{int op,l,r;ll v;}q[N];
signed main(){
	n=read();m=read();
	for(int i=1;i<=n;i++) a[i]=read();
	int cnt=0;t=(n+B-1)/B;
	for(int i=1;i<=t;i++) b[i].init(i);
	for(int i=1;i<=m;i++){
		q[i].op=read();
		if(q[i].op==1){
			q[i].v=read();
			for(int j=1;j<=t;j++) b[j].solve(-cnt,i,q[i].v);
		}else if(q[i].op==2) cnt++;
		else q[i].l=read(),q[i].r=read();
	}
	cnt=0;s.build(1,1,n);
	for(int i=1;i<=n;i++) b1.add(i,a[i]),b2.add(i,i);
	for(int i=1;i<=m;i++){
		if(q[i].op==1){
			int p=s.pos(1,1,n,q[i].v);
			if(p!=-1) s.upd(1,1,n,p,n,(Lazy){q[i].v,0});
			for(int v:d[i]){
				b1.add(v,-a[v]),b2.add(v,-v);
				s.upd(1,1,n,v,q[i].v);
			}
		}
		else if(q[i].op==2) cnt++,s.upd(1,1,n,1,n,(Lazy){-1,1});
		else printf("%lld\n",s.qry(1,1,n,q[i].l,q[i].r)+b1.q(q[i].l,q[i].r)+b2.q(q[i].l,q[i].r)*cnt);
	}
	return 0;
}

P7811

題解

前言:卡了 \(7\)

感觉没什么直接做法,考虑根号分治。

取模区间 min,显然有 \(O(n^2)\) 的两种暴力,一直是对于每个查询扫一遍,另一种是枚举模数 \(k\) 然后预处理元素后区间查 min。

显然后者拼更有前途,考虑拼 \(k \in [1,\sqrt{a}]\) 的后部分暴力。

那如何处理 \(k > \sqrt{a}\) 的部分,考虑取模的另类形式 \(a \%k=a-\lfloor\dfrac{a}{k}\rfloor k\)

一个显然的事实是 \(\forall j \in [0,\lfloor\dfrac{a}{k}\rfloor]\),有 $ a-jk>a-\lfloor\dfrac{a}{k}\rfloor k$

\(\because k > \sqrt{a}, \therefore \lfloor\dfrac{a}{k}\rfloor \leq \sqrt{a}\)

那么我们就有一个想法了,一次查询相当于对于 \(\forall j \in [0,\lfloor\dfrac{a}{k}\rfloor]\),找到区间 \(\geq jk\) 的最小值。

\(\geq p\) 的限制很烦,值域大到小扫描线,具体的,若当前扫到 \(p\) ,已经将 \(>p\) 的数更新,加入值为 \(p\) 的位置,而此时相当于枚举 \(p\) 的因子 \(k\),更新所有查询模数为 \(k\) 的 min。

但是用什么数据结构维护?

你仔细想想,如果修改复杂度 \(O(S_1)\),查询复杂度 \(O(S_2)\),不难发现一个位置恰好会被加入一次,固修改复杂度为 \(O(S_1 n)\),而一个询问会贡献 \(O(\sqrt{a})\) 次查询,复杂度为 \(O(S_2 q\sqrt{a})\)

显然修改和查询复杂度不是很平衡,考虑用 \(S_1,S_2\) 平衡一下,一个比较显然的 ds 是用 sqrttree 维护一下,单点修改可以做到 \(O( \sqrt{n})\),查询可做 \(O(\log \log \log n)\) 或者 \(O(1)\)

但是常數大,會被卡。

好,那换个小常数的?区间最小值? \(O(S_2q\sqrt{a})\) 显然不能再加 \(\text{polylog}\) 的东西,那要 \(O(1)\) 的话只有 RMQ,用 st 表维护一下,不需要预处理,动态修改 st 表怎么搞?考虑分块,对每个块维护一个 st 表,块与块之间维护 st 表。

我们搞搞修改,先考虑块内 st 表 的修改,显然的事实是只有一个值被更改了,考虑一下 st 表跳 \(2^i\) 的数时,跨过当前被修改的点的区间个数是多少,不难发现是形如 \(\sum\limits_{i\geq 0} 2^{i}\) ,而 \(i\) 的上界为 \(\lfloor \log_{2}{\sqrt{n}} \rfloor\),根据等比数列求和公式不难得出,被影响的区间数为 \(O(\sqrt{n})\) 的,而块与块之间的,因为只有一个块被修改,所以可以类似做 \(O(\sqrt{n})\)

其实这种操作类似 sqrttree 的修改,但是 sqrttree 如果这样修改由于递归层数为 \(O(\log \log n)\) 的,所以复杂度会退化到 \(O(\sqrt n \log \log n)\)

那,查询就正常散块并整块查就好了。

时间复杂度 \(O(n \sqrt{a} +q \sqrt{a})\),空间复杂度 \(O(n)\)

warning,卡常题,建议自行思考实现细节以及自身对代码常数的理解和优化。

posted @ 2023-10-27 15:12  Detect-Perplexity  阅读(20)  评论(0编辑  收藏  举报