CWOI DS 专题 2

怎么又开一个 ds 专题啊/yun

前言:CWOI DS 专题 1

A - 如何正确地排序

以前写的,把以前写的题解贺过来。

正难则反,考虑总贡献减去不会成为 \(\min\)\(\max\) 的数。\(B_i+B_j\) 不会产生贡献的条件就是存在 \(A_i+A_j,C_i+C_j\) 满足 \(\begin{cases}A_i+A_j\le B_i+B_j\\B_i+B_j\le C_i+C_j\end{cases}\),移项可得 \(\begin{cases}A_i-B_i\le B_j-A_j\\B_i-C_i\le C_j-B_j\end{cases}\)。两边的项都只和 \(i,j\) 有关系,可以直接算出来。枚举 \(A,B,C\),这就是一个二维数点。注意相等会算两遍,我们可以钦定行数大的更大。因为有负数,要开两倍空间。

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int inf=1e18,SIZ=2e5;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
	while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
struct BIT{
	int c[400005];
	void clear(){memset(c,0,sizeof(c));}
	void add(int x,int k){
		for(;x<=SIZ<<1;x+=x&-x)c[x]+=k;
	}
	int ask(int x){
		int res=0;for(;x;x-=x&-x)res+=c[x];
		return res;
	}
}Tr;
struct Node{
	int op,x,y,id;
}q[400005];
int cmp(Node x,Node y){
	return (x.x!=y.x)?(x.x<y.x):(x.op<y.op);
}
int n,m,ans,a[5][200005];
void calc(int x,int y,int z){
	Tr.clear();
	for(int i=1;i<=m;i++){
		q[i]=(Node){0,(a[x][i]-a[y][i])+(x>y)+SIZ,(a[y][i]-a[z][i])+(y>z)+SIZ,i};
		q[i+m]=(Node){1,-(a[x][i]-a[y][i])+SIZ,-(a[y][i]-a[z][i])+SIZ,i};
	}
	sort(q+1,q+m*2+1,cmp);
	for(int i=1;i<=m*2;i++){
		if(q[i].op==0)Tr.add(q[i].y,1);
		else ans-=a[y][q[i].id]*Tr.ask(q[i].y);
	}
}
signed main(){
	n=read(),m=read();
	for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)
		a[i][j]=read(),ans+=a[i][j]*2*m;
	for(int i=n+1;i<=4;i++)for(int j=1;j<=m;j++)
		a[i][j]=a[i-n][j],ans+=a[i][j]*2*m;
	n=4;
	for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)for(int k=1;k<=n;k++)
		if(i!=j&&i!=k&&j!=k)calc(i,j,k);
	printf("%lld",ans);
	return 0;
}

B - Tower Defense

默认 \(n,m\) 同阶。考虑沿用 CF453E 的做法,大力分块。注意到一个怪会让前缀若干个块清零,然后修改散块。散块修改可以暴力查,复杂度 \(\mathcal{O}(n\sqrt{n})\)。可以预处理一个块 \(i\) 以清空为初始状态,经过 \(j\) 秒后块内总和,复杂度 \(\mathcal{O}(n\sqrt{n})\)。于是当一个块被清空后,之后的贡献就可以 \(\mathcal{O}(1)\) 计算了。一只怪兽一次最多让一个被清空的块变回需要重新暴力计算贡献的状态,显然总复杂度 \(\mathcal{O}(n\sqrt{n})\)

点击查看代码
#include<bits/stdc++.h>
#define int long long
#ifdef DEBUG
#define msg(args...) fprintf(stderr,args)
#else
#define msg(...) void()
#endif
using namespace std;
const int inf=1e18,T=2e5;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
	while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
int n,m,siz,num,bel[400005],L[1005],R[1005],s[800005];
int a[400005],b[400005],c[400005],t[400005],h[400005];
void sol(int id){
	for(int i=L[id];i<=R[id];i++){
		if(c[i]==0)continue;
		int p=b[i]/c[i];s[1]+=c[i];
		if(p+1<=T)s[p+1]+=b[i]-(p+1)*c[i];
		if(p+2<=T)s[p+2]+=p*c[i]-b[i];
	}
	for(int i=1;i<=T;i++)s[i]+=s[i-1];
	for(int i=1;i<=T;i++)s[i]+=s[i-1];
	int tag=0,lst=0;
	for(int i=1;i<=m;i++){
		if(!h[i])continue;
		if(tag&&h[i]>=s[t[i]-lst]){h[i]-=s[t[i]-lst],lst=t[i];continue;}
		tag=0;for(int j=L[id];j<=R[id];j++)a[j]=min(a[j]+(t[i]-lst)*c[j],b[j]);
		int flag=0;
		for(int j=L[id];j<=R[id];j++){
			if(h[i]>=a[j])h[i]-=a[j],a[j]=0;
			else{flag=1;a[j]-=h[i],h[i]=0;break;}
		}
		if(!flag)tag=1;
		lst=t[i];
		continue;
	}
	for(int i=0;i<=T;i++)s[i]=0;
}
signed main(){
	n=read(),siz=(int)sqrt(n),num=(n+siz-1)/siz;
	for(int i=1;i<=n;i++){
		a[i]=b[i]=read(),c[i]=read();
		bel[i]=(i-1)/siz+1; 
	}
	for(int i=1;i<=num;i++){
		L[i]=(i-1)*siz+1,R[i]=min(i*siz,n);
	}
	m=read();
	for(int i=1;i<=m;i++)t[i]=read(),h[i]=read(); 
	for(int i=1;i<=num;i++)sol(i);
	int ans=0;for(int i=1;i<=m;i++)ans+=h[i];
	printf("%lld\n",ans);
	return 0;
}

C - Treasure

kruskal 重构树。注意到每种颜色的点不超过 10 个,考虑对每种颜色建一颗虚树,虚树上每个点维护子树内最大的点权,需要在原树上进行一个链加单点查,即单点加子树查。复杂度 \(\mathcal{O}(10n\log n)\)

点击查看代码
#pragma GCC optimize("Ofast")
#include<bits/stdc++.h>
#define ll long long
using namespace std;
struct IO {
#define MAXSIZE (1 << 20)
#define isdigit(x) (x >= '0' && x <= '9')
  char buf[MAXSIZE], *p1, *p2;
  char pbuf[MAXSIZE], *pp;
#if DEBUG
#else
  IO() : p1(buf), p2(buf), pp(pbuf) {}

  ~IO() { fwrite(pbuf, 1, pp - pbuf, stdout); }
#endif
  char gc() {
#if DEBUG  // 调试,可显示字符
    return getchar();
#endif
    if (p1 == p2) p2 = (p1 = buf) + fread(buf, 1, MAXSIZE, stdin);
    return p1 == p2 ? ' ' : *p1++;
  }

  bool blank(char ch) {
    return ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t';
  }

  template <class T>
  void read(T &x) {
    double tmp = 1;
    bool sign = 0;
    x = 0;
    char ch = gc();
    for (; !isdigit(ch); ch = gc())
      if (ch == '-') sign = 1;
    for (; isdigit(ch); ch = gc()) x = x * 10 + (ch - '0');
    if (ch == '.')
      for (ch = gc(); isdigit(ch); ch = gc())
        tmp /= 10.0, x += tmp * (ch - '0');
    if (sign) x = -x;
  }

  void read(char *s) {
    char ch = gc();
    for (; blank(ch); ch = gc())
      ;
    for (; !blank(ch); ch = gc()) *s++ = ch;
    *s = 0;
  }

  void read(char &c) {
    for (c = gc(); blank(c); c = gc())
      ;
  }

  void push(const char &c) {
#if DEBUG  // 调试,可显示字符
    putchar(c);
#else
    if (pp - pbuf == MAXSIZE) fwrite(pbuf, 1, MAXSIZE, stdout), pp = pbuf;
    *pp++ = c;
#endif
  }

  template <class T>
  void write(T x) {
    if (x < 0) x = -x, push('-');  // 负数输出
    static T sta[35];
    T top = 0;
    do {
      sta[top++] = x % 10, x /= 10;
    } while (x);
    while (top) push(sta[--top] + '0');
  }

  template <class T>
  void write(T x, char lastChar) {
    write(x), push(lastChar);
  }
} io;
int fa[400005];
int find(int x){
	return ((x==fa[x])?x:fa[x]=find(fa[x]));
}
struct edge{
	int v,nxt;
}e[800005];
int tot,head[400005];
void add(int u,int v){
	e[++tot]=(edge){v,head[u]},head[u]=tot;
}
int cur,pa[23][400005],dep[400005],dfn[400005],siz[400005];
void dfs(int u,int fa){
	dep[u]=dep[fa]+1,siz[u]=1,pa[0][u]=fa,dfn[u]=++cur;
	for(int i=0;i<19;i++)pa[i+1][u]=pa[i][pa[i][u]];
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].v;if(v==fa)continue;
		dfs(v,u);siz[u]+=siz[v];
	}
}
int getlca(int x,int y){
	if(dep[x]>dep[y])swap(x,y);
	for(int i=19;i>=0&&dep[x]!=dep[y];i--)if(dep[y]-dep[x]>=(1<<i))y=pa[i][y];
	if(x==y)return x;
	for(int i=19;i>=0;i--)if(pa[i][x]!=pa[i][y])x=pa[i][x],y=pa[i][y];
	return pa[0][x];
}
struct vtree{
	int len=0;vector<int>p,lc,rc,t;
	void clear(){
		p.clear(),lc.clear(),rc.clear(),t.clear();len=0;
		p.push_back(0),lc.push_back(0),rc.push_back(0),t.push_back(0);
	}
	int get(int u){
		for(int i=1;i<=len;i++)if(p[i]==u)return i;
		len++;p.push_back(u),lc.push_back(0),rc.push_back(0),t.push_back(0);
		return len;
	}
	void add(int u,int v){
		if(dfn[u]>dfn[v])swap(u,v);
		u=get(u),v=get(v);
		if(!lc[u])lc[u]=v;
		else rc[u]=v;
	}
}vt[400005];
struct BIT{
	ll c[400005];
	void clear(){
		for(int i=1;i<=cur;i++)c[i]=0;
	}
	void add(int x,int v){
		for(;x<=cur;x+=x&-x)c[x]+=v;
	}
	ll ask(int x){
		ll res=0;
		for(;x;x-=x&-x)res+=c[x];
		return res;
	}
	ll ask(int l,int r){
		if(l>r)return 0;
		return ask(r)-ask(l-1);
	}
}Tr;
int c[400005],a[400005],st[400005];vector<int>o[400005];
struct Node{
	int u,v,w;
}E[400005];
void solve(){
	int n,m,q;io.read(n),io.read(m),io.read(q);
	int cnt=n;Tr.clear();cur=0;
	for(int i=1;i<=n;i++)o[i].clear(),vt[i].clear();
	tot=0;for(int i=1;i<=n+n;i++)head[i]=0,fa[i]=i;
	for(int i=1;i<=n;i++)io.read(c[i]),o[c[i]].push_back(i);
	for(int i=1;i<=n;i++)io.read(a[i]);
	for(int i=1,u,v,w;i<=m;i++)io.read(u),io.read(v),io.read(w),E[i]=(Node){u,v,w};
	sort(E+1,E+m+1,[](Node x,Node y){return x.w<y.w;});
	for(int i=1;i<=m;i++){
		int u=E[i].u,v=E[i].v,w=E[i].w;if(find(u)==find(v))continue;
		cnt++,a[cnt]=w,add(cnt,find(u)),add(cnt,find(v));
		fa[find(u)]=fa[find(v)]=cnt;
	}
	dfs(cnt,0);
	for(int k=1;k<=n;k++){
		sort(o[k].begin(),o[k].end(),[](int x,int y){return dfn[x]<dfn[y];});
		int top=0;
		for(auto x:o[k]){
			vt[k].get(x);
			int lca=getlca(x,st[top]);
			if(lca!=st[top]){
				while(dfn[lca]<dfn[st[top-1]])vt[k].add(st[top-1],st[top]),top--;
				if(dfn[lca]>dfn[st[top-1]])vt[k].add(lca,st[top]),st[top]=lca;
				else vt[k].add(lca,st[top]),top--;
			}
			st[++top]=x;
		}
		for(int i=1;i<top;i++)vt[k].add(st[i],st[i+1]);
		int len=vt[k].len;vector<int>tmp,p=vt[k].p;
		for(int i=1;i<=len;i++)tmp.push_back(i);
		sort(tmp.begin(),tmp.end(),[&](int x,int y){return dfn[p[x]]>dfn[p[y]];});
		for(int i=0;i<len;i++){
			if(p[tmp[i]]<=n)vt[k].t[tmp[i]]=a[p[tmp[i]]];
			else vt[k].t[tmp[i]]=max(vt[k].t[vt[k].lc[tmp[i]]],vt[k].t[vt[k].rc[tmp[i]]]);
		}
		for(int i=1;i<=len;i++){
			Tr.add(dfn[p[i]],vt[k].t[i]-vt[k].t[vt[k].lc[i]]-vt[k].t[vt[k].rc[i]]);
		}
	}
	while(q--){
		int op,x,y;io.read(op),io.read(x),io.read(y);
		if(op==0){
			int k=c[x],len=vt[k].len;vector<int>tmp,p=vt[k].p;a[x]+=y;
			for(int i=1;i<=len;i++){
				tmp.push_back(i),Tr.add(dfn[p[i]],-vt[k].t[i]+vt[k].t[vt[k].lc[i]]+vt[k].t[vt[k].rc[i]]);
			}
			for(int i=1;i<=len;i++)vt[k].t[i]=0;
			sort(tmp.begin(),tmp.end(),[&](int x,int y){return dfn[p[x]]>dfn[p[y]];});
			for(int i=0;i<len;i++){
				if(p[tmp[i]]<=n)vt[k].t[tmp[i]]=a[p[tmp[i]]];
				else vt[k].t[tmp[i]]=max(vt[k].t[vt[k].lc[tmp[i]]],vt[k].t[vt[k].rc[tmp[i]]]);
			}
			for(int i=1;i<=len;i++){
				Tr.add(dfn[p[i]],vt[k].t[i]-vt[k].t[vt[k].lc[i]]-vt[k].t[vt[k].rc[i]]);
			}
		}
		else{
			for(int i=19;i>=0;i--)if(pa[i][x]&&a[pa[i][x]]<=y)x=pa[i][x];
			io.write(Tr.ask(dfn[x],dfn[x]+siz[x]-1),'\n');
		}
	}
}
signed main(){
	int T;io.read(T);
	while(T--){
		solve();
	} 
	return 0;
}

E - WD与地图

牛的。默认 \(n,m,q\) 同阶。

考虑进行一个时光的倒流,删边变成加边。然而动态维护 dcc 看上去还是很 npc 的样子,想一下有什么办法。注意到如果一条边 \((u,v)\)\(a\) 时刻被加入,且在 \(b\) 时刻时满足 \(u,v\) 在一个 dcc 里(\(b\le a\)),那么我们把所有边按 \(b\) 从大到小排序,这就可以看成一个合并联通块了,就可以线段树合并了,启发式合并一下应该是 \(\mathcal{O}(n\log^2 n)\) 的。

注意到这个 \(b\) 具有二分性捏,于是就可以二分了捏。但是现在有一堆边,所以就要整体二分捏。假设当前状态是 \([l,r,L,R]\),表示编号区间 \([l,r]\),答案取值范围 \([L,R]\),且此时已经加入了 \(a\)\([R,q+1]\) 范围内的边。如果 \(L=R\) 直接确定。否则令 \(L,R\) 中点为 \(mid\),流程如下:

  • 此时前面已经加了一些边了对吧,我们把在一个 dcc 里的点在并查集里合并;

  • 然后遍历 \([l,r]\) 中出现时间大于 \(mid\) 的边 \((u,v)\)link(find(u),find(v))

  • 然后对连了刚刚那些边的点跑 tarjan,再求个 dcc,然后清除刚刚连的边;

  • 把出现时间大于 \(mid\) 且两端点在一个 dcc 里的边划分到 \(mid\) 右边,剩下的丢到左边;

  • 递归左边,撤销并查集合并后递归右边。

为什么刚刚可以直接把边都删了只保留并查集的信息就递归捏,因为如果一条边被分到左边的话待会会再次加的,分到右边的话说明两端点已经在一个 dcc 里了,没用了捏。时间复杂度 \(\mathcal{O}(n\log^2 n)\)

唉,调了一天。

点击查看代码
#include<bits/stdc++.h>
#define int long long
#define fi first
#define se second
#ifdef DEBUG
#define msg(args...) fprintf(stderr,args)
#else
#define msg(...) void()
#endif
using namespace std;
typedef pair<int,int>pii;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
	while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
//dsu
int fa[600005],h[600005];stack<pii>s;
int find(int x){
	return ((x==fa[x])?x:find(fa[x]));
}
void join(int x,int y){
	if(x==y)return;
	if(h[x]<h[y])swap(x,y);
	s.push({y,(h[x]==h[y])}),fa[y]=x,h[x]+=(h[x]==h[y]);
}
void undo(int top){
	while((int)s.size()>top){
		int u=s.top().fi,c=s.top().se;s.pop();
		h[fa[u]]-=c,fa[u]=u;
	}
}
//tarjan
struct edge{
	int v,nxt;
}e[600005];
int tot,head[600005];
void add(int u,int v){
	e[++tot]=(edge){v,head[u]},head[u]=tot;
}
int top,cur,st[600005],dfn[600005],low[600005],in[600005];
void tarjan(int u){
	dfn[u]=low[u]=++cur;st[++top]=u,in[u]=1;
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].v;
		if(!dfn[v])tarjan(v),low[u]=min(low[u],low[v]);
		else if(in[v])low[u]=min(low[u],dfn[v]);
	}
	if(low[u]==dfn[u]){
		int v;
		do{
			v=st[top--];in[v]=0;
			join(find(u),find(v));
		}while(u!=v);
	}
}
//binary
int t[600005],o[600005],op[600005],a[600005],b[600005],id[600005],eu[600005],ev[600005];
void solve(int l,int r,int L,int R){
	if(L==R){
		for(int i=l;i<=r;i++)t[o[i]]=L;
		return;
	}
	int mid=(L+R)>>1;vector<int>pos;
	for(int i=l;i<=r;i++){
		if(o[i]>mid){
			int u=a[o[i]],v=b[o[i]],fu=find(u),fv=find(v);
			pos.push_back(fu);pos.push_back(fv);add(fu,fv);
		}
	}
	int oldtop=(int)s.size();
	for(auto x:pos)if(!dfn[x])tarjan(x);
	tot=cur=top=0;for(auto x:pos)head[x]=dfn[x]=low[x]=in[x]=0;
	vector<int>t1,t2;
	for(int i=l;i<=r;i++){
		if(o[i]>mid&&find(a[o[i]])==find(b[o[i]]))t2.push_back(o[i]);
		else t1.push_back(o[i]);
	}
	int l1=l-1;for(auto x:t1)o[++l1]=x;
	int l2=l1;for(auto x:t2)o[++l2]=x;
	solve(l,l1,L,mid);undo(oldtop);
	solve(l1+1,l2,mid+1,R);
}
//segtree 
int root[600005],B[600005];
struct segtree{
	#define ls(p) (c[p].lc)
	#define rs(p) (c[p].rc)
	#define s(p) (c[p].s)
	#define c(p) (c[p].c)
	#define lson l,mid,ls(p)
	#define rson mid+1,r,rs(p)
	struct Node{
		int lc,rc,s,c;
	}c[40000005];
	int T;
	void up(int p){
		s(p)=s(ls(p))+s(rs(p));
		c(p)=c(ls(p))+c(rs(p));
	}
	int upd(int l,int r,int p,int x,int v){
		if(!p)p=++T;
		if(l==r){c(p)+=v,s(p)+=B[l]*v;return p;}
		int mid=(l+r)>>1;
		if(x<=mid)ls(p)=upd(lson,x,v);
		else rs(p)=upd(rson,x,v);
		up(p);return p;
	}
	int merge(int l,int r,int p,int q){
		if(!p||!q)return p+q;
		if(l==r){s(p)+=s(q),c(p)+=c(q);return p;}
		int mid=(l+r)>>1;
		ls(p)=merge(lson,ls(q));
		rs(p)=merge(rson,rs(q));
		up(p);return p;
	}
	int ask(int l,int r,int p,int k){
		if(l==r)return B[l]*k;
		int mid=(l+r)>>1;
		if(c(rs(p))>=k)return ask(rson,k);
		else return s(rs(p))+ask(lson,k-c(rs(p)));
	}
	#undef ls
	#undef rs
	#undef s
	#undef c
	#undef lson
	#undef rson
}Tr;
//misc
int val[600005],used[600005],w[600005],Fa[600005],siz[600005],ans[600005];map<int,int>mp[600005];
int Find(int x){
	return ((x==Fa[x])?x:Fa[x]=Find(Fa[x]));
}
signed main(){
	int n=read(),m=read(),q=read(),num=0;
	for(int i=1;i<=n;i++)val[i]=read(),fa[i]=i,h[i]=0,B[++num]=val[i];
	for(int i=1;i<=m;i++)eu[i]=read(),ev[i]=read(),mp[eu[i]][ev[i]]=i;
	for(int i=1;i<=q;i++)op[i]=read(),a[i]=read(),b[i]=read();
	for(int i=1;i<=q;i++)if(op[i]==1)used[mp[a[i]][b[i]]]=1,id[i]=mp[a[i]][b[i]];
	for(int i=1;i<=q;i++)if(op[i]==2)val[a[i]]+=b[i],B[++num]=val[a[i]],b[i]=-b[i];
	for(int i=1;i<=m;i++)if(!used[i])op[++q]=1,a[q]=eu[i],b[q]=ev[i],id[q]=i;
	int Q=0;for(int i=1;i<=q;i++)if(op[i]==1)o[++Q]=i;
	solve(1,Q,0,q);
	for(int i=1;i<=q;i++)if(op[i]!=1)t[i]=i;
	for(int i=1;i<=q;i++)w[i]=i;
	sort(w+1,w+q+1,[&](int x,int y){
		if(t[x]^t[y])return t[x]>t[y];
		else return op[x]<op[y];
	});
	sort(B+1,B+num+1);num=unique(B+1,B+num+1)-B-1;
	for(int i=1;i<=n;i++)val[i]=lower_bound(B+1,B+num+1,val[i])-B;
	for(int i=1;i<=n;i++)Fa[i]=i,siz[i]=1,root[i]=Tr.upd(1,num,root[i],val[i],1);
	for(int i=1;i<=q;i++){
		if(op[w[i]]==1){
			int u=a[w[i]],v=b[w[i]],fu=Find(u),fv=Find(v);
			if(fu==fv)continue;
			if(siz[fu]<siz[fv])swap(u,v),swap(fu,fv);
			root[fu]=Tr.merge(1,num,root[fu],root[fv]);
			Fa[fv]=fu,siz[fu]+=siz[fv];
		}
		else if(op[w[i]]==2){
			int u=a[w[i]],fu=Find(u);
			root[fu]=Tr.upd(1,num,root[fu],val[u],-1);
			val[u]=lower_bound(B+1,B+num+1,B[val[u]]+b[w[i]])-B;
			root[fu]=Tr.upd(1,num,root[fu],val[u],1);
		}
		else{
			int u=a[w[i]],fu=Find(u);
			ans[w[i]]=Tr.ask(1,num,root[fu],min(Tr.c[root[fu]].c,b[w[i]]));
		}
	}
	for(int i=1;i<=q;i++)if(op[i]==3)printf("%lld\n",ans[i]);
	return 0;
}

F - 对数据结构的爱

弱化版:CF1172F

数据范围表明应该是 polylog 的做法,考虑线段树。线段树上每个区间 \([l,r]\) 维护 \(c_i\) 表示当伪代码中的 \(result\) 初值为 \(c_i\) 时,在伪代码执行过程中会出现 \(i\)\(-p\) 操作,\(c_i\) 是满足这个的最小值。显然维护这个东西空间复杂度是 \(\mathcal{O}(n\log n)\) 的。

思考怎么合并两个区间,从 \(\{c_{ls(p),x},c_{rs(p),y}\}\) 推到 \(c_{p,x+y}\)。注意到如果满足 \(c_{ls(p),x+1}-1+sum_{ls(p)}-xp\ge c_{rs(p),y}\) 就可以用 \(\max\{c_{ls(p),x},c_{rs(p),y}-sum_{ls(p)}+xp\}\) 来更新 \(c_{p,x+y}\)。于是单次合并复杂度 \(\mathcal{O}(len^2)\)

考虑优化。条件和式子没什么可以动的地方,考虑找单调性。注意到如果 \(\{c_{ls(p),x},c_{rs(p),y}\}\)\(\{c_{ls(p),x-1},c_{rs(p),y+1}\}\) 都可以更新 \(c_{p,x+y}\) 的话,后者应该比前者更优。证明就是因为两个都合法,所以 \(c_{ls(p),x}-1+sum_{ls(p)}-(x-1)p\ge c_{rs(p),y+1}\),即 \(c_{rs(p),y+1}-sum_{ls(p)}+(x-1)p\le c_{ls(p),x}-1\)。同时显然 \(c\) 也有单调性,所以 \(c_{ls(p),x-1}\le c_{ls(p),x}\),所以这一定更优。

(插一嘴:题解都说要证 \(c_{x+1}-c_x\ge p\),虽然这好像是对的,而且也挺显然,但是刚刚的单调性证明完全不需要这个引理捏。)

所以一次合并的时候可以从小到大枚举 \(y\),像双指针一样找最小的合法的 \(x\),于是预处理总复杂度 \(\mathcal{O}(n\log n)\),单次询问就在每个节点上 upper_bound 就行了,复杂度 \(\mathcal{O}(q\log^2 n)\)

代码中查询时传入的值表示当前 \(result\) 的值。

点击查看代码
#include<bits/stdc++.h>
#define int long long
#ifdef DEBUG
#define msg(args...) fprintf(stderr,args)
#else
#define msg(...) void()
#endif
using namespace std;
const int inf=1e18;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
	while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
int n,m,P,a[1000005],s[4000005];vector<int>c[4000005];
#define ls (p<<1)
#define rs (p<<1|1)
#define lson l,mid,ls
#define rson mid+1,r,rs
void pushup(int l,int r,int p){
	int mid=(l+r)>>1;s[p]=s[ls]+s[rs];
	for(int x=0,y=0;x<=mid-l+1;x++){
		while(1){
			if(y>r-mid||c[ls][x+1]-1+s[ls]-x*P<c[rs][y]){y--;break;}
			c[p][x+y]=min(c[p][x+y],max(c[ls][x],c[rs][y]-s[ls]+x*P));y++;
		}
	}
}
void build(int l,int r,int p){
	c[p].resize(r-l+3);
	c[p][0]=-inf;for(int i=1;i<=r-l+2;i++)c[p][i]=inf;
	if(l==r){s[p]=a[l],c[p][1]=P-a[l];return;}
	int mid=(l+r)>>1;
	build(lson);build(rson);
	pushup(l,r,p);
}
int ask(int l,int r,int p,int L,int R,int v){
	if(L<=l&&r<=R){
		int pos=upper_bound(c[p].begin(),c[p].end(),v)-c[p].begin()-1;
		return v+s[p]-pos*P;
	}
	int mid=(l+r)>>1;
	if(R<=mid)return ask(lson,L,R,v);
	if(L>mid)return ask(rson,L,R,v);
	return ask(rson,L,R,ask(lson,L,R,v));
}
signed main(){
	n=read(),m=read(),P=read();
	for(int i=1;i<=n;i++)a[i]=read();
	build(1,n,1);
	while(m--){
		int l=read(),r=read();
		printf("%lld\n",ask(1,n,1,l,r,0));
	}
	return 0;
}

G - Serious Business

sto Caiest_Oier orz

考虑记 \(f_i\) 表示从 \((1,1)\) 走到 \((2,i)\) 的最大价值,\(s_{i,j}\) 表示前缀和,答案就是 \(\max\{f_i+s_{3,n}-s_{3,i-1}\}\)。转移的话考虑枚举最后一个要用的区间 \([l,r,v]\)\(l\le i\le r\),那么有转移式 \(f'_i=\max\{f_i,f_{l-1}+s_{2,i}-s_{2,l-1}-v,\max\limits_{j=l}^i\{s_{1,j}+s_{2,i}-s_{2,j-1}-v\}\}\)

解释一下:前面那个是从第一行下到第二行的拐点不在 \([l,r]\) 内的情况,第二种是在的情况。注意到第一个转移很简单,可以在第二个都转完之后再做,于是看第二个。

提一个 \(s_{2,i}\) 出来,相当于求 \(\max\limits_{j=l}^i\{(s_{1,j}-s_{2,j-1})-v\}\)。不妨把所有区间按右端点从大到小排,同时从大到小枚举 \(i\)。这样 \(i\le r\) 的限制天然满足了,只需要 \(l\le j\le i\)。发现这个形如 \(A(x)+B(y),x\le y\) 的最大值的玩意可以直接线段树维护,于是做完了。复杂度 \(\mathcal{O}(n\log n)\)

点击查看代码
#include<bits/stdc++.h>
#define int long long
#ifdef DEBUG
#define msg(args...) fprintf(stderr,args)
#else
#define msg(...) void()
#endif
using namespace std;
const int inf=1e18;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
	while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
int a[5][500005],s[5][500005];
struct Node{
	int v1,v2,ans;
	Node operator +(const Node &b)const{
		Node c;
		c.v1=max(v1,b.v1);c.v2=max(v2,b.v2);
		c.ans=max({ans,b.ans,v1+b.v2});
		return c;
	}
};
struct segtree{
	#define ls (p<<1)
	#define rs (p<<1|1)
	#define lson l,mid,ls
	#define rson mid+1,r,rs
	Node c[2000005];
	void pushup(int p){
		c[p]=c[ls]+c[rs];
	}
	void build(int l,int r,int p){
		if(l==r){
			c[p]={-inf,s[1][l]-s[2][l-1],-inf};
			return;
		}
		int mid=(l+r)>>1;
		build(lson);build(rson);
		pushup(p);
	}
	void upd(int l,int r,int p,int x,int v){
		if(l==r){
			c[p].v1=max(c[p].v1,v);
			c[p].ans=max(c[p].ans,v+c[p].v2);
			return;
		}
		int mid=(l+r)>>1;
		if(x<=mid)upd(lson,x,v);
		else upd(rson,x,v);
		pushup(p);
	}
	Node ask(int l,int r,int p,int L,int R){
		if(L<=l&&r<=R)return c[p];
		int mid=(l+r)>>1;
		if(R<=mid)return ask(lson,L,R);
		if(L>mid)return ask(rson,L,R);
		return ask(lson,L,R)+ask(rson,L,R);
	}
	#undef lson
	#undef rson
	#undef ls
	#undef rs 
}Tr;
int f[500005],ql[500005],qr[500005],qv[500005];vector<int>vl[500005],vr[500005],v[500005];
signed main(){
	int n=read(),q=read();
	for(int i=1;i<=3;i++)for(int j=1;j<=n;j++)a[i][j]=read(),s[i][j]=s[i][j-1]+a[i][j];
	for(int i=1;i<=q;i++)ql[i]=read(),qr[i]=read(),qv[i]=read();
	for(int i=1;i<=q;i++)vl[ql[i]].push_back(i),vr[qr[i]].push_back(i);
	for(int i=0;i<=n;i++)f[i]=-inf;
	Tr.build(1,n,1);
	for(int i=n;i>=1;i--){
		for(auto x:vr[i])Tr.upd(1,n,1,ql[x],-qv[x]);
		f[i]=Tr.ask(1,n,1,1,i).ans;
	}
	multiset<int>st;
	for(int i=1;i<=n;i++){
		for(auto x:vl[i]){
			st.insert(f[i-1]-s[2][i-1]-qv[x]);
			v[qr[x]].push_back(f[i-1]-s[2][i-1]-qv[x]);
		}
		if(!st.empty())f[i]=max(f[i],(*st.rbegin()));
		f[i]+=s[2][i];
		for(auto x:v[i])st.erase(st.find(x));
	}
	int ans=-inf;
	for(int i=1;i<=n;i++)ans=max(ans,f[i]+s[3][n]-s[3][i-1]);
	printf("%lld\n",ans);
	return 0;
}

I - 命运

容斥等部分分做法是简单的,略过。考虑 \(f_{u,i}\) 表示在以 \(u\) 为根的子树内染色,且子树内尚未满足的限制中上端点深度最深的那个深度是 \(i\) 的方案数,其中 \(i=0\) 表示子树内没有未满足的限制。考虑枚举 \(v\in son(u)\),如果 \((u,v)\) 这条边选了那么限制没有意义,直接 \(f'_{u,i}=f_{u,i}\times\sum\limits_{j=0}^{dep_u}f_{v,j}\);否则 \(f'_{u,\max(i,j)}=\sum\limits_{0\le i,j\le dep_u} f_{u,i}\times f_{v,j}\),即 \(f'_{u,i}=f_{u,i}\sum\limits_{j=0}^{i}f_{v,j}+f_{v,i}\sum\limits_{j=0}^{i-1}f_{u,j}\)

不妨令 \(g_{u,i}=\sum\limits_{j=0}^i f_{u,j}\),转移即为 \(f'_{u,i}=f_{u,i}\times\sum\limits_{j=0}^{dep_u}f_{v,j}+f_{u,i}\sum\limits_{j=0}^{i}f_{v,j}+f_{v,i}\sum\limits_{j=0}^{i-1}f_{u,j}=f_{u,i}(g_{v,dep_u}+g_{v,i})+f_{v,i}g_{u,i-1}\)。答案就是 \(f_{1,0}\),于是我们得到了一个 \(\mathcal{O}(n^2)\) 的做法。

考虑优化。注意到在叶子上时有用的 \(f\) 只有一个,考虑线段树合并,于是复杂度优化到 \(\mathcal{O}(n\log n)\)。感觉还是挺典的啊,主要是这个线段树合并优化 dp 有点抽象了,不过听说这好像也是套路(?)。

点击查看代码
#include<bits/stdc++.h>
#define ll long long
#ifdef DEBUG
#define msg(args...) fprintf(stderr,args)
#else
#define msg(...) void()
#endif
using namespace std;
const int mod=998244353;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
	while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
struct edge{
	int v,nxt;
}e[1000005];
int tot,head[500005];
void add(int u,int v){
	e[++tot]=(edge){v,head[u]},head[u]=tot;
}
int dep[500005],lim[500005];
void dfs1(int u,int fa){
	dep[u]=dep[fa]+1;
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].v;if(v==fa)continue;
		dfs1(v,u);
	}
}
struct segtree{
	#define ls(p) (c[p].lc)
	#define rs(p) (c[p].rc)
	#define s(p) (c[p].s)
	#define m(p) (c[p].m)
	#define lson l,mid,ls(p)
	#define rson mid+1,r,rs(p)
	struct Node{
		int lc,rc,s,m;
	}c[5000005];
	int T;
	int newnode(){
		int p=++T;
		c[p]=(Node){0,0,0,1};
		return p; 
	} 
	void up(int p){
		s(p)=(s(ls(p))+s(rs(p)))%mod;
	}
	void down(int p){
		if(ls(p)){
			s(ls(p))=1ll*s(ls(p))*m(p)%mod;
			m(ls(p))=1ll*m(ls(p))*m(p)%mod;
		}
		if(rs(p)){
			s(rs(p))=1ll*s(rs(p))*m(p)%mod;
			m(rs(p))=1ll*m(rs(p))*m(p)%mod;
		}
		m(p)=1;
	}
	int add(int l,int r,int p,int x,int v){
		if(!p)p=newnode();
		if(l==r){s(p)=(s(p)+v)%mod;return p;}
		int mid=(l+r)>>1;down(p);
		if(x<=mid)ls(p)=add(lson,x,v);
		else rs(p)=add(rson,x,v);
		up(p);return p;
	}
	int merge(int l,int r,int p,int q,int &s1,int &s2){
		if(!p){
			s1=(s1+s(q))%mod;
			s(q)=1ll*s(q)*s2%mod;
			m(q)=1ll*m(q)*s2%mod;
			return q;
		}
		if(!q){
			s2=(s2+s(p))%mod;
			s(p)=1ll*s(p)*s1%mod;
			m(p)=1ll*m(p)*s1%mod;
			return p;
		}
		if(l==r){
			s1=(s1+s(q))%mod;int tmp=s(p);
			s(p)=(1ll*s(p)*s1%mod+1ll*s(q)*s2%mod)%mod;
			s2=(s2+tmp)%mod;
			return p;
		}
		int mid=(l+r)>>1;down(p),down(q);
		ls(p)=merge(lson,ls(q),s1,s2);
		rs(p)=merge(rson,rs(q),s1,s2);
		up(p);return p;
	}
	int ask(int l,int r,int p,int L,int R){
		if(!p)return 0;
		if(L<=l&&r<=R)return s(p);
		int mid=(l+r)>>1,res=0;down(p);
		if(L<=mid)res=(res+ask(lson,L,R))%mod;
		if(R>mid)res=(res+ask(rson,L,R))%mod;
		return res; 
	}
	#undef lson
	#undef rson
	#undef ls
	#undef rs
}Tr;
int n,m,root[500005];
void dfs2(int u,int fa){
	root[u]=Tr.add(0,n,root[u],lim[u],1);
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].v;if(v==fa)continue;
		dfs2(v,u);int s1=Tr.ask(0,n,root[v],0,dep[u]),s2=0;
		root[u]=Tr.merge(0,n,root[u],root[v],s1,s2);
	}
}
signed main(){
	n=read();
	for(int i=1,u,v;i<n;i++)u=read(),v=read(),add(u,v),add(v,u);
	dfs1(1,0);
	m=read();
	for(int i=1,u,v;i<=m;i++)u=read(),v=read(),lim[v]=max(lim[v],dep[u]);
	dfs2(1,0);
	printf("%d\n",Tr.ask(0,n,root[1],0,0));
	return 0;
}

J - 星座 3

sto Caiest_Oier orz

考虑刻画这个星座的形成条件。智慧的 lh 告诉我们,没有星座当且仅当对于每个 \(i\),记 \([l_i,r_i]\) 表示最大的满足 \(\forall j\in[l_i,r_i],a_j\le a_i\) 的区间,在 \([l_i,r_i]\) 内至多有一颗高过 \(a_i\) 的星星。注意到删最少的星星就是要保留最多的星星,考虑对 \(a\) 建笛卡尔树,记 \(f_{u,i}\) 表示 \(u\) 子树内最高的星星高度为 \(i\) 的所有保留方案中最大的权值和。那么 \(f_{u,\max\{i,j\}}=\max\limits_{[i>a_u]+[j>a_u]\le 1}\{f_{ls(u),i}+f_{rs(u),j}\}\)。考虑线段树合并优化这个 dp,复杂度 \(\mathcal{O}(n\log n)\)

太菜了。

点击查看代码
#include<bits/stdc++.h>
#define int long long
#ifdef DEBUG
#define msg(args...) fprintf(stderr,args)
#else
#define msg(...) void()
#endif
using namespace std;
const int inf=1e18;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
	while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
int n,m,a[200005],f[20][200005];
int getmax(int x,int y){
	return ((a[x]>a[y])?x:y);
}
int ask(int l,int r){
	int o=__lg(r-l+1);
	return getmax(f[o][l],f[o][r-(1ll<<o)+1]); 
}
int root[200005];
struct segtree{
	#define ls (c[p].lc)
	#define rs (c[p].rc)
	#define lson l,mid,ls
	#define rson mid+1,r,rs
	struct Node{
		int lc,rc,mx,tg;
	}c[10000005];
	int T;
	int newnode(){
		int p=++T;
		c[p]=(Node){0,0,0,0};
		return p;
	}
	void pushup(int p){
		c[p].mx=0;
		if(ls)c[p].mx=max(c[p].mx,c[ls].mx);
		if(rs)c[p].mx=max(c[p].mx,c[rs].mx);
	}
	void pushdown(int p){
		if(c[p].tg){
			if(ls)c[ls].tg+=c[p].tg,c[ls].mx+=c[p].tg;
			if(rs)c[rs].tg+=c[p].tg,c[rs].mx+=c[p].tg;
			c[p].tg=0;
		}
	}
	int add(int l,int r,int p,int x,int v){
		if(!p)p=newnode();
		if(l==r){c[p].mx=max(c[p].mx,v);return p;}
		int mid=(l+r)>>1;pushdown(p);
		if(x<=mid)ls=add(lson,x,v);
		else rs=add(rson,x,v);
		pushup(p);return p;
	}
	int ask(int l,int r,int p,int L,int R){
		if(!p)return 0;
		if(R<l||L>r||L>R)return 0;
		if(L<=l&&r<=R)return c[p].mx;
		int mid=(l+r)>>1,res=0;pushdown(p);
		if(L<=mid)res=max(res,ask(lson,L,R));
		if(R>mid)res=max(res,ask(rson,L,R));
		return res;
	}
	int merge(int l,int r,int p,int q,int pos,int &v1,int &v2){
		if(!p&&!q)return 0;
		if(!p){
			v2=max(v2,ask(l,r,q,1,pos));
			c[q].tg+=v1,c[q].mx+=v1;
			return q;
		}
		if(!q){
			v1=max(v1,ask(l,r,p,1,pos));
			c[p].tg+=v2,c[p].mx+=v2;
			return p;
		}
		if(l==r){
			if(l<=pos)v1=max(v1,c[p].mx);
			if(l<=pos)v2=max(v2,c[q].mx);
			c[p].mx=max(c[p].mx+v2,c[q].mx+v1);
			return p;
		}
		int mid=(l+r)>>1;pushdown(p),pushdown(q);
		ls=merge(lson,c[q].lc,pos,v1,v2);
		rs=merge(rson,c[q].rc,pos,v1,v2);
		pushup(p);return p;
	}
	#undef lson
	#undef rson
	#undef ls
	#undef rs
}Tr;
int qx[200005],qy[200005],qc[200005],lc[200005],rc[200005];vector<int>t[200005];
int build(int l,int r){
	if(l>r)return 0;
	int u=ask(l,r);
	lc[u]=build(l,u-1);
	rc[u]=build(u+1,r);
	return u;
}
void dfs(int u){
	for(auto x:t[u])root[u]=Tr.add(1,n,root[u],qy[x],qc[x]);
	int v1=0,v2=0;
	if(lc[u])dfs(lc[u]),v1=0,v2=0,root[u]=Tr.merge(1,n,root[u],root[lc[u]],a[u],v1,v2);
	if(rc[u])dfs(rc[u]),v1=0,v2=0,root[u]=Tr.merge(1,n,root[u],root[rc[u]],a[u],v1,v2);
}
signed main(){
	n=read();for(int i=1;i<=n;i++)a[i]=read(),f[0][i]=i;
	for(int j=1;(1ll<<j)<=n;j++){
		for(int i=1;i+(1ll<<j)-1<=n;i++){
			f[j][i]=getmax(f[j-1][i],f[j-1][i+(1ll<<(j-1))]);
		}
	}
	int rt=build(1,n),ans=0;m=read();
	for(int i=1;i<=m;i++)qx[i]=read(),qy[i]=read(),qc[i]=read(),ans+=qc[i],t[qx[i]].push_back(i);
	dfs(rt);printf("%lld\n",ans-Tr.ask(1,n,root[rt],1,n));
	return 0;
}

L - Souvenirs

典,考虑所有 \(i<j,a_i\ge a_j\)\((i,j)\) 的贡献,\(a_i\le a_j\) 的贡献可以令 \(a_i\gets -a_i\) 再做一遍。枚举 \(i\),向左找到第一个 \(j\) 满足 \(a_j\ge a_i\),注意到此时一对 \((k,i),k<j<i\) 可能有用的条件是 \(a_i\le a_k\le\lfloor\dfrac{a_i+a_j}{2}\rfloor\),因为如果更靠近 \(a_j\),那么如果一个询问包含 \((k,i)\),那 \((k,j)\) 也一定可选,且一定比 \((k,i)\) 更优。如果 \(a_k>a_j\) 那更不行了。于是一个位置只会有不超过 \(\log V\) 对有用的答案,找出来之后扫描线即可,复杂度 \(\mathcal{O}(n\log n\log V)\)

点击查看代码
#include<bits/stdc++.h>
#define int long long
#ifdef DEBUG
#define msg(args...) fprintf(stderr,args)
#else
#define msg(...) void()
#endif
using namespace std;
const int inf=1e18,V=1e9;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
	while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
struct segtree{
	#define ls (lc[p])
	#define rs (rc[p])
	#define lson l,mid,ls
	#define rson mid+1,r,rs
	int T,lc[4000005],rc[4000005],mx[4000005];
	void clear(){
		for(int i=0;i<=T;i++)lc[i]=rc[i]=0,mx[i]=-inf;
		T=0;
	}
	void pushup(int p){
		mx[p]=max(mx[ls],mx[rs]);
	}
	void add(int l,int r,int &p,int x,int v){
		if(!p)p=++T,mx[p]=-inf;
		if(l==r){mx[p]=max(mx[p],v);return;}
		int mid=(l+r)>>1;
		if(x<=mid)add(lson,x,v);
		else add(rson,x,v);
		pushup(p);
	}
	int ask(int l,int r,int p,int L,int R){
		if(!p)return -inf;
		if(L<=l&&r<=R)return mx[p];
		int mid=(l+r)>>1,res=-inf;
		if(L<=mid)res=max(res,ask(lson,L,R));
		if(R>mid)res=max(res,ask(rson,L,R));
		return res;
	}
	#undef lson
	#undef rson
	#undef ls
	#undef rs
}Tr;
int a[100005],ql[300005],qr[300005],ans[300005];vector<int>v[100005],t[100005];
signed main(){
	int n=read(),root=0;
	for(int i=1;i<=n;i++)a[i]=read();
	int m=read();
	for(int i=1;i<=m;i++)ql[i]=read(),qr[i]=read(),v[qr[i]].push_back(i),ans[i]=inf;
	Tr.clear();root=0;
	for(int i=1;i<=n;i++){
		int j=V;
		while(a[i]<=j){
			int p=Tr.ask(0,V,root,a[i],j);if(p<=0)break;
			t[i].push_back(p);
			if(a[p]==a[i])break;
			j=(a[i]+a[p])>>1;
		}
		Tr.add(0,V,root,a[i],i);
	}
	Tr.clear();root=0;
	for(int i=1;i<=n;i++){
		for(auto x:t[i])Tr.add(1,n,root,x,a[i]-a[x]);
		for(auto x:v[i])ans[x]=min(ans[x],-Tr.ask(1,n,root,ql[x],qr[x]));
	}
	for(int i=1;i<=n;i++)a[i]=V-a[i],t[i].clear();
	Tr.clear();root=0;
	for(int i=1;i<=n;i++){
		int j=V;
		while(a[i]<=j){
			int p=Tr.ask(0,V,root,a[i],j);if(p<=0)break;
			t[i].push_back(p);
			if(a[p]==a[i])break;
			j=(a[i]+a[p])>>1;
		}
		Tr.add(0,V,root,a[i],i);
	}
	Tr.clear();root=0;
	for(int i=1;i<=n;i++){
		for(auto x:t[i])Tr.add(1,n,root,x,a[i]-a[x]);
		for(auto x:v[i])ans[x]=min(ans[x],-Tr.ask(1,n,root,ql[x],qr[x]));
	}
	for(int i=1;i<=m;i++)printf("%lld\n",ans[i]);
	return 0;
}

M - Matches Are Not a Child's Play

牛的。

注意到只要能维护 up 和 when,这个 compare 就是白送的。考虑手模一下删除的过程,发现如果把值最大的点提成根,每次可以看成删除一条从下到上值递增的链。记每个点 \(i\) 子树内最大的点是 \(v_i\),那么每个点 \(i\) 被删除的时间就是 \(\text{dist}(i,v_i)+\sum\limits_{j=1}^n[p_{v_j}<p_{v_i}]\)。然后可以发现每次修改只会让上一次的根 \(x\) 到现在的根 \(y\) 路径上所有点的 \(p_i\) 变成 \(y\),于是直接吉司机/珂朵莉树维护即可,复杂度 \(\mathcal{O}(n\log^2 n)\),吉司机可能还要多带个 \(\log\) 或加个 \(n\sqrt{n}\)

点击查看代码
#include<bits/stdc++.h>
#define int long long
#ifdef DEBUG
#define msg(args...) fprintf(stderr,args)
#else
#define msg(...) void()
#endif
using namespace std;
const int inf=1e18;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
	while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
struct edge{
	int v,nxt;
}e[400005];
int tot,head[200005];
void add(int u,int v){
	e[++tot]=(edge){v,head[u]},head[u]=tot;
}
int n,m,T,num,a[200005],b[400005],siz[200005],son[200005],pa[200005],dep[200005],val[200005];
void dfs1(int u,int fa){
	siz[u]=1,son[u]=0,pa[u]=fa,dep[u]=dep[fa]+1;val[u]=a[u];
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].v;if(v==fa)continue;
		dfs1(v,u);siz[u]+=siz[v];val[u]=max(val[u],val[v]);
		if(siz[son[u]]<siz[v])son[u]=v;
	}
}
int cur,dfn[200005],rnk[200005],top[200005],f[20][200005];
int getmin(int u,int v){
	return ((dfn[u]<dfn[v])?u:v);
}
void dfs2(int u,int rt){
	dfn[u]=++cur,rnk[cur]=u,top[u]=rt;f[0][cur]=pa[u];
	if(son[u])dfs2(son[u],rt);
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].v;if(v==pa[u]||v==son[u])continue;
		dfs2(v,v);
	}
}
int getlca(int u,int v){
	if(u==v)return u;
	if((u=dfn[u])>(v=dfn[v]))swap(u,v);
	int o=__lg(v-u++);
	return getmin(f[o][u],f[o][v-(1ll<<o)+1]);
}
int dist(int u,int v){
	return dep[u]+dep[v]-2*dep[getlca(u,v)];
}
int bel[400005],bl[1005],br[1005];
struct block{
	int c1[1005],c2[400005];
	void add(int x,int v){
		if(x)c1[bel[x]]+=v,c2[x]+=v;
	}
	int ask(int x){
		if(!x)return 0;
		int res=0;
		for(int i=1;i<=bel[x]-1;i++)res+=c1[i];
		for(int i=bl[bel[x]];i<=x;i++)res+=c2[i];
		return res;
	}
}B;
struct segtree{
	#define ls (p<<1)
	#define rs (p<<1|1)
	#define lson l,mid,ls
	#define rson mid+1,r,rs
	struct Node{
		int mn,s,se,tag;
	}c[1600005];
	void pushup(int p){
		c[p].mn=min(c[ls].mn,c[rs].mn);
		c[p].se=min(c[ls].se,c[rs].se);
		if(c[p].mn!=c[ls].mn)c[p].se=min(c[p].se,c[ls].mn);
		if(c[p].mn!=c[rs].mn)c[p].se=min(c[p].se,c[rs].mn);
		c[p].s=0;
		if(c[p].mn==c[ls].mn)c[p].s+=c[ls].s;
		if(c[p].mn==c[rs].mn)c[p].s+=c[rs].s;
	}
	void pushdown(int p){
		if(c[p].tag>c[ls].mn){
			c[ls].mn=c[p].tag;
			c[ls].tag=c[p].tag;
		}
		if(c[p].tag>c[rs].mn){
			c[rs].mn=c[p].tag;
			c[rs].tag=c[p].tag;
		}
		c[p].tag=-inf;
	}
	void build(int l,int r,int p){
		c[p].tag=-inf;
		if(l==r){
			c[p].mn=val[rnk[l]],c[p].s=1,c[p].se=inf; 
			B.add(val[rnk[l]],1);
			return;
		}
		int mid=(l+r)>>1;
		build(lson);build(rson);
		return;
	}
	void upd(int l,int r,int p,int L,int R,int v){
		if(c[p].mn>=v)return;
		if(L<=l&&r<=R){
			if(c[p].mn<v&&v<c[p].se){
				B.add(c[p].mn,-c[p].s);
				c[p].mn=v,c[p].tag=v;
				B.add(c[p].mn,c[p].s);
				return;
			}
		}
		if(l==r)return;
		int mid=(l+r)>>1;pushdown(p);
		if(L<=mid)upd(lson,L,R,v);
		if(R>mid)upd(rson,L,R,v);
		pushup(p);
	}
	int ask(int l,int r,int p,int x){
		if(l==r)return b[c[p].mn];
		int mid=(l+r)>>1;pushdown(p);
		if(x<=mid)return ask(lson,x);
		else return ask(rson,x);
	}
	#undef lson
	#undef rson
	#undef ls
	#undef rs
}Tr;
void upd(int u,int v,int k){
	while(top[u]!=top[v]){
		if(dep[top[u]]<dep[top[v]])swap(u,v);
		Tr.upd(1,cur,1,dfn[top[u]],dfn[u],k);
		u=pa[top[u]];
	}
	if(dep[u]<dep[v])swap(u,v);
	Tr.upd(1,cur,1,dfn[v],dfn[u],k);
}
int Rank(int u){
	int x=Tr.ask(1,cur,1,dfn[u]);
	return dist(x,u)+1+B.ask(a[x]-1);
}
char op[10];
signed main(){
	n=read(),m=read(),T=(int)sqrt(n+m),num=(n+m+T-1)/T;
	for(int i=1;i<=n+m;i++)bel[i]=(i-1)/T+1;
	for(int i=1;i<=num;i++)bl[i]=(i-1)*T+1,br[i]=min(n+m,i*T);
	for(int i=1,u,v;i<n;i++)u=read(),v=read(),add(u,v),add(v,u);
	int mx=n,rt=n;for(int i=1;i<=n;i++)a[i]=i,b[i]=i;
	dfs1(n,0);dfs2(n,n);Tr.build(1,cur,1);
	for(int j=1;(1ll<<j)<=n;j++){
		for(int i=1;i+(1ll<<j)-1<=n;i++){
			f[j][i]=getmin(f[j-1][i],f[j-1][i+(1ll<<(j-1))]);
		}
	}
	while(m--){
		scanf("%s",op);
		if(op[0]=='u'){
			int u=read();upd(rt,u,mx);
			a[u]=++mx,b[mx]=u,rt=u;upd(rt,rt,mx);
		}
		else if(op[0]=='w'){
			int u=read();
			printf("%lld\n",Rank(u));
		}
		else{
			int u=read(),v=read();
			if(Rank(u)<Rank(v))printf("%lld\n",u);
			else printf("%lld\n",v);
		}
	}
	return 0;
}

O - 列队

啊啊啊啊啊,为什么自己这么蠢啊,被蠢哭了。

这个操作看起来就很平衡树的样子,考虑 fhq 维护。注意到在向前看齐的时候空位一定在最后一列,所以我们不需要对每一列都存进平衡树,只需要每一行和最后一列的状态即可。显然直接存是存不下的,考虑类似珂朵莉树那样存连续的一段,需要的时候再破开。那么后面的操作就很简单了,注意细节即可。复杂度 \(\mathcal{O}(n\log n)\)

点击查看代码
#include<bits/stdc++.h>
#define int long long
#ifdef DEBUG
#define msg(args...) fprintf(stderr,args)
#else
#define msg(...) void()
#endif
using namespace std;
const int inf=1e18;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
	while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
mt19937 rnd(19260817);
int T,root[300005];
#define ls(p) (c[p].lc)
#define rs(p) (c[p].rc)
#define l(p) (c[p].l)
#define r(p) (c[p].r)
#define pri(p) (c[p].pri)
#define siz(p) (c[p].siz)
struct Node{
	int lc,rc,l,r,pri,siz;
}c[1500005];
int newnode(int l,int r){
	c[++T]=(Node){0,0,l,r,rnd(),r-l+1};
	return T;
}
void pushup(int p){
	siz(p)=siz(ls(p))+siz(rs(p))+r(p)-l(p)+1;
}
void split(int p,int siz,int &x,int &y){
	if(!p){x=y=0;return;}
	if(siz(ls(p))+r(p)-l(p)+1<=siz)x=p,split(rs(p),siz-(siz(ls(p))+r(p)-l(p)+1),rs(x),y);
	else y=p,split(ls(p),siz,x,ls(y));
	pushup(p);
}
int merge(int x,int y){
	if(!x||!y)return x+y;
	if(pri(x)>pri(y)){
		rs(x)=merge(rs(x),y);
		return pushup(x),x;
	}
	else{
		ls(y)=merge(x,ls(y));
		return pushup(y),y;
	}
}
int ask(int p){
	if(ls(p))return ask(ls(p));
	return p;
}
int n,m,q;
void sol1(int X,int Y){
	int x,y,z;split(root[X],Y-1,x,z);
	split(z,r(ask(z))-l(ask(z))+1,y,z);
	//注意不是 siz(ask(z)),因为 ask(z)可能还有右儿子 
	int L=l(y),R=r(y),p=L+(Y-siz(x))-1;
	printf("%lld\n",p);
	if(L<=p-1)x=merge(x,newnode(L,p-1));
	if(p+1<=R)x=merge(x,newnode(p+1,R));
	root[X]=merge(x,z);
	split(root[n+1],X-1,x,z);split(z,1,y,z);
	root[X]=merge(root[X],y);
	root[n+1]=merge(merge(x,z),newnode(p,p));
}
void sol2(int X,int Y){
	int x,y,z;
	split(root[n+1],X-1,x,z);split(z,1,y,z);
	printf("%lld\n",l(y));
	root[n+1]=merge(merge(x,z),y);
}
signed main(){
	n=read(),m=read(),q=read();
	for(int i=1;i<=n;i++)root[i]=merge(root[i],newnode((i-1)*m+1,i*m-1));
	for(int i=1;i<=n;i++)root[n+1]=merge(root[n+1],newnode(i*m,i*m));
	while(q--){
		int x=read(),y=read();
		if(y<m)sol1(x,y);
		else sol2(x,y);
	}
	return 0;
}

P - Welcome home, Chtholly

加强版:P4117

考虑分块,那么每块内最大值的和最大为 \(V\sqrt{n}\)。散块操作考虑暴力重构,整块操作考虑并查集。注意到记当块内最大值为 \(t\),当 \(t\le x\) 时操作没有影响;当 \(x<t\le 2x\) 时可以把 \([x+1,t]\) 内的贡献向左合并到 \([1,t-x]\) 上,复杂度 \(\mathcal{O}(t-x)\),此时 \(t\) 最大变为 \(x\);当 \(t>2x\)\(t-x\) 可能非常巨大,考虑把 \([1,x]\) 向右合并到 \([x+1,2x]\) 上,同时给这个块打上一个标记 \(tag\),表示 \(i\) 处存的答案实际是 \(i-tag\) 处的,复杂度 \(\mathcal{O}(x)\),此时 \(t\) 变为 \(t-x\)。注意到上述操作不会让最大值变大,且操作复杂度与最大值变化量有关,所以复杂度摊下来是对的,\(\mathcal{O}(V\sqrt{n})\)

细节有点多,可以参考代码。fa 是并查集,c 是出现次数,mp 是这个值对应的并查集的点,val 是这个点对应的值。

点击查看代码
#include<bits/stdc++.h>
#ifdef DEBUG
#define msg(args...) fprintf(stderr,args)
#else
#define msg(...) void()
#endif
using namespace std;
const int V=1e5+1;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
	while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
int a[1000005],fa[1000005],c[1000005],mp[100005],val[1000005];
int find(int x){
	return ((x==fa[x])?x:fa[x]=find(fa[x]));
}
void merge(int x,int y){
	if(!mp[y])return;
	if(!mp[x]){mp[x]=mp[y],val[mp[x]]=x,mp[y]=0;return;}
	int tx=mp[x],ty=mp[y],fx=find(tx),fy=find(ty);
	if(fx!=fy)fa[fy]=fx,c[fx]+=c[fy],c[fy]=val[fy]=mp[y]=0;
}
int n,m,siz,num,bel[1000005],L[1005],R[1005],op[500005],ql[500005],qr[500005],qx[500005],ans[500005];
void sol(int id){
	int mx=0,tag=0;for(int i=0;i<=V;i++)mp[i]=0;
	for(int i=L[id];i<=R[id];i++){
		if(!mp[a[i]])mp[a[i]]=i,fa[i]=i;
		else fa[i]=mp[a[i]];
		val[i]=a[i],c[mp[a[i]]]++,mx=max(mx,a[i]);
	}
	for(int i=1;i<=m;i++){
		if(qr[i]<L[id]||ql[i]>R[id])continue;
		int l=max(ql[i],L[id]),r=min(qr[i],R[id]),x=qx[i];
		if(l==L[id]&&r==R[id]){
			if(op[i]==1){
				if(mx-tag>=2*x){
					for(int j=tag+1;j<=tag+x;j++)if(j+x<=V)merge(j+x,j);
					tag+=x;
				}
				else{
					for(int j=tag+x+1;j<=mx;j++)if(j-x>=0&&j<=V)merge(j-x,j);
					mx=min(mx,x+tag);
				}
			}
			else if(x+tag<=V&&mp[x+tag])ans[i]+=c[mp[x+tag]];
		}
		else{
			if(op[i]==1){
				for(int j=L[id];j<=R[id];j++)a[j]=val[find(j)]-tag;
				for(int j=L[id];j<=R[id];j++)mp[a[j]+tag]=fa[j]=c[j]=val[j]=0;
				mx=tag=0;for(int j=l;j<=r;j++)if(a[j]>x)a[j]-=x;
				for(int j=L[id];j<=R[id];j++){
					if(!mp[a[j]])mp[a[j]]=j,fa[j]=j;
					else fa[j]=mp[a[j]];
					val[j]=a[j],c[mp[a[j]]]++,mx=max(mx,a[j]);
				}
			}
			else{for(int j=l;j<=r;j++)if(val[find(j)]==x+tag)ans[i]++;}
		}
	}
}
signed main(){
	n=read(),m=read(),siz=(int)sqrt(n),num=(n+siz-1)/siz;
	for(int i=1;i<=n;i++)a[i]=read();
	for(int i=1;i<=n;i++)bel[i]=(i-1)/siz+1;
	for(int i=1;i<=num;i++)L[i]=(i-1)*siz+1,R[i]=min(i*siz,n);
	for(int i=1;i<=m;i++)op[i]=read(),ql[i]=read(),qr[i]=read(),qx[i]=read();
	for(int i=1;i<=num;i++)sol(i);
	for(int i=1;i<=m;i++)if(op[i]==2)printf("%d\n",ans[i]);
	return 0;
}

Q - Yuno loves sqrt technology II

二次离线,注意到有 \(\mathcal{O}(n\sqrt{n})\) 次求和而只有 \(\mathcal{O}(n)\) 次单点加,考虑值域分块平衡复杂度,复杂度 \(\mathcal{O}(n(\sqrt{n}+\sqrt{V}))\)

点击查看代码
#include<bits/stdc++.h>
#define ll long long
#ifdef DEBUG
#define msg(args...) fprintf(stderr,args)
#else
#define msg(...) void()
#endif
using namespace std;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
	while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
struct Que{
	int l,r,id;
}q[100005];
int n,m,tot,siz,num,b[100005],a[100005],bel[100005],L[505],R[505];ll p[100005],s[100005],ans[100005],Ans[100005];
struct Info{
	int l,r,id,val;
};
vector<Info>v1[100005],v2[100005];
struct Block{
	int c1[100005],c2[505];
	void clear(){
		for(int i=1;i<=n;i++)c1[i]=0;
		for(int i=1;i<=num;i++)c2[i]=0;
	}
	void add(int x,int v){
		for(int i=x;i<=R[bel[x]];i++)c1[i]+=v;
		for(int i=bel[x];i<=num;i++)c2[i]+=v;
	}
	int ask(int x){
		return c2[bel[x]-1]+c1[x];
	}
	int ask(int l,int r){
		if(l>r)return 0;
		return ask(r)-ask(l-1);
	}
}B;
signed main(){
	n=read(),m=read(),siz=(int)sqrt(n),num=(n+siz-1)/siz;
	for(int i=1;i<=n;i++)bel[i]=(i-1)/siz+1;
	for(int i=1;i<=num;i++)L[i]=(i-1)*siz+1,R[i]=min(i*siz,n);
	for(int i=1;i<=n;i++)a[i]=b[++tot]=read();
	sort(b+1,b+tot+1);tot=unique(b+1,b+tot+1)-b-1;
	for(int i=1;i<=n;i++)a[i]=lower_bound(b+1,b+tot+1,a[i])-b;
	for(int i=1;i<=m;i++)q[i].l=read(),q[i].r=read(),q[i].id=i;
	sort(q+1,q+m+1,[](Que x,Que y){return (bel[x.l]<bel[y.l]||(bel[x.l]==bel[y.l]&&x.r<y.r));});
	B.clear();
	for(int i=1;i<=n;i++){
		p[i]=p[i-1]+B.ask(a[i]+1,tot);
		B.add(a[i],1);
	}
	B.clear();
	for(int i=n;i>=1;i--){
		s[i]=s[i+1]+B.ask(1,a[i]-1);
		B.add(a[i],1);
	}
	for(int i=1,L=1,R=0;i<=m;i++){
		int ql=q[i].l,qr=q[i].r;
		if(ql<L)ans[i]+=s[ql]-s[L],v1[R].push_back({ql,L-1,i,-1}),L=ql;
		if(qr>R)ans[i]+=p[qr]-p[R],v2[L].push_back({R+1,qr,i,-1}),R=qr;
		if(ql>L)ans[i]-=s[L]-s[ql],v1[R].push_back({L,ql-1,i,1}),L=ql;
		if(qr<R)ans[i]-=p[R]-p[qr],v2[L].push_back({qr+1,R,i,1}),R=qr;		
	}
	B.clear();
	for(int i=n;i>=1;i--){
		for(auto x:v1[i])for(int j=x.l;j<=x.r;j++)ans[x.id]+=x.val*B.ask(1,a[j]-1);
		B.add(a[i],1);
	}
	B.clear();
	for(int i=1;i<=n;i++){
		for(auto x:v2[i])for(int j=x.l;j<=x.r;j++)ans[x.id]+=x.val*B.ask(a[j]+1,tot);
		B.add(a[i],1);
	}
	for(int i=1;i<=m;i++)ans[i]+=ans[i-1];
	for(int i=1;i<=m;i++)Ans[q[i].id]=ans[i];
	for(int i=1;i<=m;i++)printf("%lld\n",Ans[i]);
	return 0;
}

R - 莫队二次离线(第十四分块(前体))

根号科技,很神奇吧。

考虑朴素莫队,发现单次加入/删除的复杂度为 \(\mathcal{O}(\dbinom{14}{k})\),显然不能接受。

考虑一个贡献的拆。假如当前区间为 \([l,r]\),现在需要把右端点拓展到 \(R\)。记 \(f(i,l,r)\) 表示 \(\sum\limits_{j=l}^r[\text{popcount}(a_i\oplus a_j)=k]\),那么新增加的答案就是 \(\sum\limits_{i=r+1}^R f(i,l,i-1)\)注意到 f 具有可减性,可以写成 \(f(i,1,i-1)-f(i,1,l-1)\)。前面的可以预处理,而后面的右端点都是 \(l-1\),可以离线下来扫描线。其余情况类似。空间复杂度 \(\mathcal{O}(n+V)\),时间复杂度 \(\mathcal{O}(n\dbinom{14}{k}+n\sqrt{n})\),因为莫队端点移动次数是 \(n\sqrt{n}\) 的。

总结一下,二次离线需要的条件是可以离线(好像是废话),且维护信息具有可减性。如果加入和查询的复杂度分别为 \(\mathcal{O}(k_1)\)\(\mathcal{O}(k_2)\),二离可以将其从 \(\mathcal{O}(n(k_1+k_2)\sqrt{n})\) 优化到 \(\mathcal{O}(nk_1+nk_2\sqrt{n})\)

点击查看代码
#include<bits/stdc++.h>
#define int long long
#ifdef DEBUG
#define msg(args...) fprintf(stderr,args)
#else
#define msg(...) void()
#endif
using namespace std;
const int inf=1e18,V=16384;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
	while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
struct Que{
	int l,r,id;
}q[100005];
int a[100005],bel[100005],p[100005],s[100005],b[20005],ans[100005],Ans[100005];
struct Info{
	int l,r,id,val;
};
vector<Info>v1[100005],v2[100005];
signed main(){
	int n=read(),m=read(),k=read();
	if(k>14){
		while(m--)puts("0");
		return 0;
	}
	int siz=(int)sqrt(n);vector<int>t;
	for(int i=1;i<=n;i++)bel[i]=(i-1)/siz+1;
	for(int i=0;i<V;i++)if(__builtin_popcountll(i)==k)t.push_back(i);
	for(int i=1;i<=n;i++)a[i]=read();
	for(int i=1;i<=m;i++)q[i].l=read(),q[i].r=read(),q[i].id=i;
	sort(q+1,q+m+1,[](Que x,Que y){return (bel[x.l]<bel[y.l]||(bel[x.l]==bel[y.l]&&x.r<y.r));});
	for(int i=0;i<V;i++)b[i]=0;
	for(int i=1;i<=n;i++){
		p[i]=p[i-1]+b[a[i]];
		for(auto x:t)b[a[i]^x]++;
	}
	for(int i=0;i<V;i++)b[i]=0;
	for(int i=n;i>=1;i--){
		s[i]=s[i+1]+b[a[i]];
		for(auto x:t)b[a[i]^x]++;
	}
	for(int i=1,L=1,R=0;i<=m;i++){
		int ql=q[i].l,qr=q[i].r;
		if(ql<L)ans[i]+=s[ql]-s[L],v1[R].push_back({ql,L-1,i,-1}),L=ql;
		if(qr>R)ans[i]+=p[qr]-p[R],v2[L].push_back({R+1,qr,i,-1}),R=qr;
		if(ql>L)ans[i]-=s[L]-s[ql],v1[R].push_back({L,ql-1,i,1}),L=ql;
		if(qr<R)ans[i]-=p[R]-p[qr],v2[L].push_back({qr+1,R,i,1}),R=qr;		
	}
	for(int i=0;i<V;i++)b[i]=0;
	for(int i=n;i>=1;i--){
		for(auto x:v1[i])for(int j=x.l;j<=x.r;j++)ans[x.id]+=x.val*b[a[j]];
		for(auto x:t)b[a[i]^x]++;
	}
	for(int i=0;i<V;i++)b[i]=0;
	for(int i=1;i<=n;i++){
		for(auto x:v2[i])for(int j=x.l;j<=x.r;j++)ans[x.id]+=x.val*b[a[j]];
		for(auto x:t)b[a[i]^x]++;
	}
	for(int i=1;i<=m;i++)ans[i]+=ans[i-1];
	for(int i=1;i<=m;i++)Ans[q[i].id]=ans[i];
	for(int i=1;i<=m;i++)printf("%lld\n",Ans[i]);
	return 0;
}

S - 来者不拒,去者不追

题目所求即为对每个数的排名乘值求和,加入一个数 \(a_i\) 时只需要求 \(\sum_{j}[a_j<a_i]\)\(\sum_{j}[a_j>a_i]a_j\),随便维护一下即可。复杂度 \(\mathcal{O}(n(\sqrt{n}+\sqrt{V}))\)

点击查看代码
#include<bits/stdc++.h>
#define int long long
#ifdef DEBUG
#define msg(args...) fprintf(stderr,args)
#else
#define msg(...) void()
#endif
using namespace std;
const int V=100000;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
	while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
struct Que{
	int l,r,id;
}q[500005];
int n,m,siz,num,a[500005],bel[500005],L[1005],R[1005],p[500005],s[500005],ans[500005],Ans[500005];
struct Info{
	int l,r,id,val;
};
vector<Info>v1[500005],v2[500005];
struct Block{
	int c1[500005],c2[1005];
	void clear(){
		for(int i=1;i<=V;i++)c1[i]=0;
		for(int i=1;i<=num;i++)c2[i]=0;
	}
	void add(int x,int v){
		for(int i=x;i<=R[bel[x]];i++)c1[i]+=v;
		for(int i=bel[x];i<=num;i++)c2[i]+=v;
	}
	int ask(int x){
		return c2[bel[x]-1]+c1[x];
	}
	int ask(int l,int r){
		if(l>r)return 0;
		return ask(r)-ask(l-1);
	}
}B;
signed main(){
	n=read(),m=read(),siz=(int)sqrt(n);
	for(int i=1;i<=n;i++)bel[i]=(i-1)/siz+1;
	for(int i=1;i<=n;i++)a[i]=read();
	for(int i=1;i<=m;i++)q[i].l=read(),q[i].r=read(),q[i].id=i;
	sort(q+1,q+m+1,[](Que x,Que y){return (bel[x.l]<bel[y.l]||(bel[x.l]==bel[y.l]&&x.r<y.r));});
	siz=(int)sqrt(V),num=(V+siz-1)/siz;
	for(int i=1;i<=V;i++)bel[i]=(i-1)/siz+1;
	for(int i=1;i<=num;i++)L[i]=(i-1)*siz+1,R[i]=min(i*siz,V);
	B.clear();
	for(int i=1;i<=n;i++){
		p[i]=p[i-1]+B.ask(1,a[i]-1)*a[i];
		B.add(a[i],1);
	}
	B.clear();
	for(int i=n;i>=1;i--){
		s[i]=s[i+1]+B.ask(1,a[i]-1)*a[i];
		B.add(a[i],1);
	}
	for(int i=1,L=1,R=0;i<=m;i++){
		int ql=q[i].l,qr=q[i].r;
		if(ql<L)ans[i]+=s[ql]-s[L],v1[R].push_back({ql,L-1,i,-1}),L=ql;
		if(qr>R)ans[i]+=p[qr]-p[R],v2[L].push_back({R+1,qr,i,-1}),R=qr;
		if(ql>L)ans[i]-=s[L]-s[ql],v1[R].push_back({L,ql-1,i,1}),L=ql;
		if(qr<R)ans[i]-=p[R]-p[qr],v2[L].push_back({qr+1,R,i,1}),R=qr;		
	}
	B.clear();
	for(int i=n;i>=1;i--){
		for(auto x:v1[i])for(int j=x.l;j<=x.r;j++)ans[x.id]+=x.val*B.ask(1,a[j]-1)*a[j];
		B.add(a[i],1);v1[i].clear();
	}
	B.clear();
	for(int i=1;i<=n;i++){
		for(auto x:v2[i])for(int j=x.l;j<=x.r;j++)ans[x.id]+=x.val*B.ask(1,a[j]-1)*a[j];
		B.add(a[i],1);v2[i].clear();
	}
	B.clear();
	for(int i=1;i<=n;i++){
		p[i]=p[i-1]+B.ask(a[i]+1,V);
		B.add(a[i],a[i]);
	}
	B.clear();
	for(int i=n;i>=1;i--){
		s[i]=s[i+1]+B.ask(a[i]+1,V);
		B.add(a[i],a[i]);
	}
	for(int i=1,L=1,R=0;i<=m;i++){
		int ql=q[i].l,qr=q[i].r;
		if(ql<L)ans[i]+=s[ql]-s[L],v1[R].push_back({ql,L-1,i,-1}),L=ql;
		if(qr>R)ans[i]+=p[qr]-p[R],v2[L].push_back({R+1,qr,i,-1}),R=qr;
		if(ql>L)ans[i]-=s[L]-s[ql],v1[R].push_back({L,ql-1,i,1}),L=ql;
		if(qr<R)ans[i]-=p[R]-p[qr],v2[L].push_back({qr+1,R,i,1}),R=qr;		
	}
	B.clear();
	for(int i=n;i>=1;i--){
		for(auto x:v1[i])for(int j=x.l;j<=x.r;j++)ans[x.id]+=x.val*B.ask(a[j]+1,V);
		B.add(a[i],a[i]);v1[i].clear();
	}
	B.clear();
	for(int i=1;i<=n;i++){
		for(auto x:v2[i])for(int j=x.l;j<=x.r;j++)ans[x.id]+=x.val*B.ask(a[j]+1,V);
		B.add(a[i],a[i]);v2[i].clear();
	}
	for(int i=1;i<=m;i++)ans[i]+=ans[i-1];
	p[0]=0;for(int i=1;i<=n;i++)p[i]=p[i-1]+a[i];
	for(int i=1;i<=m;i++)ans[i]+=p[q[i].r]-p[q[i].l-1];
	for(int i=1;i<=m;i++)Ans[q[i].id]=ans[i];
	for(int i=1;i<=m;i++)printf("%lld\n",Ans[i]);
	return 0;
}

U - 成都七中

感觉这个题很好啊!有一种难但也很简单的感觉!

考虑先把询问离线下来挂到对应的点上。一个点 \(y\)\(x\) 所在连通块内是一个比较抽象的概念,其实就是 \(x\)\(y\) 路径上所有点都在 \([l,r]\) 内。有关路径信息的问题,考虑点分治。

但是问题在于在处理多个分治中心时,同一个点可能会得到多个不同的答案,怎么办捏。注意到一定存在一个分治中心满足 \(x\) 所在的整个连通块都在其子树内,因为连通块中任意两点的路径上都会在一个分治中心处统计到,考虑这些点在点分树上的 lca,那么它的子树内一定包含整个连通块。同时显然这个点一定是最浅的那个,所以我们对每个询问只用求一遍答案,之后不管就行了。

接下来问题变成求所有 \(L\le l_i,r_i\le R\) 的点的 \(c_i\) 的种类数。鉴定为典,排序后记录每种颜色的最后出现位置,bit 区间求和即可。复杂度 \(\mathcal{O}((n+q)\log^2 n)\)。感觉一步一步来说难也不难啊,主要还是要想清楚。

点击查看代码
#include<bits/stdc++.h>
#define int long long
#ifdef DEBUG
#define msg(args...) fprintf(stderr,args)
#else
#define msg(...) void()
#endif
using namespace std;
const int inf=1e18;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
	while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
struct edge{
	int v,nxt;
}e[200005];
int tot,head[100005];
void add(int u,int v){
	e[++tot]=(edge){v,head[u]},head[u]=tot;
}
int siz[100005],vis[100005];
int ql[100005],qr[100005],qx[100005];vector<int>q[100005];
void dfssiz(int u,int fa){
	siz[u]=1+(int)q[u].size();
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].v;if(v==fa||vis[v])continue;
		dfssiz(v,u);siz[u]+=siz[v];
	}
}
int root,mx[100005];
void dfsroot(int u,int fa,int sum){
	mx[u]=-inf;
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].v;if(v==fa||vis[v])continue;
		dfsroot(v,u,sum);mx[u]=max(mx[u],siz[v]);
	}
	mx[u]=max(mx[u],sum-siz[u]);
	if(mx[root]>mx[u])root=u;
}
int n,m,c1,c2,L[100005],R[100005],a1[100005],a2[100005],lst[100005];
void dfsdis(int u,int fa){
	a1[++c1]=u;
	for(auto x:q[u])if(ql[x]<=L[u]&&R[u]<=qr[x])a2[++c2]=x;
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].v;if(v==fa||vis[v])continue;
		L[v]=min(L[u],v),R[v]=max(R[u],v);dfsdis(v,u);
	}
}
struct BIT{
	int c[100005];
	void clear(){
		for(int i=1;i<=n;i++)c[i]=0;
	}
	void add(int x,int v){
		for(;x<=n;x+=x&-x)c[x]+=v;
	}
	int ask(int x){
		int res=0;
		for(;x;x-=x&-x)res+=c[x];
		return res;
	}
	int ask(int l,int r){
		if(l>r)return 0;
		return ask(r)-ask(l-1);
	}
}Tr;
int c[100005],ans[100005];
void calc(int u){
	c1=c2=0;L[u]=R[u]=u;dfsdis(u,0);
	sort(a1+1,a1+c1+1,[](int x,int y){return L[x]>L[y];});
	sort(a2+1,a2+c2+1,[](int x,int y){return ql[x]>ql[y];});
	for(int i=1,j=1;j<=c2;j++){
		while(i<=c1&&ql[a2[j]]<=L[a1[i]]){
			if(lst[c[a1[i]]]<inf)Tr.add(lst[c[a1[i]]],-1);
			lst[c[a1[i]]]=min(lst[c[a1[i]]],R[a1[i]]);
			Tr.add(lst[c[a1[i]]],1);i++;
		}
		ans[a2[j]]=max(ans[a2[j]],Tr.ask(ql[a2[j]],qr[a2[j]]));
	}
	for(int i=1;i<=c1;i++)if(lst[c[a1[i]]]<inf)Tr.add(lst[c[a1[i]]],-1),lst[c[a1[i]]]=inf;
}
void sol(int u){
	vis[u]=1,calc(u);
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].v;if(vis[v])continue;
		root=0,dfssiz(v,0),dfsroot(v,0,siz[v]),sol(root);
	}
}
signed main(){
	n=read(),m=read();for(int i=1;i<=n;i++)c[i]=read(),lst[c[i]]=inf;
	for(int i=1,u,v;i<n;i++)u=read(),v=read(),add(u,v),add(v,u);
	for(int i=1;i<=m;i++)ql[i]=read(),qr[i]=read(),qx[i]=read(),q[qx[i]].push_back(i);
	mx[0]=inf,root=0,dfssiz(1,0),dfsroot(1,0,siz[1]),sol(root);
	for(int i=1;i<=m;i++)printf("%lld\n",ans[i]);
	return 0;
}

V - 大学

弱化版:P3987 我永远喜欢珂朵莉~

注意到当 \(x>1\) 时,每个数最多被操作 \(\log V\) 次就会变成 1,考虑如何快速找到区间内需要除的位置。平衡树是可行的,然而常数太大被 lxl 干掉了,令人感叹。

考虑预处理,开一堆 vector,每次在对应的里面二分找到第一个位置,然后往后跳。注意到除了 \(x\) 之后这个位置可能在很多个 vector 里面都需要删掉,考虑懒删除。类似链表一样维护一个点后面第一个 \(x\) 的倍数,均摊复杂度正确。单点加区间求和可以用树状数组维护。复杂度 \(\mathcal{O}(V\ln V+n\log n\max\limits_{i=1}^V\{d(i)\})\)。常数小。

然而这样还是不太能过,考虑卡常。比如 c++98,树状数组,stable_sort,调和级数预处理因子,还有快读

funfact:妈的,傻逼 oiwiki 的快读慢得跟狗屎一样,卡了 2.5h 最后发现是这个最慢,在之前的代码上一改就过了。

点击查看代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
char buf[1<<15],*p1=buf,*p2=buf;
#define nc() (p1==p2&&(p2=buf+fread(p1=buf,1,1<<15,stdin),p1==p2)?-1:*p1++)
inline int read(){
    int x=0;char c=nc();
    for(;!isdigit(c);c=nc());
    for(; isdigit(c);c=nc()) x=(x<<3)+(x<<1)+(c^48);
    return x;
}
int n,m,V,a[100005];ll lst,c[100005];
void add(int x,int v){
	for(;x<=n;x+=x&-x)c[x]+=v;
}
ll ask(int x){
	ll res=0;
	for(;x;x-=x&-x)res+=c[x];
	return res;
}
ll ask(int l,int r){
	return ask(r)-ask(l-1);
}
int s[500005],b[100005];vector<int>v[500005],nxt[500005];
signed main(){
	n=read(),m=read();
	for(int i=1;i<=n;i++)a[i]=read(),add(i,a[i]),V=max(V,a[i]),s[a[i]]++;
	for(int i=1;i<=V+1;i++)s[i]+=s[i-1];
	for(int i=1;i<=n;i++)b[--s[a[i]]]=i;
	for(int i=2;i<=V;i++){
		for(int j=i;j<=V;j+=i)for(int k=s[j];k<s[j+1];k++)v[i].push_back(b[k]);
		int siz=(int)v[i].size();if(!siz)continue;
		nxt[i].resize(siz+2);stable_sort(v[i].begin(),v[i].end());
		for(int j=0;j<siz-1;j++)nxt[i][j]=j+1;
		nxt[i][siz-1]=n+1;
	}
	while(m--){
		int op=read();
		if(op==1){
			int l=read()^lst,r=read()^lst,x=read()^lst;
			int siz=(int)v[x].size();
			if(v[x].empty()||v[x].back()<l||v[x].front()>r)continue;
			int p=lower_bound(v[x].begin(),v[x].end(),l)-v[x].begin(),o=p;
			while(1){
				while(p<siz&&a[v[x][p]]%x!=0)p=nxt[x][p];
				if(p>=siz){for(int i=o,j=nxt[x][i];i<siz;i=j,j=(j<siz?nxt[x][j]:j))nxt[x][i]=n+1;break;}
				if(v[x][p]>r){for(int i=o,j=nxt[x][i];i<p;i=j,j=(j<siz?nxt[x][j]:j))nxt[x][i]=p;break;}
				add(v[x][p],a[v[x][p]]/x-a[v[x][p]]),a[v[x][p]]/=x;
				if(a[v[x][p]]%x==0){for(int i=o,j=nxt[x][i];i<p;i=j,j=(j<siz?nxt[x][j]:j))nxt[x][i]=p;o=p;}
				p=nxt[x][p];
			}
		}
		else{
			int l=read()^lst,r=read()^lst;
			lst=ask(l,r),printf("%lld\n",lst);
		}
	} 
	return 0;
}

W - Stations

考虑对每个位置维护 \(p_i\) 表示 \(i\) 最远能传播到哪,于是变成区间取 min 单点修,再开个数据结构维护一下 \(b\) 就行了。复杂度 \(\mathcal{O}(n\log^2 n)\)

点击查看代码
#include<bits/stdc++.h>
#define int long long
#ifdef DEBUG
#define msg(args...) fprintf(stderr,args)
#else
#define msg(...) void()
#endif
using namespace std;
const int inf=1e18;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
	while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
int n,q;
struct BIT{
	int c1[200005],c2[200005];
	void add(int x,int v){
		for(int y=x;x<=n;x+=x&-x)c1[x]+=v,c2[x]-=y*v;
	}
	void add(int l,int r,int v){
		if(l<=r)add(l,v),add(r+1,-v);
	}
	int ask(int x){
		int res=0;
		for(int y=x;x;x-=x&-x)res+=(y+1)*c1[x]+c2[x];
		return res;
	}
	int ask(int l,int r){
		if(l>r)return 0;
		return ask(r)-ask(l-1);
	}	
}bit;
struct segtree{
	#define ls p<<1
	#define rs p<<1|1
	#define lson l,mid,ls
	#define rson mid+1,r,rs
	struct Node{
		int mx,se,cnt,tag;
	}c[800005];
	void pushup(int p){
		c[p].se=-inf,c[p].cnt=0;c[p].mx=max(c[ls].mx,c[rs].mx);
		if(c[ls].mx==c[p].mx)c[p].cnt+=c[ls].cnt;
		if(c[rs].mx==c[p].mx)c[p].cnt+=c[rs].cnt;
		if(c[ls].mx!=c[p].mx)c[p].se=max(c[p].se,c[ls].mx);
		else c[p].se=max(c[p].se,c[ls].se);
		if(c[rs].mx!=c[p].mx)c[p].se=max(c[p].se,c[rs].mx);
		else c[p].se=max(c[p].se,c[rs].se);		
	}
	void pushdown(int l,int r,int p){
		if(c[ls].mx>c[p].tag){
			c[ls].mx=c[p].tag,c[ls].tag=c[p].tag;
		}
		if(c[rs].mx>c[p].tag){
			c[rs].mx=c[p].tag,c[rs].tag=c[p].tag;
		}
		c[p].tag=inf;  
	}
	void build(int l,int r,int p){
		c[p].tag=inf;
		if(l==r){
			c[p].mx=l,c[p].cnt=1,c[p].se=-inf;
			bit.add(1,c[p].mx,c[p].cnt);
			bit.add(1,c[p].mx-1,-1);
			return;
		}
		int mid=(l+r)>>1;
		build(lson),build(rson);
		pushup(p);
	}
	void upd(int l,int r,int p,int x,int k){
		if(l==r){
			bit.add(1,c[p].mx,-c[p].cnt);
			c[p].mx=k;
			bit.add(1,c[p].mx,c[p].cnt);
			return;
		}
		int mid=(l+r)>>1;pushdown(l,r,p);
		if(x<=mid)upd(lson,x,k);
		else upd(rson,x,k);
		pushup(p);
	}
	void add(int l,int r,int p,int L,int R,int k){
		if(c[p].mx<=k)return;
		if(L<=l&&r<=R){
			if(c[p].se<k&&k<c[p].mx){
				bit.add(1,c[p].mx,-c[p].cnt);
				c[p].mx=k,c[p].tag=k;
				bit.add(1,c[p].mx,c[p].cnt);
				return;
			}
		}
		if(l==r)return;
		int mid=(l+r)>>1;pushdown(l,r,p);
		if(L<=mid)add(lson,L,R,k);
		if(R>mid)add(rson,L,R,k);
		pushup(p);
	}
	int qry(int l,int r,int p,int L,int R){
		if(L<=l&&r<=R)return c[p].mx;
		int mid=(l+r)>>1,res=-inf;pushdown(l,r,p);
		if(L<=mid)res=max(res,qry(lson,L,R));
		if(R>mid)res=max(res,qry(rson,L,R));
		return res;
	}
	#undef ls
	#undef rs
	#undef lson
	#undef rson
}Tr;
signed main(){
	n=read(),q=read();Tr.build(1,n,1);
	while(q--){
		int op=read(),x=read(),y=read();
		if(op==1)Tr.add(1,n,1,1,x-1,x-1),Tr.upd(1,n,1,x,y);
		else printf("%lld\n",bit.ask(x,y));
	} 
	return 0;
}

X - The Fair Nut's getting crazy

考虑暴力。枚举交的左右端点 \(l,r\),记这两个子段为 \([a,b],[c,d](a<c)\),那么 \(b=r,c=l\)\(a<l,d>r\)。注意到“位于两个子段交中的元素在每个子段中只能出现一次”这个限制可以写成 \(a>\max\limits_{j=l}^r L_j\)\(b<\min\limits_{j=l}^r R_j\),其中 \(L_i\) 表示 \(i\) 左边第一个等于 \(a_i\) 的位置(没有则为 0),\(R_i\) 表示 \(i\) 右边第一个等于 \(a_i\) 的位置(没有则为 \(n+1\))。于是得到了 \(\mathcal{O}(n^2)\) 的暴力。

考虑优化。不妨从小到大枚举 \(r\),开一颗线段树存 \(\max\limits_{j=i}^r L_j\)\(\min\limits_{j=i}^r R_j\),每次移动的时候二分找到需要赋值的区间即可,这一部分是 \(\mathcal{O}(n\log n)\) 的。观察刚刚计算答案的式子 \((l-\max\limits_{j=l}^r L_j-1)(\min\limits_{j=l}^r R_j-r-1)\),不妨令 \(L_i\gets L_i+1,R_i\gets R_i-1\),然后把式子拆开,发现只需要在刚刚的基础上额外维护对应位置乘积的和即可。因为修改操作只有区间赋值,所以修改贡献是简单的。还有一个限制是 \((l-\max\limits_{j=l}^r L_j),(\min\limits_{j=l}^r R_j-r)\ge 0\),这个也可以二分找到合法的 \(l\) 所在的区间。这样可以处理除了 \(l\min\limits_{j=l}^r R_j\) 外的三项(其实也可以求,只不过懒得再维护了),可以枚举 \(l\) 再做一遍类似的过程即可求解。复杂度 \(\mathcal{O}(n\log n)\)

点击查看代码
#include<bits/stdc++.h>
#define int long long
#ifdef DEBUG
#define msg(args...) fprintf(stderr,args)
#else
#define msg(...) void()
#endif
using namespace std;
const int inf=1e18,mod=1e9+7;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
	while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
struct segtree{
	#define ls (p<<1)
	#define rs (p<<1|1)
	#define lson l,mid,ls
	#define rson mid+1,r,rs
	struct Node{
		int a,b,ab,ta,tb;
	}c[400005];
	void pushup(int p){
		c[p].a=(c[ls].a+c[rs].a)%mod;
		c[p].b=(c[ls].b+c[rs].b)%mod;
		c[p].ab=(c[ls].ab+c[rs].ab)%mod;
	}
	void pushdown(int l,int r,int p){
		int mid=(l+r)>>1;
		if(c[p].ta!=inf){
			int ta=c[p].ta;c[p].ta=inf;c[ls].ta=c[rs].ta=ta;
			c[ls].a=(mid-l+1)*ta%mod;c[rs].a=(r-mid)*ta%mod;
			c[ls].ab=c[ls].b*ta%mod;c[rs].ab=c[rs].b*ta%mod;
		}
		if(c[p].tb!=inf){
			int tb=c[p].tb;c[p].tb=inf;c[ls].tb=c[rs].tb=tb;
			c[ls].b=(mid-l+1)*tb%mod;c[rs].b=(r-mid)*tb%mod;
			c[ls].ab=c[ls].a*tb%mod;c[rs].ab=c[rs].a*tb%mod;
		}
	}
	void build(int l,int r,int p){
		c[p].ta=c[p].b=inf;
		if(l==r){
			c[p].a=c[p].b=c[p].ab=0;
			return;
		}
		int mid=(l+r)>>1;
		build(lson);build(rson);
		pushup(p);
	}
	void upda(int l,int r,int p,int L,int R,int k){
		if(L<=l&&r<=R){
			c[p].ta=k,c[p].a=k*(r-l+1)%mod,c[p].ab=k*c[p].b%mod;
			return;
		}
		int mid=(l+r)>>1;pushdown(l,r,p);
		if(L<=mid)upda(lson,L,R,k);
		if(R>mid)upda(rson,L,R,k);
		pushup(p);
	}
	void updb(int l,int r,int p,int L,int R,int k){
		if(L<=l&&r<=R){
			c[p].tb=k,c[p].b=k*(r-l+1)%mod,c[p].ab=k*c[p].a%mod;
			return;
		}
		int mid=(l+r)>>1;pushdown(l,r,p);
		if(L<=mid)updb(lson,L,R,k);
		if(R>mid)updb(rson,L,R,k);
		pushup(p);
	}
	int aska(int l,int r,int p,int L,int R){
		if(L<=l&&r<=R){
			return c[p].a;
		}
		int mid=(l+r)>>1,res=0;pushdown(l,r,p);
		if(L<=mid)res=(res+aska(lson,L,R))%mod;
		if(R>mid)res=(res+aska(rson,L,R))%mod;
		return res;		
	}
	int askb(int l,int r,int p,int L,int R){
		if(L<=l&&r<=R){
			return c[p].b;
		}
		int mid=(l+r)>>1,res=0;pushdown(l,r,p);
		if(L<=mid)res=(res+askb(lson,L,R))%mod;
		if(R>mid)res=(res+askb(rson,L,R))%mod;
		return res;		
	}
	int askab(int l,int r,int p,int L,int R){
		if(L<=l&&r<=R){
			return c[p].ab;
		}
		int mid=(l+r)>>1,res=0;pushdown(l,r,p);
		if(L<=mid)res=(res+askab(lson,L,R))%mod;
		if(R>mid)res=(res+askab(rson,L,R))%mod;
		return res;		
	}
	#undef ls
	#undef rs
	#undef lson
	#undef rson
}Tr;
int a[100005],b[100005],L[100005],R[100005],p[100005],f[20][100005],g[20][100005];
int askf(int l,int r){
	if(l>r)return -inf;
	int o=__lg(r-l+1);
	return max(f[o][l],f[o][r-(1ll<<o)+1]);
}
int askg(int l,int r){
	if(l>r)return -inf;
	int o=__lg(r-l+1);
	return min(g[o][l],g[o][r-(1ll<<o)+1]);
}
signed main(){
	int n=read(),tot=0,ans=0;
	for(int i=1;i<=n;i++)a[i]=b[++tot]=read();
	sort(b+1,b+tot+1);tot=unique(b+1,b+tot+1)-b-1;
	for(int i=1;i<=n;i++)a[i]=lower_bound(b+1,b+tot+1,a[i])-b;
	for(int i=1;i<=tot;i++)p[i]=1;
	for(int i=1;i<=n;i++)L[i]=p[a[i]],p[a[i]]=i+1;
	for(int i=1;i<=tot;i++)p[i]=n;
	for(int i=n;i>=1;i--)R[i]=p[a[i]],p[a[i]]=i-1;
	for(int i=1;i<=n;i++)f[0][i]=L[i],g[0][i]=R[i];
	for(int j=1;(1ll<<j)<=n;j++){
		for(int i=1;i+(1ll<<j)-1<=n;i++){
			f[j][i]=max(f[j-1][i],f[j-1][i+(1ll<<(j-1))]);
			g[j][i]=min(g[j-1][i],g[j-1][i+(1ll<<(j-1))]);			
		}
	}
	Tr.build(1,n,1);
	for(int i=1,l,r,res;i<=n;i++){
		int p=1;
		l=1,r=i,res=i;
		while(l<=r){
			int mid=(l+r)>>1;
			if(askf(mid,i)<=L[i])res=mid,r=mid-1;
			else l=mid+1;
		}
		Tr.upda(1,n,1,res,i,L[i]);
		l=1,r=i,res=i;
		while(l<=r){
			int mid=(l+r)>>1;
			if(askg(mid,i)>=R[i])res=mid,r=mid-1;
			else l=mid+1;
		}
		Tr.updb(1,n,1,res,i,R[i]);
		l=1,r=i,res=-1;
		while(l<=r){
			int mid=(l+r)>>1;
			if(askf(mid,i)<mid)res=mid,r=mid-1;
			else l=mid+1;
		}
		if(res==-1)continue;
		p=max(p,res);
		l=1,r=i,res=-1;
		while(l<=r){
			int mid=(l+r)>>1;
			if(askg(mid,i)>i)res=mid,r=mid-1;
			else l=mid+1;
		}
		if(res==-1)continue;
		p=max(p,res);
		if(p>i)continue;
		ans=(ans-(p+i)*(i-p+1)/2%mod*i%mod+mod)%mod;
		ans=(ans-Tr.askab(1,n,1,p,i)+mod)%mod;
		ans=(ans+i*Tr.aska(1,n,1,p,i)%mod)%mod;
	}
	Tr.build(1,n,1);
	for(int i=n,l,r,res;i>=1;i--){
		int p=n;
		l=i,r=n,res=i;
		while(l<=r){
			int mid=(l+r)>>1;
			if(askf(i,mid)<=L[i])res=mid,l=mid+1;
			else r=mid-1;
		}
		Tr.upda(1,n,1,i,res,L[i]);
		l=i,r=n,res=i;
		while(l<=r){
			int mid=(l+r)>>1;
			if(askg(i,mid)>=R[i])res=mid,l=mid+1;
			else r=mid-1;
		}
		Tr.updb(1,n,1,i,res,R[i]);
		l=i,r=n,res=-1;
		while(l<=r){
			int mid=(l+r)>>1;
			if(askf(i,mid)<i)res=mid,l=mid+1;
			else r=mid-1;
		}
		if(res==-1)continue;
		p=min(p,res);
		l=i,r=n,res=-1;
		while(l<=r){
			int mid=(l+r)>>1;
			if(askg(i,mid)>mid)res=mid,l=mid+1;
			else r=mid-1;
		}
		if(res==-1)continue;
		p=min(p,res);
		if(p<i)continue;
		ans=(ans+i*Tr.askb(1,n,1,i,p)%mod)%mod;
	}
	printf("%lld\n",ans);
	return 0;
}
posted @ 2023-12-21 20:22  xx019  阅读(31)  评论(0编辑  收藏  举报