杂题选做 Ⅱ

优秀的拆分

实际上只需要求以 \(i\) 为起始位置的 \(AA\) 串的长度。
枚举串 \(A\) 长度 \(len\),定长分块,每隔 \(len\) 撒一个点,则一个 \(AA\) 串必会经过相邻的两个点。

那么对于此时每一对相邻的点,求他们对应前后缀的的 \(lcs,lcp\) 就能算出来经过这两个点长为 \(len\)\(AA\) 串的起始下标的区间,差分后一起处理即可。

枚举复杂度是调和级数,瓶颈在后缀数组,总复杂度 \(O(n\log n)\)

AC Code
// Written by Mi2uk1 
// Try Harder.

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define pii pair<int,int>
#define pll pair<long long,long long>
#define i28 __int128 
#define fir first
#define INF (1e9) 
#define sec second
#define pb push_back
#define eb emplace_back

const int N=3e4+9,M=1e6+9;
const int MOD=1e9+7,base=251;
const double eps=1e-14;
inline void chkmax(int &x,int y){x=x<y?y:x;}
inline void chkmin(int &x,int y){x=x<y?x:y;}
inline void chkmax(ll &x,ll y){x=x<y?y:x;}
inline void chkmin(ll &x,ll y){x=x<y?x:y;}
inline int lowbit(int x){return x&(-x);}
int qpow(int a,int b,int p){
    int ret=1;
    while(b){
        if(b&1) ret=1ll*ret*a%p;
        a=1ll*a*a%p;
        b>>=1;
    }
    return ret; 
}

struct SA{
	char s[N];
	int len,tot,lg[N];
	int sa[N],rk[N],h[N];
	int tmp[N],bin[N],siz;
	void clear(){
		for(int i=1;i<=len;++i) s[i]='\0'; 
		memset(sa,0,sizeof sa);
		memset(rk,0,sizeof rk);
		memset(h,0,sizeof h);
		memset(tmp,0,sizeof tmp);
		memset(bin,0,sizeof bin);
	}
	void qsort(){
		for(int i=0;i<=siz;++i) bin[i]=0;
		for(int i=1;i<=len;++i) bin[rk[tmp[i]]]++;
		for(int i=1;i<=siz;++i) bin[i]+=bin[i-1];
		for(int i=len;i>=1;--i) sa[bin[rk[tmp[i]]]--]=tmp[i];
	} 
	void build(){
		len=strlen(s+1);
		siz=max(len,'z'-'0'+1);
		for(int i=1;i<=len;++i) 
			tmp[i]=i,rk[i]=s[i]-'0'+1;
		qsort();
		for(int j=1;j<=len;j<<=1){
			tot=0;
			for(int i=len-j+1;i<=len;++i) tmp[++tot]=i;
			for(int i=1;i<=len;++i)
				if(sa[i]-j>0)
					tmp[++tot]=sa[i]-j;
					
			qsort();
			swap(tmp,rk);
			rk[sa[1]]=tot=1;
			for(int i=2;i<=len;++i){
				if(tmp[sa[i]]==tmp[sa[i-1]] &&
				   tmp[sa[i]+j]==tmp[sa[i-1]+j])
					rk[sa[i]]=tot;
				else rk[sa[i]]=++tot;
			}
			if(tot==len) break;
		}
	} 
	int calc(int x,int y,int tlen){
    	while(x+tlen<=len && y+tlen<=len && 
			  s[x+tlen]==s[y+tlen]) ++tlen;
		return tlen;
	}
	void build_h(){
    	for(int i=1;i<=len;++i)
			h[i]=(rk[i]==1)?0:calc(i,sa[rk[i]-1],max(h[i-1]-1,0));
	}
	int st[22][N];
	void build_st(){
		lg[0]=-1;
		for(int i=1;i<=len;++i) lg[i]=lg[i>>1]+1;
        for(int i=1;i<=len;++i) st[0][i]=h[sa[i]];
		for(int j=1;(1<<j)<=len;++j){
            int step=1<<(j-1);
			for(int i=1;i+step<=len;++i){
                st[j][i]=min(st[j-1][i],st[j-1][i+step]);
			}
		}
	}
	int lcp(int x,int y){
		if(x==y) return len-x+1;
		x=rk[x],y=rk[y];
		if(x>y)swap(x,y); ++x;
		int step=lg[y-x+1];
		return min(st[step][x],st[step][y-(1<<step)+1]);
	}
} pre,suf; 
char s[N],tmp[N]; 
int n,a[N],b[N],d[N];
void solve(){
	for(int i=1;i<=n;++i) d[i]=0;
	suf.clear(); pre.clear();
	for(int i=1;i<=n;++i){
		suf.s[i]=s[i];
		pre.s[i]=s[n-i+1];
	}
	suf.build(); suf.build_h(); suf.build_st();
	pre.build(); pre.build_h(); pre.build_st();
	for(int len=1;len<=n;++len){
		for(int i=len,j;i<=n;i+=len){
			j=i+len;
			if(j>n) break;
			if(s[i]!=s[j]) continue;
			int L=pre.lcp(n-i+1,n-j+1),R=suf.lcp(i,j);
			int ul=i-L+1,ur=i+R-1;
			chkmax(ul,i-len+1);
			chkmin(ur,j-1);
			if(ul+len-1>ur) continue;
			++d[ul]; --d[ur-len+2];
		}
	}
	for(int i=1;i<=n;++i) d[i]+=d[i-1];
	for(int i=1;i<=n;++i) a[i]=d[i];
}
void Mian(){
	cin>>s+1;
	n=strlen(s+1);
	solve();
	for(int i=1;i<=n;++i) b[i]=a[i];
	for(int i=1;i<=n;++i) tmp[i]=s[i];
	for(int i=1;i<=n;++i) s[i]=tmp[n-i+1];
	solve();
	ll ans=0;
	for(int i=1;i<n;++i){
		ans+=1ll*a[n-i+1]*b[i+1];
	}
	cout<<ans<<'\n';
	
	//for(int i=1;i<=n;++i) cout<<a[i]<<' '<<b[i]<<'\n'; cout<<'\n'<<'\n';
}
void Mianclr(){
	for(int i=1;i<=n;++i) s[i]='\0',a[i]=b[i]=0;
} 
signed main(){
    ios::sync_with_stdio(false); 
    cin.tie(0); cout.tie(0);     
	
	//freopen("P10544_3.in","r",stdin);
	//freopen("P10544_3.op","w",stdout);
	
    int c,T=1; cin>>T;
    while(T--){
    	Mian();
		Mianclr(); 
	}
}

拦截导弹

\(len_i\) 为以 \(i\) 为结尾的最长子序列,\(cnt_i\) 为此时以 \(i\) 结尾最长子序列个数,有转移:

\[\left\{\begin{matrix} len_i=\max(len_j)+1 \\ cnt_i=\max_{len_j+1=len_i} cnt_j \end{matrix}\right. \]

其中要求 \(j < i,h_j \geq h_i,v_j \geq v_i\)

直接转移会爆,发现限制是一个三维偏序,我们可以使用 cdq 分治维护。

求出现概率也很简单,那就是包含 \(i\) 的最长子序列和总最长子序列数的比值。我们对称记一个反的 \(len,cnt\),则如果正反 \(len\) 加起来是最长的,那么包含 \(i\) 的最长子序列数就是两个 \(cnt\) 的乘积。

注意有可能爆 long long,要用 double。以及统计总最长子序列数的时候不要重复计数。

AC Code
// Written by Mi2uk1 
// Try Harder.

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define pii pair<int,int>
#define pll pair<long long,long long>
#define i28 __int128 
#define fir first
#define INF (1e9) 
#define sec second
#define pb push_back
#define eb emplace_back

const int N=1e5+9,M=1e6+9;
const int MOD=1e9+7,base=251;
const double eps=1e-14;
inline void chkmax(int &x,int y){x=x<y?y:x;}
inline void chkmin(int &x,int y){x=x<y?x:y;}
inline void chkmax(ll &x,ll y){x=x<y?y:x;}
inline void chkmin(ll &x,ll y){x=x<y?x:y;}
inline int lowbit(int x){return x&(-x);}
int qpow(int a,int b,int p){
    int ret=1;
    while(b){
        if(b&1) ret=1ll*ret*a%p;
        a=1ll*a*a%p;
        b>>=1;
    }
    return ret; 
}

#define pid pair<int,long double>
int n,h[N],v[N],b[N],tot,ans; 
struct BIT1{
	pid f[N];
	void clear(int u){
		for(;u<=tot;u+=lowbit(u)) f[u]={0,0.0};
	}
	void modify(int u,pid d){
		for(;u<=tot;u+=lowbit(u)){
			if(d.fir>f[u].fir) f[u]=d;
			else if(d.fir==f[u].fir) f[u].sec+=d.sec;
		}
	}
	pid query(int u){
		pid ret={0,0.0};
		for(;u;u-=lowbit(u)){
			if(f[u].fir>ret.fir) ret=f[u];
			else if(f[u].fir==ret.fir) ret.sec+=f[u].sec;
		}
		return ret;
	}
} T;
pid p[N],s[N];
struct node{
	int id,h,v,tag;
} a[N],tmp[N];
bool cmp1(node u,node v){
	return u.id<v.id;
}
bool cmp2(node u,node v){
	return u.h>v.h;
}

// a - h down
void cdq1(int l,int r,pid *dp){
	if(l==r) return ;
	int mid=l+r>>1;
	cdq1(l,mid,dp);
	for(int i=l;i<=r;++i) tmp[i]=a[i];
	
	sort(a+l,a+mid+1,cmp2);
	sort(a+mid+1,a+r+1,cmp2);
	
	for(int i=mid+1,j=l;i<=r;++i){
		while(j<=mid && a[j].h>=a[i].h){
			T.modify(tot-a[j].v+1,dp[a[j].id]);
			++j;
		}
		pid uu=T.query(tot-a[i].v+1);
		if(uu.fir<=0) continue;
		uu.fir+=1;
		if(uu.fir>dp[a[i].id].fir) dp[a[i].id]=uu;
		else if(uu.fir==dp[a[i].id].fir) dp[a[i].id].sec+=uu.sec;
	}
	for(int j=l;j<=mid;++j) T.clear(tot-a[j].v+1);
	for(int i=l;i<=r;++i) a[i]=tmp[i]; 
    cdq1(mid+1,r,dp);
}

// a - h up
void cdq2(int l,int r,pid *dp){
	if(l==r) return ;
	int mid=l+r>>1;
	cdq2(mid+1,r,dp);
	for(int i=l;i<=r;++i) tmp[i]=a[i];
	
	sort(a+l,a+mid+1,cmp2); reverse(a+l,a+mid+1);
	sort(a+mid+1,a+r+1,cmp2); reverse(a+mid+1,a+r+1);
	for(int i=l,j=mid+1;i<=mid;++i){
		while(j<=r && a[j].h<=a[i].h){
			T.modify(a[j].v,dp[a[j].id]);
			++j;
		}
		pid uu=T.query(a[i].v);
		if(uu.fir<=0) continue;
		uu.fir+=1;
		if(uu.fir>dp[a[i].id].fir) dp[a[i].id]=uu;
		else if(uu.fir==dp[a[i].id].fir) dp[a[i].id].sec+=uu.sec;
	}
	for(int j=mid+1;j<=r;++j) T.clear(a[j].v);
	for(int i=l;i<=r;++i) a[i]=tmp[i]; 
    cdq2(l,mid,dp);
}
long double sum; 
void Mian(){
	cin>>n;
	for(int i=1;i<=n;++i) cin>>h[i]>>v[i];
	for(int i=1;i<=n;++i) b[++tot]=h[i],b[++tot]=v[i];
	sort(b+1,b+tot+1);
	tot=unique(b+1,b+tot+1)-b-1;
	for(int i=1;i<=n;++i){
		h[i]=lower_bound(b+1,b+tot+1,h[i])-b;
		v[i]=lower_bound(b+1,b+tot+1,v[i])-b;
	} 
	
	
	for(int i=1;i<=n;++i){
		a[i].id=i;
		a[i].h=h[i]; a[i].v=v[i];
	}
	for(int i=1;i<=n;++i) p[i]={1,1.0};
	cdq1(1,n,p);
	//for(int i=1;i<=n;++i) cout<<lenp[i]<<' '; cout<<'\n';
	
	for(int i=1;i<=n;++i){
		a[i].id=i;
		a[i].h=h[i]; a[i].v=v[i];
	}
	for(int i=1;i<=n;++i) s[i]={1,1.0};
	cdq2(1,n,s);
	//for(int i=1;i<=n;++i) cout<<lens[i]<<' '; cout<<'\n';
	
	for(int i=1;i<=n;++i) chkmax(ans,p[i].fir+s[i].fir-1);
	printf("%d\n",ans); 
	for(int i=1;i<=n;++i){
		if(p[i].fir==ans)
			sum+=p[i].sec; 
	} 
	for(int i=1;i<=n;++i){
		if(p[i].fir+s[i].fir-1==ans)
			printf("%.5Lf ",p[i].sec*s[i].sec/sum);
		else printf("0 "); 
	}
}
void Mianclr(){
	
} 
signed main(){
    ios::sync_with_stdio(false); 
    cin.tie(0); cout.tie(0);     
	
	//freopen("P10544_3.in","r",stdin);
	//freopen("P10544_3.op","w",stdout);
	
    int c,T=1; //cin>>T;
    while(T--){
    	Mian();
		Mianclr(); 
	}
}

DZY Loves Chinese

这题真神了。
强制在线,注意到输入的边集大小也要异或上前面的值,所以可以直接判后面输入了多少个数来得到上一组数据的答案。
所以只用暴力最后一组就行了。

AC Code
// Written by Mi2uk1 
// Try Harder.

#include<bits/stdc++.h>
using namespace std;
#define i64 long long
#define i128 __int128 
#define ull unsigned long long
#define pii pair<int,int>
#define pll pair<long long,long long>
#define fir first
#define INF (1e9) 
#define sec second
#define pb push_back
#define eb emplace_back

const int N=5e5+9,M=1e6+9;
const int MOD=998244353,base=251;
const double eps=1e-14;
inline void chkmax(int &x,int y){x=x<y?y:x;}
inline void chkmin(int &x,int y){x=x<y?x:y;}
inline void chkmax(i64 &x,i64 y){x=x<y?y:x;}
inline void chkmin(i64 &x,i64 y){x=x<y?x:y;}
inline int lowbit(int x){return x&(-x);}
int qpow(int a,int b,int p){
    int ret=1;
    while(b){
        if(b&1) ret=1ll*ret*a%p;
        a=1ll*a*a%p;
        b>>=1;
    }
    return ret; 
}

int n,m,q,lst,o[N];
int fa[N],siz[N];
int find(int u){
	return fa[u]==u?u:fa[u]=find(fa[u]);
}
void merge(int u,int v){
	u=find(u); v=find(v);
	if(u==v) return ;
	fa[v]=u; siz[u]+=siz[v];
}
pii a[N]; 
bool used[N];
void Mian(){
	cin>>n>>m;
	for(int i=1;i<=m;++i) cin>>a[i].fir>>a[i].sec;
	cin>>q; cin.ignore();
	for(int i=1;i<q;++i){
		string s;
		getline(cin,s);
		int k=0,cnt=0;
		for(int j=0;j<s.size();++j){
			if(isdigit(s[j])){
				k=k*10+s[j]-'0';
			}else break;
		}
		for(int j=0;j<s.size();++j)
			if(!isdigit(s[j])) ++cnt; int tk=k;
		k^=lst;
		if(i==1) continue;
		if(k!=cnt){
			puts("Connected");
			lst++;
		}else puts("Disconnected");
//		cout<<i<<' '<<tk<<' '<<lst<<' '<<cnt<<"!!!\n";
	}
	int k; cin>>k; k^=lst;
	int x,cnt=0; vector<int> tmp;
	while(cin>>x){
		tmp.pb(x); ++cnt;
	} 
	if(k!=cnt){
		puts("Connected");
		++lst; 
	}
	else puts("Disconnected");
//	cout<<lst<<"???"<<endl;
	for(auto i:tmp) used[i^lst]=1;
	for(int i=1;i<=n;++i) fa[i]=i,siz[i]=1;
	for(int i=1;i<=m;++i){
		if(used[i]) continue;
		merge(a[i].fir,a[i].sec);
	}
	if(siz[find(1)]==n) puts("Connected");
	else puts("Disconnected");
}
void Mianclr(){
	
} 
signed main(){
    ios::sync_with_stdio(false); 
    cin.tie(0); cout.tie(0);     
	
	//freopen("P10544_3.in","r",stdin);
	//freopen("P10544_3.op","w",stdout);
	
    int c,T=1; //cin>>T;
    while(T--){
    	Mian();
		Mianclr(); 
	}
}

DZY Loves Chinese II

神题这真了。
具体做法证明在这里,推荐看一下,以下展示笔者在做此题时的思路。

由于只考虑连通性,我们还是可以取出一棵 dfs 树考虑,那么不连通的条件就是存在一个树边,这条边和所有跨越这条边的非树边都被删除。

做过 wmr 的哈希智慧题,我们考虑异或哈希,对非树边随机赋权,树边就是所有跨过这条边的非树边的异或和,查询即为判断是否存在一个子集对应边权异或和为 \(0\)。上线性基就做完了。

AC Code
// Written by Mi2uk1 
// Try Harder.

#include<bits/stdc++.h>
using namespace std;
#define i64 long long
#define i128 __int128 
#define ull unsigned long long
#define pii pair<int,int>
#define pll pair<long long,long long>
#define fir first
#define INF (1e9) 
#define sec second
#define pb push_back
#define eb emplace_back

const int N=1e5+9,M=5e5+9;
const int MOD=998244353,base=251;
const double eps=1e-14;
inline void chkmax(int &x,int y){x=x<y?y:x;}
inline void chkmin(int &x,int y){x=x<y?x:y;}
inline void chkmax(i64 &x,i64 y){x=x<y?y:x;}
inline void chkmin(i64 &x,i64 y){x=x<y?x:y;}
inline int lowbit(int x){return x&(-x);}
int qpow(int a,int b,int p){
    int ret=1;
    while(b){
        if(b&1) ret=1ll*ret*a%p;
        a=1ll*a*a%p;
        b>>=1;
    }
    return ret; 
}

mt19937 rd(random_device{}());
int h[39];
void init(){
	for(int j=0;j<=31;++j) h[j]=0;
}
int insert(int x){
	for(int j=31;j>=0;--j){
		if(!((x>>j)&1)) continue;
		if(h[j]){x^=h[j]; continue;}
		h[j]=x;
		return 0;
	}
	if(!x) return 1;
}


int n,m,q,a[M],dep[N];
pii e[M];
bool intr[M];
vector<pii> G[N];
void pdfs(int u,int fa){
	dep[u]=dep[fa]+1;
	for(auto y:G[u]){
		int v=y.fir,id=y.sec;
		if(v==fa) continue;
		if(!dep[v]){
			pdfs(v,u);
			intr[id]=1;
		}
	}
}
int dfs(int u,int fa){
	int w=0; 
	for(auto y:G[u]){
		int v=y.fir,id=y.sec;
		if(v==fa) continue;
		if(intr[id]) w^=dfs(v,u);
		else w^=a[id];
	}
	for(auto y:G[u]){
		int v=y.fir,id=y.sec;
		if(v==fa) a[id]=w;
	}
	return w;
}
void Mian(){
	cin>>n>>m;
	for(int i=1;i<=m;++i){
		int u,v; cin>>u>>v;
		e[i]={u,v};
		G[u].pb({v,i}); G[v].pb({u,i});
	}
	pdfs(1,0);
	for(int i=1;i<=m;++i) if(!intr[i]) a[i]=rd();
	dfs(1,0);
	
	cin>>q;
	int lstans=0;
	while(q--){
		init();
		int k,flg=0; cin>>k;
		while(k--){
			int o; cin>>o;
			flg|=insert(a[o^lstans]); 
		}
		if(flg) puts("Disconnected");
		else{
			puts("Connected");
			++lstans;
		}
	} 
}
void Mianclr(){
	
} 
signed main(){
    ios::sync_with_stdio(false); 
    cin.tie(0); cout.tie(0);     
	
	//freopen("P10544_3.in","r",stdin);
	//freopen("P10544_3.op","w",stdout);
	
    int c,T=1; //cin>>T;
    while(T--){
    	Mian();
		Mianclr(); 
	}
}

切割

上一个题的双倍经验,注意重边的处理就行。

AC Code
// Written by Mi2uk1 
// Try Harder.

#include<bits/stdc++.h>
using namespace std;
#define i64 long long
#define i128 __int128 
#define ull unsigned long long
#define pii pair<int,int>
#define pll pair<long long,long long>
#define fir first
#define INF (1e9) 
#define sec second
#define pb push_back
#define eb emplace_back

const int N=1e6+9,M=1e6+9;
const int MOD=998244353,base=251;
const double eps=1e-14;
inline void chkmax(int &x,int y){x=x<y?y:x;}
inline void chkmin(int &x,int y){x=x<y?x:y;}
inline void chkmax(i64 &x,i64 y){x=x<y?y:x;}
inline void chkmin(i64 &x,i64 y){x=x<y?x:y;}
inline int lowbit(int x){return x&(-x);}
int qpow(int a,int b,int p){
    int ret=1;
    while(b){
        if(b&1) ret=1ll*ret*a%p;
        a=1ll*a*a%p;
        b>>=1;
    }
    return ret; 
}

mt19937 rd(random_device{}());
int h[39];
void init(){
	for(int j=0;j<=31;++j) h[j]=0;
}
int insert(int x){
	for(int j=31;j>=0;--j){
		if(!((x>>j)&1)) continue;
		if(h[j]){x^=h[j]; continue;}
		h[j]=x;
		return 0;
	}
	if(!x) return 1;
}


int n,m,q,a[M],dep[N];
pii e[M];
bool intr[M];
vector<pii> G[N];
void pdfs(int u,int fa,int fid){
	dep[u]=dep[fa]+1;
	for(auto y:G[u]){
		int v=y.fir,id=y.sec;
		if(v==fa) continue;
		if(!dep[v]){
			pdfs(v,u,id);
			intr[id]=1;
		}
	}
}
int dfs(int u,int fa,int fid){
	int w=0; 
	for(auto y:G[u]){
		int v=y.fir,id=y.sec;
		if(v==fa){
			if(id==fid) continue;
			w^=a[id];
			continue;
		}
		if(intr[id]) w^=dfs(v,u,id);
		else w^=a[id];
	}
	for(auto y:G[u]){
		int v=y.fir,id=y.sec;
		if(id==fid) a[id]=w;
	}
	return w;
}
void Mian(){
	cin>>n>>m;
	for(int i=1;i<=m;++i){
		int u,v; cin>>u>>v;
		e[i]={u,v};
		G[u].pb({v,i}); G[v].pb({u,i});
	}
	pdfs(1,0,0);
	for(int i=1;i<=m;++i) if(!intr[i]) a[i]=rd();
	dfs(1,0,0);
	
	cin>>q; 
	while(q--){
		init();
		int k,flg=0; cin>>k;
		while(k--){
			int o; cin>>o;
			flg|=insert(a[o]); 
		}
		if(flg) puts("Bob");
		else puts("ymqOAO");
	} 
}
void Mianclr(){
	
} 
signed main(){
    ios::sync_with_stdio(false); 
    cin.tie(0); cout.tie(0);     
	
	//freopen("P10544_3.in","r",stdin);
	//freopen("P10544_3.op","w",stdout);
	
    int c,T=1; //cin>>T;
    while(T--){
    	Mian();
		Mianclr(); 
	}
}

命运

经典题了。
设出来 \(dp(u,j)\) 表示 \(u\) 点子树内未满足的限制中对应子树外结点最大深度为 \(j\),转移考虑当前点 \(v\) 到其父亲 \(u\) 的边的颜色,即:

\[dp'(u,i)=dp(u,i)\left(\sum_{j=0}^{i}dp(v,j)+\sum_{j=0}^{dep_u} dp(v,j)\right)+dp(v,i)\sum_{j=0}^{i-1} dp(u,i) \]

然后跟 Minimax 这个题一样,在线段树合并的时候维护 \(dp\) 值即可。由于一开始只有 \(m\) 个点非空,视 \(n,m\) 同阶,复杂度为 \(O(n\log n)\)

AC Code
// Written by Mi2uk1 
// Try Harder.

#include<bits/stdc++.h>
using namespace std;
#define i64 long long
#define i128 __int128 
#define ull unsigned long long
#define pii pair<int,int>
#define pll pair<long long,long long>
#define fir first
#define INF (1e9) 
#define sec second
#define pb push_back
#define eb emplace_back

const int N=5e5+9,M=1e6+9;
const int MOD=998244353,base=251;
const double eps=1e-14;
inline void chkmax(int &x,int y){x=x<y?y:x;}
inline void chkmin(int &x,int y){x=x<y?x:y;}
inline void chkmax(i64 &x,i64 y){x=x<y?y:x;}
inline void chkmin(i64 &x,i64 y){x=x<y?x:y;}
inline int lowbit(int x){return x&(-x);}
int qpow(int a,int b,int p){
    int ret=1;
    while(b){
        if(b&1) ret=1ll*ret*a%p;
        a=1ll*a*a%p;
        b>>=1;
    }
    return ret; 
}


int n,m,root[N],dep[N],tot;
vector<int> G[N],l[N];
struct node{
	int ls,rs;
	i64 sum,tag;
} tre[N*80];

void add(i64 &x,i64 y){x=(x+y)%MOD;}
void mul(i64 &x,i64 y){x=(x*y)%MOD;}
void insert(int &u,int l,int r,int x){
	u=++tot;
	tre[u].sum=tre[u].tag=1;
	if(l==r) return ;
	int mid=l+r>>1;
	if(x<=mid) insert(tre[u].ls,l,mid,x);
	if(x>mid) insert(tre[u].rs,mid+1,r,x);
}
void push_down(int u){
	if(tre[u].tag==1) return ; 
	if(tre[u].ls){
		mul(tre[tre[u].ls].sum,tre[u].tag);
		mul(tre[tre[u].ls].tag,tre[u].tag);
	}
	if(tre[u].rs){
		mul(tre[tre[u].rs].sum,tre[u].tag);
		mul(tre[tre[u].rs].tag,tre[u].tag);
	}
	tre[u].tag=1;
}
int query(int u,int l,int r,int x){
	if(!u || l==r) return tre[u].sum;
	push_down(u);
	int mid=l+r>>1;
	if(x<=mid) return query(tre[u].ls,l,mid,x);
	if(x>mid) return (tre[tre[u].ls].sum+query(tre[u].rs,mid+1,r,x))%MOD;  
}
int merge(int x,int y,int l,int r,i64 &s1,i64 &s2){
	if(!x && !y) return 0;
	if(!x || !y){
		if(!x){
			add(s1,tre[y].sum);
			mul(tre[y].sum,s2);
			mul(tre[y].tag,s2);
			return y;
		}
		add(s2,tre[x].sum);
		mul(tre[x].sum,s1);
		mul(tre[x].tag,s1);
		return x;
	}
	if(l==r){
		i64 tx=tre[x].sum,ty=tre[y].sum;
		tre[x].sum=(tx*(s1+ty)+ty*s2)%MOD;
		add(s1,ty); add(s2,tx);
		return x;
	}
	push_down(x); push_down(y);
	int mid=l+r>>1;
	tre[x].ls=merge(tre[x].ls,tre[y].ls,l,mid,s1,s2);
	tre[x].rs=merge(tre[x].rs,tre[y].rs,mid+1,r,s1,s2);
	tre[x].sum=(tre[tre[x].ls].sum+tre[tre[x].rs].sum)%MOD;
	return x;
}
void dfs(int u,int fa){
	dep[u]=dep[fa]+1;
	int mxdep=0;
	for(auto v:l[u]) chkmax(mxdep,dep[v]); 
	insert(root[u],0,n,mxdep);
	for(auto v:G[u]){
		if(v==fa) continue;
		dfs(v,u);
		i64 s1=query(root[v],0,n,dep[u]),s2=0;
		root[u]=merge(root[u],root[v],0,n,s1,s2); 
	}
}
void Mian(){
	cin>>n;
	for(int i=1;i<n;++i){
		int u,v; cin>>u>>v;
		G[u].pb(v); G[v].pb(u);
	}
	cin>>m;
	for(int i=1;i<=m;++i){
		int u,v; cin>>u>>v;
		l[v].pb(u);
	}
	dfs(1,0);
	cout<<query(root[1],0,n,0);
}
void Mianclr(){
	
} 
signed main(){
    ios::sync_with_stdio(false); 
    cin.tie(0); cout.tie(0);     
	
	//freopen("P10544_3.in","r",stdin);
	//freopen("P10544_3.op","w",stdout);
	
    int c,T=1; //cin>>T;
    while(T--){
    	Mian();
		Mianclr(); 
	}
}

如何正确地排序

好题啊!!1

笔者的做法是把 max 和 min 拆开。假设现在要算 min,考虑一个 \(a_{i,j}\) 什么时候有贡献,然后会发现限制可以写成一个三维偏序的形式。

然后直接跑三维偏序就是对的,注意这里要去重。

另一个 比较高妙的做法 是把其中的 min 用 min-max 容斥拆开,和 max 正好抵消,这样就只需要二维偏序了。

AC Code
// Written by Mi2uk1 
// Try Harder.

#include<bits/stdc++.h>
using namespace std;
#define i64 long long
#define i128 __int128 
#define ull unsigned long long
#define pii pair<int,int>
#define pll pair<long long,long long>
#define fir first
#define INF (1e9) 
#define sec second
#define pb push_back
#define eb emplace_back

const int N=2e5+9,M=1e6+9;
const int MOD=998244353,base=251;
const double eps=1e-14;
inline void chkmax(int &x,int y){x=x<y?y:x;}
inline void chkmin(int &x,int y){x=x<y?x:y;}
inline void chkmax(i64 &x,i64 y){x=x<y?y:x;}
inline void chkmin(i64 &x,i64 y){x=x<y?x:y;}
inline int lowbit(int x){return x&(-x);}
int qpow(int a,int b,int p){
    int ret=1;
    while(b){
        if(b&1) ret=1ll*ret*a%p;
        a=1ll*a*a%p;
        b>>=1;
    }
    return ret; 
}

#define int long long
int m,n,r[5][N],tot,ans,D=2e5+5; 
struct BIT{
	int f[(int)4e5+9],up=(4e5);
	void modify(int u,int val){
		for(;u<=up;u+=lowbit(u))
			f[u]+=val; 
	} 
	int query(int u){
		int ret=0;
		for(;u;u-=lowbit(u))
			ret+=f[u];
		return ret;
	}
} T;
struct node{
	int op,a,b,c,w;
} a[N<<4],tmp[N<<4]; 
bool cmp(node u,node v){
	return u.a!=v.a?u.a<v.a:u.op<v.op;
}
void cdq(int l,int r){
	if(l==r) return ;
	int mid=l+r>>1;
	cdq(l,mid); cdq(mid+1,r);
	int p=l-1,i=mid+1,j=l;
	for(;i<=r;++i){
		while(j<=mid && a[j].b<=a[i].b){
			if(!a[j].op) T.modify(a[j].c,1);
			tmp[++p]=a[j++];
		}
		if(a[i].op) ans+=a[i].w*T.query(a[i].c);
		tmp[++p]=a[i];
	}
	for(int o=l;o<j;++o) 
		if(!a[o].op) T.modify(a[o].c,-1);
	while(j<=mid) tmp[++p]=a[j++];
	for(int o=l;o<=r;++o) a[o]=tmp[o];
}
void Mian(){
	cin>>m>>n;
	for(int i=1;i<=m;++i)
		for(int j=1;j<=n;++j) cin>>r[i][j];
	
	for(int i=m+1;i<=4;++i)
		for(int j=1;j<=n;++j) r[i][j]=(2e5+1);
	for(int k=1;k<=m;++k){
		tot=0;
		for(int i=1;i<=n;++i){
			int t1[4],t2[4],tc=0;
			for(int j=1;j<=4;++j){
				if(j<k) t1[++tc]=r[k][i]-r[j][i],t2[tc]=r[j][i]-r[k][i]-1;
				if(j>k) t1[++tc]=r[k][i]-r[j][i],t2[tc]=r[j][i]-r[k][i];
			}		
			a[++tot]={0,t1[1]+D,t1[2]+D,t1[3]+D,r[k][i]};
			a[++tot]={1,t2[1]+D,t2[2]+D,t2[3]+D,r[k][i]};
		}
		sort(a+1,a+tot+1,cmp);
		cdq(1,tot);
	}
	
	for(int i=m+1;i<=4;++i)
		for(int j=1;j<=n;++j) r[i][j]=-1;
	for(int k=1;k<=m;++k){
		tot=0;
		for(int i=1;i<=n;++i){
			int t1[4],t2[4],tc=0;
			for(int j=1;j<=4;++j){
				if(j<k) t1[++tc]=r[k][i]-r[j][i],t2[tc]=r[j][i]-r[k][i]+1;
				if(j>k) t1[++tc]=r[k][i]-r[j][i],t2[tc]=r[j][i]-r[k][i];
			}		
			a[++tot]={1,t1[1]+D,t1[2]+D,t1[3]+D,r[k][i]};
			a[++tot]={0,t2[1]+D,t2[2]+D,t2[3]+D,r[k][i]};
		}
		sort(a+1,a+tot+1,cmp);
		cdq(1,tot);
	}
	cout<<ans*2;
}
void Mianclr(){
	
} 
signed main(){
    ios::sync_with_stdio(false); 
    cin.tie(0); cout.tie(0);     
	
	//freopen("P10544_3.in","r",stdin);
	//freopen("P10544_3.op","w",stdout);
	
    int c,T=1; //cin>>T;
    while(T--){
    	Mian();
		Mianclr(); 
	}
}


接水果

哎呀这个要求怎么是盘子是水果的子路径,太坏了。

把限制用 dfs 序刻画后直接上整体二分就是对的,复杂度两个 log。

AC Code
// Written by Mi2uk1 
// Try Harder.

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define pii pair<int,int>
#define pll pair<long long,long long>
#define i28 __int128 
#define fir first
#define INF (1e9) 
#define sec second
#define pb push_back
#define eb emplace_back

const int N=2e5+9,M=1e6+9;
const int MOD=1e9+7,base=251;
const double eps=1e-14;
inline void chkmax(int &x,int y){x=x<y?y:x;}
inline void chkmin(int &x,int y){x=x<y?x:y;}
inline void chkmax(ll &x,ll y){x=x<y?y:x;}
inline void chkmin(ll &x,ll y){x=x<y?x:y;}
inline int lowbit(int x){return x&(-x);}
int qpow(int a,int b,int p){
    int ret=1;
    while(b){
        if(b&1) ret=1ll*ret*a%p;
        a=1ll*a*a%p;
        b>>=1;
    }
    return ret; 
}

int n,p,q,dfn[N],siz[N],idx,tot;
int ans[N],f[20][N],dep[N];
struct node{
	int x,L,R;
	int op,k,id;
	bool operator<(const node &res)const{
		return x==res.x?op<res.op:x<res.x; 
	}	
} a[N];
vector<int> G[N];  
void dfs(int u,int fa){
	f[0][u]=fa; dfn[u]=++idx; 
	siz[u]=1; dep[u]=dep[fa]+1;
	for(int j=1;j<=19;++j)
        f[j][u]=f[j-1][f[j-1][u]];
	for(auto v:G[u]){
		if(v==fa) continue;
		dfs(v,u);
		siz[u]+=siz[v];
	}
}
int lca(int u,int v){
	if(dep[u]>dep[v]) swap(u,v);
	for(int j=19;j>=0;--j){
		if(dep[f[j][v]]>=dep[u])
			v=f[j][v];
	}
	if(u==v) return u;
	for(int j=19;j>=0;--j)
		if(f[j][u]!=f[j][v]) u=f[j][u],v=f[j][v];
	return f[0][u];
}
int getson(int u,int v){
	for(int j=19;j>=0;--j){
		if(dep[f[j][v]]>dep[u])
			v=f[j][v];
	}
	return v;
}
struct BIT{
	int f[N];
	void modify(int u,int d){
		for(;u<=n;u+=lowbit(u))
			f[u]+=d;
	}
	int query(int u){
		int ret=0;
		for(;u;u-=lowbit(u)) ret+=f[u];
		return ret;
	}
} T;

void solve(vector<int> &Q,int l,int r){
	if(l>r || !Q.size()) return ;
	if(l==r){
		for(auto y:Q)
			if(a[y].op==2) ans[a[y].id]=l;
		return ;
	}	
	int mid=(l+r)>>1;
	vector<int> L,R;
	for(auto y:Q){
		if(a[y].op==1){
			if(abs(a[y].k)<=mid){
				int coef=a[y].k/abs(a[y].k);
				T.modify(a[y].L,1*coef);
				T.modify(a[y].R+1,-1*coef);
				L.pb(y);
			}else R.pb(y);
		}
		if(a[y].op==2){
			int ret=T.query(a[y].L);
			if(a[y].k<=ret) L.pb(y);
			else{
				a[y].k-=ret;
				R.pb(y);
			}
		}
	}
	for(auto y:Q){
		if(a[y].op==1){
			if(abs(a[y].k)<=mid){
				int coef=a[y].k/abs(a[y].k);
				T.modify(a[y].L,-1*coef);
				T.modify(a[y].R+1,1*coef);
			}
		}
	}
	 
	solve(L,l,mid); solve(R,mid+1,r);
}
void Mian(){
	cin>>n>>p>>q;
	for(int i=1;i<n;++i){
		int u,v; cin>>u>>v;
		G[u].pb(v); G[v].pb(u);
	}
	dfs(1,0);
		
	for(int i=1;i<=p;++i){
		int u,v,k; cin>>u>>v>>k; ++k;
		if(dfn[u]>dfn[v]) swap(u,v);
		
		if(lca(u,v)==u){
			u=getson(u,v);
			if(dfn[u]>1){
				a[++tot]={1,dfn[v],dfn[v]+siz[v]-1,1,k,0};
				a[++tot]={dfn[u],dfn[v],dfn[v]+siz[v]-1,1,-k,0};
			}
			if(dfn[u]+siz[u]-1<n){
				a[++tot]={dfn[v],dfn[u]+siz[u],n,1,k,0};
				a[++tot]={dfn[v]+siz[v],dfn[u]+siz[u],n,1,-k,0};
			}
		}else{
			a[++tot]={dfn[u],dfn[v],dfn[v]+siz[v]-1,1,k,0};
			a[++tot]={dfn[u]+siz[u],dfn[v],dfn[v]+siz[v]-1,1,-k,0};
		}
	} 
	for(int i=1;i<=q;++i){
		int u,v,k; cin>>u>>v>>k;
		if(dfn[u]>dfn[v]) swap(u,v);
		a[++tot]={dfn[u],dfn[v],0,2,k,i};
	} 
	sort(a+1,a+tot+1); 
	vector<int> tmp; for(int i=1;i<=tot;++i) tmp.pb(i);
	solve(tmp,1,1000000001);
	for(int i=1;i<=q;++i) cout<<ans[i]-1<<'\n';
}
void Mianclr(){
	
} 
signed main(){
    ios::sync_with_stdio(false); 
    cin.tie(0); cout.tie(0);     
	
	//freopen("P10544_3.in","r",stdin);
	//freopen("P10544_3.op","w",stdout);
	
    int c,T=1; //cin>>T;
    while(T--){
    	Mian();
		Mianclr(); 
	}
}

沉玉谷

区间 dp 好题。
一个加深区间 dp 理解的 blog
由上文,设出 \(f(l,r,k)\) 表示删完 \([l,r]\) 用了 \(k\) 步,考虑最后删的哪一个,设 \(g(i,j,k,x)\) 表示删 \([l,r]\) 的一部分用了 \(k\) 步,其中余下的颜色都是 \(x\)\(g\) 的转移即钦定最后删颜色 \(x\) 时下标最小的那个元素的位置。
复杂度 \(O(n^5)\)

AC Code
// Written by Mi2uk1 
// Try Harder.

#include<bits/stdc++.h>
using namespace std;
#define i64 long long
#define i128 __int128 
#define ull unsigned long long
#define pii pair<int,int>
#define pll pair<long long,long long>
#define fir first
#define INF (1e9) 
#define sec second
#define pb push_back
#define eb emplace_back

const int N=50+9,M=1e6+9;
const int MOD=1e9+7,base=251;
const double eps=1e-14;
inline void chkmax(int &x,int y){x=x<y?y:x;}
inline void chkmin(int &x,int y){x=x<y?x:y;}
inline void chkmax(i64 &x,i64 y){x=x<y?y:x;}
inline void chkmin(i64 &x,i64 y){x=x<y?x:y;}
inline int lowbit(int x){return x&(-x);}
int qpow(int a,int b,int p){
    int ret=1;
    while(b){
        if(b&1) ret=1ll*ret*a%p;
        a=1ll*a*a%p;
        b>>=1;
    }
    return ret; 
}

int n,a[N],C[N][N],f[N][N][N],g[N][N][N][N]; 
void add(int &x,int y){x=(x+y)%MOD;}
void Mian(){
	cin>>n;
	for(int i=1;i<=n;++i) cin>>a[i];
	for(int i=0;i<=n;++i){
		for(int j=0;j<=i;++j){
			if(!j) C[i][j]=1;
			else C[i][j]=(C[i-1][j]+C[i-1][j-1])%MOD;
		}
	}
	for(int i=1;i<=n;++i) 
		f[i][i][1]=f[i][i-1][0]=f[i+1][i][0]=1,g[i][i][0][a[i]]=1;
	for(int len=2;len<=n;++len){
		for(int i=1,j;i+len-1<=n;++i){
			j=i+len-1;
			for(int p=i;p<=j;++p){
				for(int k1=0;k1<=len;++k1) for(int k2=0;k1+k2<=len;++k2){
					add(g[i][j][k1+k2][a[p]],1ll*f[i][p-1][k1]*(f[p+1][j][k2]+g[p+1][j][k2][a[p]])%MOD*C[k1+k2][k1]%MOD); 
				}
			} 
			for(int k=1;k<=len;++k)
				for(int x=1;x<=n;++x) add(f[i][j][k],g[i][j][k-1][x]);
		}
	}
//	for(int i=1;i<=n;++i){
//		for(int j=i;j<=n;++j){
//			for(int k=0;k<=n;++k){
//				for(int x=1;x<=n;++x) cout<<i<<' '<<j<<' '<<k<<' '<<x<<' '<<g[i][j][k][x]<<endl;
//			}
//		}
//	} 
	int ans=0;
	for(int i=1;i<=n;++i) add(ans,f[1][n][i]);
	cout<<ans;
}
void Mianclr(){
	
} 
signed main(){
    ios::sync_with_stdio(false); 
    cin.tie(0); cout.tie(0);     
	
	//freopen("P10544_3.in","r",stdin);
	//freopen("P10544_3.op","w",stdout);
	
    int c,T=1; //cin>>T;
    while(T--){
    	Mian();
		Mianclr(); 
	}
}

方块消除

依旧区间 dp 好题。
要求的是一个最优化的形式,还是考虑最后一个删除的操作,设 \(f(l,r)\) 表示删除完 \([l,r]\) 中所有方块的最大价值和,\(g(l,r,k,x)\) 表示删除 \([l,r]\) 至只剩 \(x\) 个颜色 \(k\),但是这样 \(g\) 转移的复杂度就爆了。
\(k\) 这一维的限制写进 \(l,r\) 中,设 \(g(l,r,x)\) 表示删除 \([l,r]\) 至只剩 \(x\) 个颜色 \(a_l\),这里隐含了钦定 \(a_l\) 是最后一次操作被删的块之一。不难发现这样设计状态一定能包含到所有情况。
\(f\) 转移就是两个删完的并起来或者用 \(g\) 转移最后一次删除。\(g\) 的转移有点复杂,因为我们钦定了 \(a_l\) 最后被删,所以是前面一段 \(g\) 后面一段 \(f\) 或者当 \(a_l=a_r\) 时把它们并起来。
时间复杂度 \(O(m^3V)\),其中 \(V=\sum c_i\)

AC Code
// Written by Mi2uk1 
// Try Harder.

#include<bits/stdc++.h>
using namespace std;
#define i64 long long
#define i128 __int128 
#define ull unsigned long long
#define pii pair<int,int>
#define pll pair<long long,long long>
#define fir first
#define INF (1e9) 
#define sec second
#define pb push_back
#define eb emplace_back

const int N=50+9,M=1000+9;
const int MOD=1e9+7,base=251;
const double eps=1e-14;
inline void chkmax(int &x,int y){x=x<y?y:x;}
inline void chkmin(int &x,int y){x=x<y?x:y;}
inline void chkmax(i64 &x,i64 y){x=x<y?y:x;}
inline void chkmin(i64 &x,i64 y){x=x<y?x:y;}
inline int lowbit(int x){return x&(-x);}
int qpow(int a,int b,int p){
    int ret=1;
    while(b){
        if(b&1) ret=1ll*ret*a%p;
        a=1ll*a*a%p;
        b>>=1;
    }
    return ret; 
}

int n,a[N],c[N],f[N][N],g[N][N][M];
void Mian(){
	cin>>n;
	for(int i=1;i<=n;++i) cin>>a[i];
	for(int i=1;i<=n;++i) cin>>c[i];
	
	for(int i=1;i<=n;++i)
		for(int j=1;j<=n;++j)
			for(int x=1;x<=1000;++x) g[i][j][x]=-INF;
	for(int i=1;i<=n;++i) f[i][i]=c[i]*c[i],g[i][i][c[i]]=0;
	for(int len=2;len<=n;++len){
		for(int i=1,j;i+len-1<=n;++i){
			j=i+len-1;
			if(a[i]==a[j]){
				for(int x=c[j];x<=1000;++x)
					chkmax(g[i][j][x],g[i][j-1][x-c[j]]);
			} 
			for(int p=i;p<j;++p){
				for(int x=1;x<=1000;++x) 
					chkmax(g[i][j][x],g[i][p][x]+f[p+1][j]);
			}
			for(int p=i;p<j;++p)
				chkmax(f[i][j],f[i][p]+f[p+1][j]);
			for(int x=1;x<=1000;++x) 
				chkmax(f[i][j],g[i][j][x]+x*x);
		}
	}
	cout<<f[1][n];
}
void Mianclr(){
	
} 
signed main(){
    ios::sync_with_stdio(false); 
    cin.tie(0); cout.tie(0);     
	
	//freopen("P10544_3.in","r",stdin);
	//freopen("P10544_3.op","w",stdout);
	
    int c,T=1; //cin>>T;
    while(T--){
    	Mian();
		Mianclr(); 
	}
}

TEST_152

从左往右扫描线一遍操作序列,ODT 维护每一段最后一次操作的 id 和值,查询查 id 大于等于左端点的序列和即可。复杂度 \(O(n\log n)\)

AC Code
// Written by Mi2uk1 
// Try Harder.

#include<bits/stdc++.h>
using namespace std;
#define i64 long long
#define i128 __int128 
#define ull unsigned long long
#define pii pair<int,int>
#define pll pair<long long,long long>
#define fir first
#define INF (1e9) 
#define sec second
#define pb push_back
#define eb emplace_back

const int N=5e5+9,M=1000+9;
const int MOD=1e9+7,base=251;
const double eps=1e-14;
inline void chkmax(int &x,int y){x=x<y?y:x;}
inline void chkmin(int &x,int y){x=x<y?x:y;}
inline void chkmax(i64 &x,i64 y){x=x<y?y:x;}
inline void chkmin(i64 &x,i64 y){x=x<y?x:y;}
inline int lowbit(int x){return x&(-x);}
int qpow(int a,int b,int p){
    int ret=1;
    while(b){
        if(b&1) ret=1ll*ret*a%p;
        a=1ll*a*a%p;
        b>>=1;
    }
    return ret; 
}

#define int long long
int n,m,q,a[N],ql[N],qr[N],qv[N],ans[N];
struct BIT{
	int f[N];
	void modify(int u,int x){
		if(!u) return ; 
		for(;u<=n;u+=lowbit(u))
			f[u]+=x;
	}
	int query(int u){
		int ret=0;
		for(;u;u-=lowbit(u))
			ret+=f[u];
		return ret;
	}
} T;
struct ask{
	int l,r,id;
	bool operator<(const ask &res)const{
		return r<res.r;
	}
} h[N];
struct rg{
	mutable int l,r,v,t;
	bool operator<(const rg &res)const{
		return l<res.l; 
	}
};
set<rg> s;
auto split(int pos){
	auto it=s.lower_bound({pos,0,0,0});
	if(it->l==pos) return it;
	--it; int l=it->l,r=it->r,v=it->v,t=it->t;  
	s.erase(it); s.insert({l,pos-1,v,t});
	return s.insert({pos,r,v,t}).fir;
}
void assign(int L,int R,int v,int t){
	auto itr=split(R+1); auto itl=split(L);
	for(auto it=itl;it!=itr;++it) 
		T.modify(it->t,-1ll*((it->r)-(it->l)+1)*it->v);
	s.erase(itl,itr); 
	T.modify(t,(R-L+1)*v); s.insert({L,R,v,t});
} 
void Mian(){
	cin>>n>>m>>q;
	for(int i=1;i<=n;++i) cin>>ql[i]>>qr[i]>>qv[i];
	for(int i=1;i<=q;++i){
		cin>>h[i].l>>h[i].r;
		h[i].id=i;
	}
	sort(h+1,h+q+1);
	
	s.insert({0,m+1,0,0}); 
	for(int i=1,j=1;i<=n;++i){
		assign(ql[i],qr[i],qv[i],i); 
		while(j<=q && h[j].r==i){
			ans[h[j].id]=T.query(n)-T.query(h[j].l-1); 
			++j; 
		}
	}
	for(int i=1;i<=q;++i) cout<<ans[i]<<'\n';
}
void Mianclr(){
	
} 
signed main(){
    ios::sync_with_stdio(false); 
    cin.tie(0); cout.tie(0);     
	
	//freopen("P10544_3.in","r",stdin);
	//freopen("P10544_3.op","w",stdout);
	
    int c,T=1; //cin>>T;
    while(T--){
    	Mian();
		Mianclr();
	}
}

镜中的昆虫

神题。
先考虑单点修改怎么做,因为每次修改对 \(pre\) 数组影响是 \(O(1)\) 的,所以加一个时间维作三维偏序即可。
现在是区间推平,由颜色段均摊,所有修改对 \(pre\) 数组影响是 \(O(n+m)\) 的,那么跟单点一样暴力改就行了。
尤其注意 odt 的细节处理,以及空间(64MB)。

AC Code
// Written by Mi2uk1 
// Try Harder.

#include<bits/stdc++.h>
using namespace std;
#define i64 long long
#define i128 __int128 
#define ull unsigned long long
#define pii pair<int,int>
#define pll pair<long long,long long>
#define fir first
#define INF (1e9) 
#define sec second
#define pb push_back
#define eb emplace_back

const int N=1e5+9,M=2e5+9;
const int MOD=1e9+7,base=251;
const double eps=1e-14;
inline void chkmax(int &x,int y){x=x<y?y:x;}
inline void chkmin(int &x,int y){x=x<y?x:y;}
inline void chkmax(i64 &x,i64 y){x=x<y?y:x;}
inline void chkmin(i64 &x,i64 y){x=x<y?x:y;}
inline int lowbit(int x){return x&(-x);}
int qpow(int a,int b,int p){
    int ret=1;
    while(b){
        if(b&1) ret=1ll*ret*a%p;
        a=1ll*a*a%p;
        b>>=1;
    }
    return ret; 
}

int n,m,ans[N],lt,tot,qcnt,lst[N];
map<int,int> mp;
struct BIT{
	int f[N];
	void modify(int u,int x){ 
		for(;u<=n;u+=lowbit(u))
			f[u]+=x;
	}
	int query(int u){
		int ret=0;
		for(;u;u-=lowbit(u))
			ret+=f[u];
		return ret;
	}
} T;
struct ask{
	int op,x,y,z,coef,id;
} q[1020892],tmp[1020892]; 
bool cmp1(ask u,ask v){
	return u.x!=v.x?u.x<v.x:u.op<v.op;
}
bool cmp2(ask u,ask v){
	return u.y!=v.y?u.y<v.y:u.op<v.op;
}
void cdq(int l,int r){
	if(l==r) return ;
	int mid=l+r>>1;
	cdq(l,mid); cdq(mid+1,r);
	for(int i=mid+1,j=l;i<=r;++i){
		while(j<=mid && q[j].y<=q[i].y){
			if(!q[j].op) T.modify(q[j].z,q[j].coef);
			++j;
		}
		if(q[i].op) ans[q[i].id]+=q[i].coef*T.query(q[i].z);
	}
	for(int i=mid+1,j=l;i<=r;++i){
		while(j<=mid && q[j].y<=q[i].y){
			if(!q[j].op) T.modify(q[j].z,-q[j].coef);
			++j;
		}
	}
	int i=l,j=mid+1,k=l-1;
	while(i<=mid && j<=r){
		if(cmp2(q[i],q[j])) tmp[++k]=q[i++];
		else tmp[++k]=q[j++];
	}
	while(i<=mid) tmp[++k]=q[i++];
	while(j<=r) tmp[++k]=q[j++];
	for(int o=l;o<=r;++o) q[o]=tmp[o];
}
struct rg{
	mutable int l,r,v;
	bool operator<(const rg &res)const{
		return l<res.l; 
	}
};
set<rg> s,col[M];
auto ins(int l,int r,int v){
	col[v].insert({l,r,v}); 
	return s.insert({l,r,v}).fir;
}
void del(int l,int r,int v){
	col[v].erase({l,r,v}); 
	s.erase({l,r,v});
}
auto split(int pos){
	auto it=s.lower_bound({pos,0,0});
	if(it->l==pos) return it;
	--it; int l=it->l,r=it->r,v=it->v;
	del(l,r,v); ins(l,pos-1,v);
	return ins(pos,r,v);
}
int getpre(int pos){
	auto it=--s.upper_bound({pos,0,0});
	if(it->l<pos) return pos-1;
	auto it2=col[it->v].lower_bound({it->l,0,0});
	if(it2!=col[it->v].begin()) return (--it2)->r;
	return 0;
}
void assign(int L,int R,int v,int t){
	auto itr=split(R+1),itl=split(L);
	vector<int> tmp; vector<rg> dmp; 
	for(auto it=itl;it!=itr;++it){
		tmp.pb(it->l);
		auto it2=col[it->v].upper_bound({it->l,0,0});
		if(it2!=col[it->v].end()) tmp.pb(it2->l);
		dmp.pb({it->l,it->r,it->v});
	}
	auto itp=col[v].upper_bound({R,0,0}); 
	if(itp!=col[v].end()) tmp.pb(itp->l);
	sort(tmp.begin(),tmp.end());
    tmp.erase(unique(tmp.begin(),tmp.end()),tmp.end());

	for(auto u:tmp) q[++tot]={0,t,getpre(u),u,-1,0};
	for(auto i:dmp) del(i.l,i.r,i.v);
	ins(L,R,v);
	for(auto u:tmp) q[++tot]={0,t,getpre(u),u,1,0};
} 
void Mian(){
	cin>>n>>m;
	for(int i=1;i<=n;++i){
		int x; cin>>x;
		if(!mp[x]) mp[x]=++lt;
		x=mp[x];
		q[++tot]={0,0,lst[x],i,1,0};
		ins(i,i,x); 
		lst[x]=i;
	}
	
	s.insert({0,0,0}); s.insert({n+1,n+1,0});
	for(int i=1;i<=m;++i){
		int op; cin>>op;
		if(op==1){
			int l,r,x; cin>>l>>r>>x;
			if(!mp[x]) mp[x]=++lt;
			x=mp[x];
			assign(l,r,x,i);
		}
		if(op==2){
			int l,r; cin>>l>>r;
			q[++tot]={1,i,l-1,l-1,-1,++qcnt};
			q[++tot]={1,i,l-1,r,1,qcnt};
		}
	}	
	
	sort(q+1,q+tot+1,cmp1);
	cdq(1,tot);
	for(int i=1;i<=qcnt;++i) cout<<ans[i]<<'\n';
}
void Mianclr(){
	
} 
signed main(){
    ios::sync_with_stdio(false); 
    cin.tie(0); cout.tie(0);     
	
	//freopen("ftc1.in","r",stdin);
	//freopen("r.out","w",stdout);
	
    int c,T=1; //cin>>T;
    while(T--){
    	Mian();
		Mianclr();
	}
}

TEST_95

本质不同其实是值不同,那么直接对每种值 \(i\) 维护 \(F_i,G_i\) 表示第一次和最后一次出现的位置,这样不带修的求法就是二维偏序,加入修改后添加时间维作三维偏序即可。
注意重复计算,枚举顺序等各种细节。

AC Code
// Written by Mi2uk1 
// Try Harder.

#include<bits/stdc++.h>
using namespace std;
#define i64 long long
#define i128 __int128 
#define ull unsigned long long
#define pii pair<int,int>
#define pll pair<long long,long long>
#define fir first
#define INF (1e9) 
#define sec second
#define pb push_back
#define eb emplace_back

const int N=1e5+9,M=1e6+9;
const int MOD=1e9+7,base=251;
const double eps=1e-14;
inline void chkmax(int &x,int y){x=x<y?y:x;}
inline void chkmin(int &x,int y){x=x<y?x:y;}
inline void chkmax(i64 &x,i64 y){x=x<y?y:x;}
inline void chkmin(i64 &x,i64 y){x=x<y?x:y;}
inline int lowbit(int x){return x&(-x);}
int qpow(int a,int b,int p){
    int ret=1;
    while(b){
        if(b&1) ret=1ll*ret*a%p;
        a=1ll*a*a%p;
        b>>=1;
    }
    return ret; 
}

int n,m,a[N],tot,lt;
i64 ans[N];
struct BIT{
	int f[N];
	void modify(int u,int val){
		for(;u<=m+1;u+=lowbit(u))
			f[u]+=val; 
	} 
	int query(int u){ 
		int ret=0;
		for(;u;u-=lowbit(u))
			ret+=f[u];
		return ret;
	}
	int query(int L,int R){
		if(L>R) return 0;
		return query(R)-query(L-1);
	}
} T;
int F[N],G[N]; 
set<int> s[N]; 
struct node{
	int op,x,y,z,coef;
} q[M],tmp[M];
bool cmp1(node u,node v){
	return u.x!=v.x?u.x<v.x:u.op<v.op;
}
bool cmp2(node u,node v){
	return u.y!=v.y?u.y<v.y:u.op<v.op;
}
void cdq(int l,int r){
	if(l==r) return ;
	int mid=l+r>>1;
	cdq(l,mid); cdq(mid+1,r);
	
	for(int i=r,j=mid;i>=mid+1;--i){
		while(j>=l && q[j].y>q[i].y){
			if(q[j].op) T.modify(q[j].z,q[j].coef);
			--j;
		}
		if(!q[i].op) ans[q[i].z]+=q[i].coef*T.query(q[i].z);
	} 
	for(int i=r,j=mid;i>=mid+1;--i){
		while(j>=l && q[j].y>q[i].y){
			if(q[j].op) T.modify(q[j].z,-q[j].coef);
			--j;
		}
	} 
	for(int i=l,j=mid+1;i<=mid;++i){
		while(j<=r && q[j].y<q[i].y){
			if(!q[j].op) T.modify(q[j].z,q[j].coef);
			++j;
		}
		if(q[i].op) ans[q[i].z]+=q[i].coef*T.query(q[i].z);
	}
	for(int i=l,j=mid+1;i<=mid;++i){
		while(j<=r && q[j].y<q[i].y){
			if(!q[j].op) T.modify(q[j].z,-q[j].coef);
			++j;
		}
	}
	
	int i=l,j=mid+1,k=l-1;
	while(i<=mid && j<=r){
		if(cmp2(q[i],q[j])) tmp[++k]=q[i++];
		else tmp[++k]=q[j++];
	} 
	while(i<=mid) tmp[++k]=q[i++];
	while(j<=r) tmp[++k]=q[j++];
	for(int o=l;o<=r;++o) q[o]=tmp[o];
}
void upd(int col,int t){
	int L,R;
	if(s[col].empty()) L=n+1,R=0;
	else L=*s[col].begin(),R=*s[col].rbegin();
	if(L!=F[col]){
		q[++tot]={0,col,F[col],t,-1};
		q[++tot]={0,col,F[col]=L,t,1};
	}
	if(R!=G[col]){
		q[++tot]={1,col,G[col],t,-1};
		q[++tot]={1,col,G[col]=R,t,1};
	}
}
void Mian(){
	cin>>n;
	for(int i=1;i<=n;++i){
		cin>>a[i];
		s[a[i]].insert(i); 
	}
	for(int i=1;i<=n;++i){
		if(s[i].empty()) F[i]=n+1,G[i]=0;
		else F[i]=*s[i].begin(),G[i]=*s[i].rbegin();
		q[++tot]={0,i,F[i],1,1};
		q[++tot]={1,i,G[i],1,1};
	}
	cin>>m;
	for(int i=1;i<=m;++i){
		int x,y; cin>>x>>y;
		s[a[x]].erase(x);
		upd(a[x],i+1);
		s[y].insert(x); a[x]=y;
		upd(y,i+1);
	}
	sort(q+1,q+tot+1,cmp1);
	cdq(1,tot);
	for(int i=1;i<=m+1;++i){
		if(i>1) ans[i]+=ans[i-1];
		else ans[i]/=2;
		cout<<ans[i]<<'\n';
	}
}
void Mianclr(){
	
} 
signed main(){
    ios::sync_with_stdio(false); 
    cin.tie(0); cout.tie(0);     
	
	//freopen("ftc1.in","r",stdin);
	//freopen("r.out","w",stdout);
	
    int c,T=1; //cin>>T;
    while(T--){
    	Mian();
		Mianclr();
	}
}

异或运算

\(n\) 很小,对 \(Y\) 数组建可持久化 trie,对 \(X\) 直接暴力,每次以 \(O(n)\) 的代价确定答案的一个二进制位,总复杂度 \(O(qn\log V)\)

AC Code
// Written by Mi2uk1 
// Try Harder.

#include<bits/stdc++.h>
using namespace std;
#define i64 long long
#define i128 __int128 
#define ull unsigned long long
#define pii pair<int,int>
#define pll pair<long long,long long>
#define fir first
#define INF (1e9) 
#define sec second
#define pb push_back
#define eb emplace_back

const int N=3e5+9,M=1e7+9;
const int MOD=1e9+7,base=251;
const double eps=1e-14;
inline void chkmax(int &x,int y){x=x<y?y:x;}
inline void chkmin(int &x,int y){x=x<y?x:y;}
inline void chkmax(i64 &x,i64 y){x=x<y?y:x;}
inline void chkmin(i64 &x,i64 y){x=x<y?x:y;}
inline int lowbit(int x){return x&(-x);}
int qpow(int a,int b,int p){
    int ret=1;
    while(b){
        if(b&1) ret=1ll*ret*a%p;
        a=1ll*a*a%p;
        b>>=1;
    }
    return ret; 
}

int n,m,q,a[N],b[M],th[N][2]; 
int root[N],ch[M][2],tot,siz[M];
int copy(int u){
	++tot;
	ch[tot][0]=ch[u][0]; ch[tot][1]=ch[u][1];
	siz[tot]=siz[u];
	return tot;
} 
int ins(int u,int val){
	int now=copy(u),tmp=now;
	for(int j=31;j>=0;--j){
		int t=((val>>j)&1);
		now=ch[now][t]=copy(ch[u][t]);
		u=ch[u][t];
		siz[now]++; 
	}
	return tmp;
}
void Mian(){
	cin>>n>>m;
	for(int i=1;i<=n;++i) cin>>a[i];
	for(int i=1;i<=m;++i) cin>>b[i];
	for(int i=1;i<=m;++i) root[i]=ins(root[i-1],b[i]);
	
	cin>>q;
	while(q--){
		int l1,r1,l2,r2,k; cin>>l1>>r1>>l2>>r2>>k;
		i64 ans=0;
		for(int i=l1;i<=r1;++i){
			th[i][0]=root[l2-1];
			th[i][1]=root[r2];
		}
		for(int j=31;j>=0;--j){
			int cnt=0;
			for(int i=l1;i<=r1;++i){
				int t=(a[i]>>j)&1;
				cnt+=(siz[ch[th[i][1]][t^1]]-siz[ch[th[i][0]][t^1]]);
			}
			if(k<=cnt){
				ans|=(1<<j);
				for(int i=l1;i<=r1;++i){
					int t=(a[i]>>j)&1;
					th[i][0]=ch[th[i][0]][t^1];
					th[i][1]=ch[th[i][1]][t^1];
				}
			}else{
				k-=cnt;
				for(int i=l1;i<=r1;++i){
					int t=(a[i]>>j)&1;
					th[i][0]=ch[th[i][0]][t];
					th[i][1]=ch[th[i][1]][t];
				}
			}
		} 
		cout<<ans<<'\n';
	}
}
void Mianclr(){
	
} 
signed main(){
    ios::sync_with_stdio(false); 
    cin.tie(0); cout.tie(0);     
	
	//freopen("ftc1.in","r",stdin);
	//freopen("r.out","w",stdout);
	
    int c,T=1; //cin>>T;
    while(T--){
    	Mian();
		Mianclr();
	}
}

middle

二分答案,把大于等于 mid 的数看成 1,小于 mid 看成 -1,则 mid 合法等价于当前区间的和大于等于 0。预处理每种可能的答案对应的线段树,由于每次答案变大时只会有一个点从 1 变成 -1,主席树即可。

AC Code
// Written by Mi2uk1 
// Try Harder.

#include<bits/stdc++.h>
using namespace std;
#define i64 long long
#define i128 __int128 
#define ull unsigned long long
#define pii pair<int,int>
#define pll pair<long long,long long>
#define fir first
#define INF (1e9) 
#define sec second
#define pb push_back
#define eb emplace_back

const int N=20000+9,M=1e7+9;
const int MOD=1e9+7,base=251;
const double eps=1e-14;
inline void chkmax(int &x,int y){x=x<y?y:x;}
inline void chkmin(int &x,int y){x=x<y?x:y;}
inline void chkmax(i64 &x,i64 y){x=x<y?y:x;}
inline void chkmin(i64 &x,i64 y){x=x<y?x:y;}
inline int lowbit(int x){return x&(-x);}
int qpow(int a,int b,int p){
    int ret=1;
    while(b){
        if(b&1) ret=1ll*ret*a%p;
        a=1ll*a*a%p;
        b>>=1;
    }
    return ret; 
}

int n,q,a[N],id[N],root[N],tot;
bool cmp(int x,int y){
	return a[x]<a[y];
}
struct info{
	int lmx,rmx,sum;
};
info merge(info L,info R){
	info ret;
	ret.sum=L.sum+R.sum;
	ret.lmx=max(L.lmx,L.sum+R.lmx);
	ret.rmx=max(R.rmx,R.sum+L.rmx);
	return ret;
}
struct node{
	int ls,rs;
	info t;
} tre[N*60];
int copy(int u){
	++tot; tre[tot]=tre[u];
	return tot;
}
int build(int l,int r){
	int u=++tot;
	if(l==r){
		tre[u].ls=tre[u].rs=0;
		tre[u].t={1,1,1};
		return tot;
	}
	int mid=l+r>>1;
	tre[u].ls=build(l,mid);
	tre[u].rs=build(mid+1,r);
	tre[u].t=merge(tre[tre[u].ls].t,tre[tre[u].rs].t);
	return u; 
}
int modify(int u,int l,int r,int x){
	u=copy(u);
	if(l==r){
		tre[u].t={-1,-1,-1};
		return u;
	}
	int mid=l+r>>1;
	if(x<=mid) tre[u].ls=modify(tre[u].ls,l,mid,x);
	if(x>mid) tre[u].rs=modify(tre[u].rs,mid+1,r,x);
	tre[u].t=merge(tre[tre[u].ls].t,tre[tre[u].rs].t);
	return u;
}
info query(int u,int l,int r,int L,int R){
	if(L<=l && r<=R) return tre[u].t;
	int mid=l+r>>1;
	if(R<=mid) return query(tre[u].ls,l,mid,L,R);
	if(L>mid) return query(tre[u].rs,mid+1,r,L,R);
	return merge(query(tre[u].ls,l,mid,L,R),query(tre[u].rs,mid+1,r,L,R));
}
bool check(int v,int l1,int r1,int l2,int r2){
	int sum=query(root[v],1,n,l1,r1).rmx+query(root[v],1,n,l2,r2).lmx;
	if(r1<l2-1) sum+=query(root[v],1,n,r1+1,l2-1).sum;
	return sum>=0;
}
void Mian(){
	cin>>n;
	for(int i=1;i<=n;++i) cin>>a[i];
	for(int i=1;i<=n;++i) id[i]=i;
	sort(id+1,id+n+1,cmp);
	
	tot=0;
	root[1]=build(1,n); 
	for(int i=2;i<=n;++i)
		root[i]=modify(root[i-1],1,n,id[i-1]);
	cin>>q;
	int lstans=0;
	while(q--){
		int tmp[4]; for(int i=0;i<4;++i) cin>>tmp[i];
		for(int i=0;i<4;++i) (tmp[i]+=lstans)%=n;
		sort(tmp,tmp+4);
		int l1=tmp[0]+1,r1=tmp[1]+1,l2=tmp[2]+1,r2=tmp[3]+1;
		int L=1,R=n,res;
		while(L<=R){
			int mid=L+R>>1;
			if(check(mid,l1,r1,l2,r2)) L=(res=mid)+1;
			else R=mid-1;
		}
		cout<<(lstans=a[id[res]])<<'\n';
	}	
}
void Mianclr(){
	
} 
signed main(){
    ios::sync_with_stdio(false); 
    cin.tie(0); cout.tie(0);     
	
	//freopen("ftc1.in","r",stdin);
	//freopen("r.out","w",stdout);
	
    int c,T=1; //cin>>T;
    while(T--){
    	Mian();
		Mianclr();
	}
}

The Classic Problem

神题啊。
首先我们肯定想做 Dijkstra,这就需要支持大数的比较和形如 \(dis[y]=dis[x]+w\) 的更新。我们可以使用一些数据结构维护这个非常大的数,更新相当于把原来的数复制过来一份,然后做修改,启发我们使用可持久化。由于做加法的边权都是 \(2^x\) 的形式,所以我们用线段树维护这些大数的每一个二进制位,加上边权时相当于查询从某个位置开始的极长 \(1\) 连续段,区间推平为 \(0\) 和一个单点修改为 \(1\),这些在线段树上都是不难的。
总复杂度 \(O(n\log^2 n)\)

尤其注意数组不要开小了,\(x \leq 10^5\) 不代表你的距离范围也 \(\leq 2^{10^5}\)。。。

相信你也不想:

upd:
在具体实现上有两种方法,一种是经典的直接打区间推平的 tag,但是这里我们维护的是可持久化线段树,于是每次 pushdown 都需要新建节点,笔者这里为了防止重复新建浪费太多空间,还再打了一个孩子是否是新建节点的 tag,空间占用较大。
另一种是对于那些区间推平的操作直接把对应节点修改成 空节点 \(0\),每次 modify 进来的时候又会新建节点,空间占用相对较小,且也是正确的。

AC Code (ver. 1)
// Written by Mi2uk1 
// Try Harder.

#include<bits/stdc++.h>
using namespace std;
#define i64 long long
#define i128 __int128 
#define ull unsigned long long
#define pii pair<int,int>
#define pll pair<long long,long long>
#define fir first
#define INF (1e9) 
#define sec second
#define pb push_back
#define eb emplace_back

const int N=1e5+109,M=2e7+9;
const int MOD=1e9+7;
const double eps=1e-14;
inline void chkmax(int &x,int y){x=x<y?y:x;}
inline void chkmin(int &x,int y){x=x<y?x:y;}
inline void chkmax(i64 &x,i64 y){x=x<y?y:x;}
inline void chkmin(i64 &x,i64 y){x=x<y?x:y;}
inline int lowbit(int x){return x&(-x);}
int qpow(int a,int b,int p){
    int ret=1;
    while(b){
        if(b&1) ret=1ll*ret*a%p;
        a=1ll*a*a%p;
        b>>=1;
    }
    return ret; 
}

int n,m,up=(int)(1e5+102),s,t,root[N],tot,tmp;
int bs0=2,bs1=251,pw0[N],pw1[N];
struct node{
	int ls,rs;
	int sum,hs0,hs1;
	int tag,dn;
} tre[M];
void push_up(int u,int l,int r){
	int mid=l+r>>1;
	tre[u].sum=tre[tre[u].ls].sum+tre[tre[u].rs].sum;
	tre[u].hs0=(tre[tre[u].ls].hs0+tre[tre[u].rs].hs0)%MOD;
	tre[u].hs1=(tre[tre[u].ls].hs1+tre[tre[u].rs].hs1)%MOD;
}
int build(int l,int r,int x){
	int u=++tot;
	if(l==r){
		tre[u].sum=x;
		tre[u].hs0=pw0[l]*x;
		tre[u].hs1=pw1[l]*x;
		return u;
	} 
	int mid=l+r>>1;
	tre[u].ls=build(l,mid,x);
	tre[u].rs=build(mid+1,r,x);
	push_up(u,l,r);
	return u;
}
void push_down(int u,int l,int r){
	if(!tre[u].dn){
		tre[++tot]=tre[tre[u].ls]; tre[u].ls=tot;
		tre[++tot]=tre[tre[u].rs]; tre[u].rs=tot;
		tre[u].dn=1;
	}
	if(!tre[u].tag) return ;
	tre[tre[u].ls].sum=tre[tre[u].rs].sum=0;
	tre[tre[u].ls].hs0=tre[tre[u].rs].hs0=0;
	tre[tre[u].ls].hs1=tre[tre[u].rs].hs1=0;
	tre[tre[u].ls].dn=tre[tre[u].rs].dn=0;
	tre[tre[u].ls].tag=tre[tre[u].rs].tag=1;
	tre[u].tag=0;
}
void modify(int &u,int l,int r,int x){
	tre[++tot]=tre[u]; u=tot;
	tre[u].dn=0; 
	if(l==r) 
		return tre[u].sum=1,tre[u].hs0=pw0[l],tre[u].hs1=pw1[l],void();
	push_down(u,l,r);
	int mid=l+r>>1;
	if(x<=mid) modify(tre[u].ls,l,mid,x);
	if(x>mid) modify(tre[u].rs,mid+1,r,x);
	push_up(u,l,r);
}
void change(int &u,int l,int r,int L,int R){
	tre[++tot]=tre[u]; u=tot;
	tre[u].dn=0;
	if(L<=l && r<=R){
		tre[u].sum=tre[u].hs0=tre[u].hs1=0;
		tre[u].tag=1;
		return ;
	}
	int mid=l+r>>1;
	push_down(u,l,r);
	if(L<=mid) change(tre[u].ls,l,mid,L,R);
	if(R>mid) change(tre[u].rs,mid+1,r,L,R); 
	push_up(u,l,r);
}
int query(int u,int l,int r,int L,int R){
	if(L<=l && r<=R) return tre[u].sum;
	int mid=l+r>>1,ret=0;
	push_down(u,l,r);
	if(L<=mid) ret+=query(tre[u].ls,l,mid,L,R);
	if(R>mid) ret+=query(tre[u].rs,mid+1,r,L,R);
	return ret; 
}
int get(int u,int l,int r,int x){
	if(l==r) return tre[u].sum;
	push_down(u,l,r); 
	int mid=l+r>>1;
	if(x>mid) return get(tre[u].rs,mid+1,r,x);
	if(query(tre[u].ls,l,mid,x,mid)==mid-x+1)
		return mid-x+1+get(tre[u].rs,mid+1,r,mid+1);
	return get(tre[u].ls,l,mid,x);
} 
int add(int u,int x){
	int len=get(u,0,up,x);
	modify(u,0,up,x+len);
	if(len) change(u,0,up,x,x+len-1);
	return u;
}
bool chk(int x,int y){
	return tre[x].hs0==tre[y].hs0 && tre[x].hs1==tre[y].hs1;
}
bool cmp(int x,int y,int l,int r){
	if(l==r) return tre[x].sum<=tre[y].sum;
	push_down(x,l,r); push_down(y,l,r);
	int mid=l+r>>1;
	if(chk(tre[x].rs,tre[y].rs)) 
		return cmp(tre[x].ls,tre[y].ls,l,mid);
	else return cmp(tre[x].rs,tre[y].rs,mid+1,r);
}

struct LH{
	int ls[N<<2],rs[N<<2];
	int pu[N<<2],rt[N<<2];
	int dist[N<<2],idx,root,siz;
	int merge(int x,int y){
		if(!x || !y) return (x|y);
		if(cmp(rt[y],rt[x],0,up)) swap(x,y);
		rs[x]=merge(rs[x],y);
		if(dist[rs[x]]>dist[ls[x]]) swap(ls[x],rs[x]);
		dist[x]=dist[ls[x]]+1;
		return x; 
	}
	void push(int iu,int irt){
		++siz;
		pu[++idx]=iu,rt[idx]=irt;
		root=merge(root,idx);
	}
	int top(){return pu[root];} 
	void pop(){--siz; root=merge(ls[root],rs[root]);}
	bool empty(){return (siz==0);}
} q;

bool vis[N];
int fr[N],st[N],tp; 
vector<pii> G[N];
void Dijkstra(){
	int mx=build(0,up,1),mi=build(0,up,0);
	for(int i=1;i<=n;++i) root[i]=mx;
	root[s]=mi;
	q.push(s,root[s]);
	while(!q.empty()){
		int u=q.top(); q.pop();
		if(vis[u]) continue;
		vis[u]=1;
		for(auto y:G[u]){
			int v=y.fir,w=y.sec;
			if(vis[v]) continue;
			int tmp=add(root[u],w);
			if(!cmp(root[v],tmp,0,up)){
				root[v]=tmp; fr[v]=u;
				q.push(v,root[v]);
			} 
		} 
	}
	if(!vis[t]) return cout<<-1,void();
	cout<<tre[root[t]].hs0<<'\n'; 
	for(int i=t;i;i=fr[i]) st[++tp]=i;
	cout<<tp<<'\n';
	while(tp){
		cout<<st[tp]<<' ';
		--tp;
	}
}
void Mian(){
	pw0[0]=pw1[0]=1;
	for(int i=1;i<=up;++i) 
		pw0[i]=1ll*pw0[i-1]*bs0%MOD,pw1[i]=1ll*pw1[i-1]*bs1%MOD;
	
	cin>>n>>m;
	for(int i=1;i<=m;++i){
		int u,v,x; cin>>u>>v>>x;
		G[u].pb({v,x});
		G[v].pb({u,x});
	} cin>>s>>t; 
	Dijkstra(); 
}
void Mianclr(){
	
} 
signed main(){
    ios::sync_with_stdio(false); 
    cin.tie(0); cout.tie(0);     
	
	//freopen("ftc1.in","r",stdin);
	//freopen("r.out","w",stdout);
	
    int c,T=1; //cin>>T;
    while(T--){
    	Mian();
		Mianclr();
	}
}
AC Code (ver. 2)
// Written by Mi2uk1 
// Try Harder.

#include<bits/stdc++.h>
using namespace std;
#define i64 long long
#define i128 __int128 
#define ull unsigned long long
#define pii pair<int,int>
#define pll pair<long long,long long>
#define fir first
#define INF (1e9) 
#define sec second
#define pb push_back
#define eb emplace_back

const int N=1e5+109,M=5e6+9;
const int MOD=1e9+7;
const double eps=1e-14;
inline void chkmax(int &x,int y){x=x<y?y:x;}
inline void chkmin(int &x,int y){x=x<y?x:y;}
inline void chkmax(i64 &x,i64 y){x=x<y?y:x;}
inline void chkmin(i64 &x,i64 y){x=x<y?x:y;}
inline int lowbit(int x){return x&(-x);}
int qpow(int a,int b,int p){
    int ret=1;
    while(b){
        if(b&1) ret=1ll*ret*a%p;
        a=1ll*a*a%p;
        b>>=1;
    }
    return ret; 
}

int n,m,up=(int)(1e5+100),s,t,root[N],tot,tmp;
int bs0=2,bs1=251,pw0[N],pw1[N];
struct node{
	int ls,rs;
	int sum,hs0,hs1;
} tre[M];
void push_up(int u,int l,int r){
	int mid=l+r>>1;
	tre[u].sum=tre[tre[u].ls].sum+tre[tre[u].rs].sum;
	tre[u].hs0=(tre[tre[u].ls].hs0+tre[tre[u].rs].hs0)%MOD;
	tre[u].hs1=(tre[tre[u].ls].hs1+tre[tre[u].rs].hs1)%MOD;
}
int build(int l,int r,int x){
	int u=++tot;
	if(l==r){
		tre[u].sum=x;
		tre[u].hs0=pw0[l]*x;
		tre[u].hs1=pw1[l]*x;
		return u;
	} 
	int mid=l+r>>1;
	tre[u].ls=build(l,mid,x);
	tre[u].rs=build(mid+1,r,x);
	push_up(u,l,r);
	return u;
}
void modify(int &u,int l,int r,int x){
	tre[++tot]=tre[u]; u=tot;
	if(l==r) 
		return tre[u].sum=1,tre[u].hs0=pw0[l],tre[u].hs1=pw1[l],void();
	int mid=l+r>>1;
	if(x<=mid) modify(tre[u].ls,l,mid,x);
	if(x>mid) modify(tre[u].rs,mid+1,r,x);
	push_up(u,l,r);
}
void change(int &u,int l,int r,int L,int R){
	if(L<=l && r<=R) return u=0,void();
	tre[++tot]=tre[u]; u=tot;
	int mid=l+r>>1;
	if(L<=mid) change(tre[u].ls,l,mid,L,R);
	if(R>mid) change(tre[u].rs,mid+1,r,L,R); 
	push_up(u,l,r);
}
int query(int u,int l,int r,int L,int R){
	if(L<=l && r<=R) return tre[u].sum;
	int mid=l+r>>1,ret=0;
	if(L<=mid) ret+=query(tre[u].ls,l,mid,L,R);
	if(R>mid) ret+=query(tre[u].rs,mid+1,r,L,R);
	return ret; 
}
int get(int u,int l,int r,int x){
	if(l==r) return tre[u].sum; 
	int mid=l+r>>1;
	if(x>mid) return get(tre[u].rs,mid+1,r,x);
	if(query(tre[u].ls,l,mid,x,mid)==mid-x+1)
		return mid-x+1+get(tre[u].rs,mid+1,r,mid+1);
	return get(tre[u].ls,l,mid,x);
} 
int add(int u,int x){
	int len=get(u,0,up,x);
	modify(u,0,up,x+len);
	if(len) change(u,0,up,x,x+len-1);
	return u;
}
bool chk(int x,int y){
	return tre[x].hs0==tre[y].hs0 && tre[x].hs1==tre[y].hs1;
}
bool cmp(int x,int y,int l,int r){
	if(l==r) return tre[x].sum<=tre[y].sum;
	int mid=l+r>>1;
	if(chk(tre[x].rs,tre[y].rs)) 
		return cmp(tre[x].ls,tre[y].ls,l,mid);
	else return cmp(tre[x].rs,tre[y].rs,mid+1,r);
}

struct LH{
	int ls[N<<2],rs[N<<2];
	int pu[N<<2],rt[N<<2];
	int dist[N<<2],idx,root,siz;
	int merge(int x,int y){
		if(!x || !y) return (x|y);
		if(cmp(rt[y],rt[x],0,up)) swap(x,y);
		rs[x]=merge(rs[x],y);
		if(dist[rs[x]]>dist[ls[x]]) swap(ls[x],rs[x]);
		dist[x]=dist[ls[x]]+1;
		return x; 
	}
	void push(int iu,int irt){
		++siz;
		pu[++idx]=iu,rt[idx]=irt;
		root=merge(root,idx);
	}
	int top(){return pu[root];} 
	void pop(){--siz; root=merge(ls[root],rs[root]);}
	bool empty(){return (siz==0);}
} q;

bool vis[N];
int fr[N],st[N],tp; 
vector<pii> G[N];
void Dijkstra(){
	int mx=build(0,up,1),mi=build(0,up,0);
	for(int i=1;i<=n;++i) root[i]=mx;
	root[s]=mi;
	q.push(s,root[s]);
	while(!q.empty()){
		int u=q.top(); q.pop();
		if(vis[u]) continue;
		vis[u]=1;
		for(auto y:G[u]){
			int v=y.fir,w=y.sec;
			if(vis[v]) continue;
			int tmp=add(root[u],w);
			if(!cmp(root[v],tmp,0,up)){
				root[v]=tmp; fr[v]=u;
				q.push(v,root[v]);
			} 
		} 
	}
	if(!vis[t]) return cout<<-1,void();
	cout<<tre[root[t]].hs0<<'\n'; 
	for(int i=t;i;i=fr[i]) st[++tp]=i;
	cout<<tp<<'\n';
	while(tp){
		cout<<st[tp]<<' ';
		--tp;
	}
}
void Mian(){
	pw0[0]=pw1[0]=1;
	for(int i=1;i<=up;++i) 
		pw0[i]=1ll*pw0[i-1]*bs0%MOD,pw1[i]=1ll*pw1[i-1]*bs1%MOD;
	
	cin>>n>>m;
	for(int i=1;i<=m;++i){
		int u,v,x; cin>>u>>v>>x;
		G[u].pb({v,x});
		G[v].pb({u,x});
	} cin>>s>>t; 
	Dijkstra(); 
}
void Mianclr(){
	
} 
signed main(){
    ios::sync_with_stdio(false); 
    cin.tie(0); cout.tie(0);     
	
	//freopen("ftc1.in","r",stdin);
	//freopen("r.out","w",stdout);
	
    int c,T=1; //cin>>T;
    while(T--){
    	Mian();
		Mianclr();
	}
}

森林

\(k\) 小问题一般在树上建主席树,这里显然没有什么优化空间。那么我们连边的时候就必定要重构两个连通块之一的所有信息,这启发我们使用启发式合并。这样每个点至多被重构 \(\log n\) 次,总复杂度 \(O(n\log^2 n)\)

AC Code
// Written by Mi2uk1 
// Try Harder.

#include<bits/stdc++.h>
using namespace std;
#define i64 long long
#define i128 __int128 
#define ull unsigned long long
#define pii pair<int,int>
#define pll pair<long long,long long>
#define fir first
#define INF (1e9) 
#define sec second
#define pb push_back
#define eb emplace_back

const int N=8e4+9,M=5e6+9;
const int MOD=1e9+7;
const double eps=1e-14;
inline void chkmax(int &x,int y){x=x<y?y:x;}
inline void chkmin(int &x,int y){x=x<y?x:y;}
inline void chkmax(i64 &x,i64 y){x=x<y?y:x;}
inline void chkmin(i64 &x,i64 y){x=x<y?x:y;}
inline int lowbit(int x){return x&(-x);}
int qpow(int a,int b,int p){
    int ret=1;
    while(b){
        if(b&1) ret=1ll*ret*a%p;
        a=1ll*a*a%p;
        b>>=1;
    }
    return ret; 
}

int n,m,q,a[N],b[N],up,tot,root[N];
struct node{
	int ls,rs,sum;
} tre[N*400];
void push_up(int u){
	tre[u].sum=tre[tre[u].ls].sum+tre[tre[u].rs].sum;
}
int build(int l,int r){
	int u=++tot;
	if(l==r){
		tre[u].sum=0;
		return u;
	}
	int mid=l+r>>1;
	tre[u].ls=build(l,mid);
	tre[u].rs=build(mid+1,r);
	push_up(u);
	return u;
} 
void modify(int &u,int l,int r,int x){
	tre[++tot]=tre[u]; u=tot;
	if(l==r) return tre[u].sum++,void();
	int mid=l+r>>1;
	if(x<=mid) modify(tre[u].ls,l,mid,x);
	if(x>mid) modify(tre[u].rs,mid+1,r,x);
	push_up(u); 
}
int query(int a1,int a2,int b1,int b2,int l,int r,int k){
	if(l==r) return l;
	int tmp=tre[tre[a1].ls].sum+tre[tre[a2].ls].sum
		   -tre[tre[b1].ls].sum-tre[tre[b2].ls].sum,mid=l+r>>1;
	if(k<=tmp) 
		return query(tre[a1].ls,tre[a2].ls,tre[b1].ls,tre[b2].ls,l,mid,k);
	return query(tre[a1].rs,tre[a2].rs,tre[b1].rs,tre[b2].rs,mid+1,r,k-tmp);
}
int fa[N],siz[N],dep[N],st[N][29]; 
bool vis[N];
vector<int> G[N];
int find(int u){
	return fa[u]==u?u:fa[u]=find(fa[u]);
}
void dfs(int u,int ft){
	vis[u]=1;
	st[u][0]=ft; fa[u]=ft; siz[u]=1; 
	for(int j=1;j<=19;++j) st[u][j]=st[st[u][j-1]][j-1];
	dep[u]=dep[ft]+1;
	root[u]=root[ft];
	modify(root[u],1,up,a[u]); 
	for(auto v:G[u]){
		if(v==ft) continue;
		dfs(v,u); siz[u]+=siz[v];
	}
}
int lca(int u,int v){
	if(dep[u]>dep[v]) swap(u,v);
	for(int j=19;j>=0;--j)
		if(dep[st[v][j]]>=dep[u])
			v=st[v][j];
	if(u==v) return u;
	for(int j=19;j>=0;--j)
		if(st[u][j]!=st[v][j])
			u=st[u][j],v=st[v][j];
	return st[u][0];
}
void Mian(){
	cin>>n>>m>>q;
	for(int i=1;i<=n;++i) cin>>a[i];
	for(int i=1;i<=n;++i) b[i]=a[i];
	sort(b+1,b+n+1);
	up=unique(b+1,b+n+1)-b-1;
	for(int i=1;i<=n;++i) 
		a[i]=lower_bound(b+1,b+up+1,a[i])-b;
	
	root[0]=build(1,up); 
	for(int i=1;i<=m;++i){
		int u,v; cin>>u>>v;
		G[u].pb(v);
		G[v].pb(u); 
	} 
	for(int i=1;i<=n;++i) if(!vis[i]){
		dfs(i,0);
		fa[i]=i;
	} 
	
	int lstans=0;
	while(q--){
		char opt; cin>>opt;
		if(opt=='Q'){
			int x,y,k; cin>>x>>y>>k;
			x^=lstans,y^=lstans,k^=lstans;
			int fu=lca(x,y);
			cout<<(lstans=b[query(root[x],root[y],root[fu],root[st[fu][0]],1,up,k)])<<'\n';	 
		} 
		if(opt=='L'){
			int x,y; cin>>x>>y;
			x^=lstans,y^=lstans;
			G[x].pb(y); G[y].pb(x);
			int gx=find(x),gy=find(y);
			if(siz[gx]>siz[gy]) swap(x,y),swap(gx,gy);
			dfs(x,y); siz[y]+=siz[x]; 
		}
	}
}
void Mianclr(){
	
} 
signed main(){
    ios::sync_with_stdio(false); 
    cin.tie(0); cout.tie(0);     
	
	//freopen("ftc1.in","r",stdin);
	//freopen("r.out","w",stdout);
	
    int c,T=1; cin>>c;
    while(T--){
    	Mian();
		Mianclr();
	}
}

Unknown

二进制分组,固定一个向量的叉乘可以写成一个直线的形式,所以对于每一个块维护一个上凸包,二分用直线去切这个凸包就能做到单块 \(O(\log n)\) 查询。
但带删的话由于合并的复杂度是均摊的,可以被卡爆。这里一个很厉害的想法是当我们有多个长度为 \(2^k\) 的区间的时候再去合并其中两个为 \(2^{k+1}\) 长的区间,即总是保留一些长度更小的区间,这样想要对一个 \(2^k\) 的区间有影响(使其需要重构)的话,也至少需要 \(O(2^k)\) 次删除操作,时间复杂度就是正确的。

写成线段树形式的话注意一下最左侧一列是没有前继节点的,总复杂度两只 \(\log\)

但是有人交 hack 把我空间卡爆了,呜呜。

并没有 AC Code
// Written by Mi2uk1 
// Try Harder.

#include<bits/stdc++.h>
using namespace std;
#define i64 long long
#define i128 __int128 
#define ull unsigned long long
#define pii pair<int,int>
#define pll pair<long long,long long>
#define fir first
#define INF (2e18) 
#define sec second
#define pb push_back
#define eb emplace_back

const int N=3e5+9,M=5e6+9;
const int MOD=998244353;
const double eps=1e-14;
inline void chkmax(int &x,int y){x=x<y?y:x;}
inline void chkmin(int &x,int y){x=x<y?x:y;}
inline void chkmax(i64 &x,i64 y){x=x<y?y:x;}
inline void chkmin(i64 &x,i64 y){x=x<y?x:y;}
inline int lowbit(int x){return x&(-x);}
int qpow(int a,int b,int p){
    int ret=1;
    while(b){
        if(b&1) ret=1ll*ret*a%p;
        a=1ll*a*a%p;
        b>>=1;
    }
    return ret; 
}

int n,m,up,ans,si; 
struct ver{
	int x,y;
	inline ver operator-(ver res)const{
		return {x-res.x,y-res.y}; 
	} 
	inline i64 operator*(ver res)const{
		return (i64)x*res.y-(i64)res.x*y; 
	} 
};
bitset<(1<<20|1)> dn;
vector<ver> v[1<<20|1],tmp;
i64 get(int u,ver x){
	int L=0,R=v[u].size(),res=0;
	while(L+1<R){
		int mid=L+R>>1;
		if(x*v[u][mid]>=x*v[u][mid-1]) L=(res=mid);
		else R=mid; 
	} 
	return x*v[u][res];
}
void merge(int u,int ls,int rs){
	v[u].clear(); tmp.clear();
	int i=0,j=0;
	while(i<v[ls].size() && j<v[rs].size()){
		if(v[ls][i].x<v[rs][j].x) tmp.pb(v[ls][i++]);
		else if(v[ls][i].x>v[rs][j].x) tmp.pb(v[rs][j++]);
		else{
			if(v[ls][i].y>=v[rs][j].y) tmp.pb(v[ls][i]);
			else tmp.pb(v[rs][j]);
			++i; ++j;
		}
	}
	while(i<v[ls].size()) tmp.pb(v[ls][i++]);
	while(j<v[rs].size()) tmp.pb(v[rs][j++]);
	for(auto i:tmp){
		while(v[u].size()>=2 && 
			 (i-v[u].back())*(v[u].back()-v[u][v[u].size()-2])<=0ll) v[u].pop_back();
		v[u].pb(i);
	} 
}
void ins(int u,int l,int r,int pos,ver x){
	if(l==r){
		chkmax(si,u); 
		v[u].pb(x); dn[u]=1;
		return ;
	}
	int mid=l+r>>1;
	if(pos<=mid) ins(u<<1,l,mid,pos,x);
	if(pos>mid) ins(u<<1|1,mid+1,r,pos,x);
	if(u!=lowbit(u) && pos==r && !dn[u-1])
		merge(u-1,(u-1)<<1,(u-1)<<1|1),dn[u-1]=1;
}
void del(int u,int l,int r,int pos){
	dn[u]=0; v[u].clear();
	if(l==r){
		chkmax(si,u);
		return ;
	} 
	int mid=l+r>>1;
	if(pos<=mid) del(u<<1,l,mid,pos);
	if(pos>mid) del(u<<1|1,mid+1,r,pos);
}
i64 qry(int u,int l,int r,int L,int R,ver x){
	if(L<=l && r<=R && dn[u]) return get(u,x); 
	if(l==r) return -INF;
	int mid=l+r>>1; i64 ret=-INF;
	if(L<=mid) chkmax(ret,qry(u<<1,l,mid,L,R,x));
	if(R>mid) chkmax(ret,qry(u<<1|1,mid+1,r,L,R,x));
	return ret;
}
void Mian(){
	up=(1<<19);
	while(cin>>m && m){
		dn.reset();
		for(int i=1;i<=si;++i) v[i].clear();
		si=ans=n=0;
		for(int i=1;i<=m;++i){
			int opt; cin>>opt;
			if(opt==1){
				int x,y; cin>>x>>y;
				ins(1,1,up,++n,{x,y}); 
			} 
			if(opt==2) del(1,1,up,n),--n;
			if(opt==3){
				int l,r,x,y; cin>>l>>r>>x>>y;
				ans^=((qry(1,1,up,l,r,{x,y})%MOD+MOD)%MOD);
			} 
		} cout<<ans<<'\n';
	}
}
void Mianclr(){
	
} 
signed main(){
    ios::sync_with_stdio(false); 
    cin.tie(0); cout.tie(0);     
	
	//freopen("ftc1.in","r",stdin);
	//freopen("r.out","w",stdout);
	
    int c,T=1; cin>>c;
    while(T--){
    	Mian();
		Mianclr();
	}
}

德莉莎世界第一可爱

四维偏序。重复元素钦定一个顺序即可,复杂度 \(O(n\log^3 n)\)

AC Code
// Written by Mi2uk1 
// Try Harder.

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define i28 __int128 
#define ull unsigned long long
#define pii pair<int,int>
#define pll pair<long long,long long>
#define fir first
#define INF (1e18) 
#define sec second
#define pb push_back
#define eb emplace_back

const int N=50000+9,M=5e6+9;
const int MOD=998244353;
const double eps=1e-14;
inline void chkmax(int &x,int y){x=x<y?y:x;}
inline void chkmin(int &x,int y){x=x<y?x:y;}
inline void chkmax(ll &x,ll y){x=x<y?y:x;}
inline void chkmin(ll &x,ll y){x=x<y?x:y;}
inline int lowbit(int x){return x&(-x);}
int qpow(int a,int b,int p){
    int ret=1;
    while(b){
        if(b&1) ret=1ll*ret*a%p;
        a=1ll*a*a%p;
        b>>=1;
    }
    return ret; 
}

struct BIT{
	ll n,f[N];
	void init(){for(int i=1;i<=n;++i) f[i]=-INF;}
	void modify(int u,ll val){
		for(;u<=n;u+=lowbit(u))
			chkmax(f[u],val);
	} 
	ll query(int u){
		ll ret=-INF;
		for(;u;u-=lowbit(u))
			chkmax(ret,f[u]);
		return ret;
	}
	void clr(int u){
		for(;u<=n;u+=lowbit(u))
			f[u]=-INF; 
	}
} T;

int n,h[N],tot;
ll dp[N];
struct node{
	int t,x,y,z;
	int tag,id,w; 
} a[N],b[N],c[N]; 
bool cmp1(node u,node v){
	if(u.t!=v.t) return u.t<v.t;
	if(u.x!=v.x) return u.x<v.x;
	if(u.y!=v.y) return u.y<v.y;
	if(u.z!=v.z) return u.z<v.z;
	return u.id<v.id;
}
bool cmp2(node u,node v){
	if(u.x!=v.x) return u.x<v.x;
	if(u.y!=v.y) return u.y<v.y;
	if(u.z!=v.z) return u.z<v.z;
	if(u.tag!=v.tag) return u.tag<v.tag;
	return u.id<v.id;
}
bool cmp3(node u,node v){
	if(u.y!=v.y) return u.y<v.y;
	if(u.z!=v.z) return u.z<v.z;
	if(u.tag!=v.tag) return u.tag<v.tag;
	if(u.x!=v.x) return u.x<v.x;
	return u.id<v.id;
}
void cdq2(int l,int r){
	if(l==r) return ;
	int mid=l+r>>1;
	cdq2(l,mid);
	for(int i=l;i<=r;++i) c[i]=b[i];
	sort(c+l,c+mid+1,cmp3); sort(c+mid+1,c+r+1,cmp3);
	for(int i=mid+1,j=l;i<=r;++i){
		while(j<=mid && c[j].y<=c[i].y){
			if(!c[j].tag) T.modify(c[j].z,dp[c[j].id]);
			++j;
		}
		if(c[i].tag) chkmax(dp[c[i].id],T.query(c[i].z)+c[i].w);
	}  
	for(int i=mid+1,j=l;i<=r;++i){
		while(j<=mid && c[j].y<=c[i].y){
			if(!c[j].tag) T.clr(c[j].z);
			++j;
		}
	}
	cdq2(mid+1,r);
}
void cdq1(int l,int r){
	if(l==r) return ;
	int mid=l+r>>1;
	cdq1(l,mid);
	
	for(int i=l;i<=r;++i) b[i]=a[i];
	for(int i=l;i<=mid;++i) b[i].tag=0;
	for(int i=mid+1;i<=r;++i) b[i].tag=1;
	sort(b+l,b+r+1,cmp2);
	cdq2(l,r);
	cdq1(mid+1,r);
}
void Mian(){
	cin>>n;
	for(int i=1;i<=n;++i) 
		cin>>a[i].t>>a[i].x>>a[i].y>>a[i].z>>a[i].w,a[i].id=i;
	for(int i=1;i<=n;++i) h[i]=a[i].z;
	sort(h+1,h+n+1); tot=unique(h+1,h+n+1)-h-1;
	for(int i=1;i<=n;++i) a[i].z=lower_bound(h+1,h+tot+1,a[i].z)-h;
	
	T.n=tot; T.init();
	for(int i=1;i<=n;++i) dp[i]=a[i].w;
	sort(a+1,a+n+1,cmp1);
	cdq1(1,n);
	
	ll ans=-INF; 
	for(int i=1;i<=n;++i) chkmax(ans,dp[i]); 
	cout<<ans; 
}
void Mianclr(){
	
} 
signed main(){
    ios::sync_with_stdio(false); 
    cin.tie(0); cout.tie(0);     
	
	//freopen("ftc1.in","r",stdin);
	//freopen("r.out","w",stdout);
	
    int c,T=1; //cin>>c;
    while(T--){
    	Mian();
		Mianclr();
	}
}

付公主的背包

经典题。作为 gf 入门练习还是不错的。

设体积为 \(v\) 的物品对应的形式幂级数为 \(F_v(x)=\sum_{k \geq 0} x^{vk}=\frac{1}{1-x^v}\),答案就是所有的 \(F_{v_i}(x)\) 卷积后 \(1\sim m\) 项的系数。

直接做时间复杂度爆炸,设答案为 \(Ans(x)=\prod F_{v_i}(x)\),我们希望求 \(\ln\) 后可以把后边的乘法变成加法以简单的运算。

即推导 \(G(x)=\ln F_v(x)\)

\[G'(x)=\frac{F_v'(x)}{F_v(x)}=(1-x^v)\sum_{k\geq0} v\times k\times x^{vk-1}=(1-x^v)\sum_{k\geq1} v\times k\times x^{vk-1} \]

\[=\sum_{k\geq1} v\times \left (k-(k-1) \right ) \times x^{vk-1}=\sum_{k\geq1}vx^{vk-1} \]

即有 $$G(x)=\int G'(x)=\sum_{k\geq 1} \frac{1}{k} x^{vk}$$

对每个不同的 \(v\) 累计最后 \(\exp\) 一下就得到了 \(Ans(x)\)

AC Code
// Written by Mi2uk1 
// Try Harder.

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define i28 __int128 
#define ull unsigned long long
#define pii pair<int,int>
#define pll pair<long long,long long>
#define fir first
#define INF (1e18) 
#define sec second
#define pb push_back
#define eb emplace_back

const int N=4e5+9,M=5e6+9;
const int MOD=998244353;
const double eps=1e-14;
inline void chkmax(int &x,int y){x=x<y?y:x;}
inline void chkmin(int &x,int y){x=x<y?x:y;}
inline void chkmax(ll &x,ll y){x=x<y?y:x;}
inline void chkmin(ll &x,ll y){x=x<y?x:y;}
inline int lowbit(int x){return x&(-x);}
int qpow(int a,int b,int p){
    int ret=1;
    while(b){
        if(b&1) ret=1ll*ret*a%p;
        a=1ll*a*a%p;
        b>>=1;
    }
    return ret; 
}


#define int long long 
const int G=3,INVG=332748118; 
int f[N],g[N],trs[N];
void NTT(int *f,bool flg,int limit){
    for(int i=0;i<limit;++i)
		trs[i]=(trs[i>>1]>>1|((i&1)?limit>>1:0));
	for(int i=0;i<limit;++i) 
		if(i<trs[i]) swap(f[i],f[trs[i]]);
	for(int p=2;p<=limit;p<<=1){
		int len=(p>>1);
		int wn=qpow(flg?INVG:G,(MOD-1)/p,MOD);
		for(int k=0;k<limit;k+=p){
			int buf=1;
			for(int i=k;i<k+len;++i){
				int tmp=f[i+len]*buf%MOD;
				f[i+len]=(f[i]-tmp+MOD)%MOD;
				f[i]=(f[i]+tmp)%MOD;
				buf=buf*wn%MOD;
			}
		}
	} 
	if(flg){
		int inv=qpow(limit,MOD-2,MOD);
		for(int i=0;i<limit;++i) f[i]=f[i]*inv%MOD; 
	}
}
void INV(int *f,int *g,int n){
	static int w[N];
	if(n==1){
		g[0]=qpow(f[0],MOD-2,MOD);
		return ;
	}
	INV(f,g,(n+1)>>1);
	int limit=1;
	while(limit<(n<<1)) limit<<=1;
	for(int i=0;i<limit;++i) w[i]=f[i];
	for(int i=n;i<limit;++i) w[i]=0;
	for(int i=(n+1)>>1;i<limit;++i) g[i]=0;
	NTT(w,0,limit); NTT(g,0,limit);
	for(int i=0;i<limit;++i)
		g[i]=(2*g[i]%MOD-g[i]*g[i]%MOD*w[i]%MOD+MOD)%MOD; 
	NTT(g,1,limit);
	for(int i=n;i<limit;++i) g[i]=0;
}
void mul(int *f,int *g,int *h,int n,int m){
	int limit=1;
	while(limit<(n+m)) limit<<=1;
	static int _f[N],_g[N];
	for(int i=0;i<n;++i) _f[i]=f[i];
	for(int i=0;i<m;++i) _g[i]=g[i];
	for(int i=n;i<limit;++i) _f[i]=0;
	for(int i=m;i<limit;++i) _g[i]=0;
	NTT(_f,0,limit); NTT(_g,0,limit);
	for(int i=0;i<limit;++i)
		h[i]=_f[i]*_g[i]%MOD; 
	NTT(h,1,limit);
}
void QiuDao(int *f,int *g,int n){
	for(int i=1;i<n;++i) g[i-1]=f[i]*i%MOD;
	g[n-1]=0; 
}
void JiFen(int *f,int *g,int n){
	for(int i=0;i<n;++i) g[i+1]=f[i]*qpow(i+1,MOD-2,MOD)%MOD;
	g[0]=0; 
}
void Ln(int *f,int *g,int n){
	static int _df[N],_if[N],w[N];
	for(int i=0;i<(n<<1);++i) _df[i]=_if[i]=w[i]=0;
	QiuDao(f,_df,n);
	INV(f,_if,n);
	mul(_df,_if,w,n,n);
	JiFen(w,g,n); 
}
void Exp(int *f,int *g,int n){
	if(n==1){
		g[0]=1;
		return ;
	} 
	Exp(f,g,(n+1)>>1);
	int limit=1;
	while(limit<(n<<1)) limit<<=1;
	static int _lng[N],w[N];
	for(int i=0;i<limit;++i) _lng[i]=0;
	Ln(g,_lng,n);
	for(int i=0;i<limit;++i) w[i]=f[i];
	for(int i=n;i<limit;++i) w[i]=0;
	NTT(w,0,limit); NTT(_lng,0,limit); NTT(g,0,limit);
	for(int i=0;i<limit;++i)
		g[i]=g[i]*(1-_lng[i]+w[i]+MOD)%MOD;
	NTT(g,1,limit);
	for(int i=n;i<limit;++i) g[i]=0;
}
int n,m,cnt[N]; 
void Mian(){
	cin>>n>>m;
	for(int i=1;i<=n;++i){
		int x; cin>>x;
		cnt[x]++;
	}
	for(int i=1;i<=m;++i){
		if(!cnt[i]) continue;
		for(int j=1;j*i<=m;++j)
			(f[j*i]+=cnt[i]*qpow(j,MOD-2,MOD)%MOD)%=MOD;
	}
	Exp(f,g,m+1);
	for(int i=1;i<=m;++i) cout<<g[i]<<'\n';
} 
void Mianclr(){
	
} 
signed main(){
    ios::sync_with_stdio(false); 
    cin.tie(0); cout.tie(0);     
	
	//freopen("ftc1.in","r",stdin);
	//freopen("r.out","w",stdout);
	
    int c,T=1; //cin>>c;
    while(T--){
    	Mian();
		Mianclr();
	}
}

玩游戏

答案的形式幂级数即为 \(Ans(k)=\frac{\sum_{i=1}^{n}\sum_{j=1}^{m}(a_i+b_j)^k}{nm}\),展开得:

\[Ans(k)=\frac{\sum_{i=1}^{n}\sum_{j=1}^{m}\sum_{p=0}^{k}\binom{k}{p}a_i^pb_j^{k-p}}{nm}=k!\times\frac{\sum_{i=1}^{n}\sum_{j=1}^{m}\sum_{p=0}^{k}\frac{a_i^p}{p!}\times \frac{b_j^{k-p}}{(k-p)!}}{nm}=k!\times\frac{\sum_{p=0}^{k}(\sum_{i=1}^{n}\frac{a_i^p}{p!})(\sum_{j=1}^{m}\frac{b_j^{k-p}}{(k-p)!})}{nm} \]

\[\therefore nm\times \frac{Ans(k)}{k!}=\sum_{p=0}^{k}(\sum_{i=1}^{n}\frac{a_i^p}{p!})(\sum_{j=1}^{m}\frac{b_j^{k-p}}{(k-p)!}) \]

也就是两个形如 \(\sum_{i=1}^n a_i^p\) 的幂级数对应的 EGF 的卷积,即求 \(G(x)=\sum_{k\geq0}(\sum_{i=1}^na_i^k) x^k\) 的前 \(1\sim k\)(题目中给的 \(k\)) 次项系数。还是推式子:

\[G(x)=\sum_{k\geq0}(\sum_{i=1}^na_i^k) x^k=\sum_{i=1}^n\left(\sum_{k\geq0}(a_ix)^k\right)=\sum_{i=1}^n\frac{1}{1-xa_i} \]

到这里已经可以暴力合并两个分式算了,但是这样每次合并需要七次 dft,常数比较大。

一个很厉害(套路?)的做法是构造 \(\ln (1-xa_i)'=\frac{-a_i}{1-xa_i}\),所以 \(G(x)=-\left(\ln \left(\prod_{i=1}^{n}(1-xa_i)\right)\right)'+n\),里面这个东西分治算就能做到 \(O(n\log^2 n)\)

AC Code
// Written by Mi2uk1 
// Try Harder.

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define i28 __int128 
#define ull unsigned long long
#define pii pair<int,int>
#define pll pair<long long,long long>
#define fir first
#define INF (1e18) 
#define sec second
#define pb push_back
#define eb emplace_back

const int N=8e5+9,M=5e6+9;
const int MOD=998244353;
const double eps=1e-14;
inline void chkmax(int &x,int y){x=x<y?y:x;}
inline void chkmin(int &x,int y){x=x<y?x:y;}
inline void chkmax(ll &x,ll y){x=x<y?y:x;}
inline void chkmin(ll &x,ll y){x=x<y?x:y;}
inline int lowbit(int x){return x&(-x);}
int qpow(int a,int b,int p){
    int ret=1;
    while(b){
        if(b&1) ret=1ll*ret*a%p;
        a=1ll*a*a%p;
        b>>=1;
    }
    return ret; 
}

#define int long long
const int G=3,INVG=332748118; 
int trs[N]; 
void NTT(int *f,bool flg,int limit){
    for(int i=0;i<limit;++i)
		trs[i]=(trs[i>>1]>>1|((i&1)?limit>>1:0));
	for(int i=0;i<limit;++i) 
		if(i<trs[i]) swap(f[i],f[trs[i]]);
	for(int p=2;p<=limit;p<<=1){
		int len=(p>>1);
		int wn=qpow(flg?INVG:G,(MOD-1)/p,MOD);
		for(int k=0;k<limit;k+=p){
			int buf=1;
			for(int i=k;i<k+len;++i){
				int tmp=f[i+len]*buf%MOD;
				f[i+len]=(f[i]-tmp+MOD)%MOD;
				f[i]=(f[i]+tmp)%MOD;
				buf=buf*wn%MOD;
			}
		}
	} 
	if(flg){
		int inv=qpow(limit,MOD-2,MOD);
		for(int i=0;i<limit;++i) f[i]=f[i]*inv%MOD; 
	}
}
void INV(int *f,int *g,int n){
	static int w[N];
	if(n==1){
		g[0]=qpow(f[0],MOD-2,MOD);
		return ;
	}
	INV(f,g,(n+1)>>1);
	int limit=1;
	while(limit<(n<<1)) limit<<=1;
	for(int i=0;i<limit;++i) w[i]=f[i];
	for(int i=n;i<limit;++i) w[i]=0;
	for(int i=(n+1)>>1;i<limit;++i) g[i]=0;
	NTT(w,0,limit); NTT(g,0,limit);
	for(int i=0;i<limit;++i)
		g[i]=(2*g[i]%MOD-g[i]*g[i]%MOD*w[i]%MOD+MOD)%MOD; 
	NTT(g,1,limit);
	for(int i=n;i<limit;++i) g[i]=0;
}
void mul(int *f,int *g,int *h,int n,int m){
	int limit=1;
	while(limit<(n+m)) limit<<=1;
	static int _f[N],_g[N];
	for(int i=0;i<n;++i) _f[i]=f[i];
	for(int i=0;i<m;++i) _g[i]=g[i];
	for(int i=n;i<limit;++i) _f[i]=0;
	for(int i=m;i<limit;++i) _g[i]=0;
	NTT(_f,0,limit); NTT(_g,0,limit);
	for(int i=0;i<limit;++i)
		h[i]=_f[i]*_g[i]%MOD; 
	NTT(h,1,limit);
}
void QiuDao(int *f,int *g,int n){
	for(int i=1;i<n;++i) g[i-1]=f[i]*i%MOD;
	g[n-1]=0; 
}
void JiFen(int *f,int *g,int n){
	for(int i=0;i<n;++i) g[i+1]=f[i]*qpow(i+1,MOD-2,MOD)%MOD;
	g[0]=0; 
}
void Ln(int *f,int *g,int n){
	static int _df[N],_if[N],w[N];
	for(int i=0;i<(n<<1);++i) _df[i]=_if[i]=w[i]=0;
	QiuDao(f,_df,n);
	INV(f,_if,n);
	mul(_df,_if,w,n,n);
	JiFen(w,g,n); 
}
int n,m,kk,a[N],b[N],ga[N],gb[N],tmpa[N],tmpb[N];
int fac[N],ifac[N],slen[N],Ans[N]; 
void solve(int u,int l,int r,int *a,int *F){
	slen[u]=r-l+2;
	if(l==r){
		F[0]=1; F[1]=MOD-a[l];
		return ;
	}
	int mid=l+r>>1,f[N],g[N];
	solve(u<<1,l,mid,a,f); solve(u<<1|1,mid+1,r,a,g);
	mul(f,g,F,slen[u<<1],slen[u<<1|1]);
}
void Mian(){
	cin>>n>>m;
	for(int i=1;i<=n;++i) cin>>a[i];
	for(int i=1;i<=m;++i) cin>>b[i];
	int tmp=qpow(n*m%MOD,MOD-2,MOD),tn=n,tm=m;
	cin>>kk; ++kk; chkmax(n,kk); chkmax(m,kk);
	
	fac[0]=1;
	for(int i=1;i<=n+m;++i) fac[i]=fac[i-1]*i%MOD;
	ifac[n+m]=qpow(fac[n+m],MOD-2,MOD);
	for(int i=n+m;i>=1;--i) ifac[i-1]=ifac[i]*i%MOD;
	
	solve(1,1,n,a,tmpa); solve(1,1,m,b,tmpb);
	Ln(tmpa,ga,kk); Ln(tmpb,gb,kk);
	for(int i=0;i<kk-1;++i){
		ga[i]=ga[i+1]*(i+1)%MOD;
		gb[i]=gb[i+1]*(i+1)%MOD;
	}
	ga[kk-1]=gb[kk-1]=0;
	for(int i=kk-1;i>=1;--i){
		ga[i]=(MOD-ga[i-1])%MOD; 
		gb[i]=(MOD-gb[i-1])%MOD;
	}
	ga[0]=tn; gb[0]=tm;
	for(int i=2;i<kk;++i){
		ga[i]=ga[i]*ifac[i]%MOD;
		gb[i]=gb[i]*ifac[i]%MOD;
	}
	mul(ga,gb,Ans,kk,kk);
	for(int i=1;i<kk;++i){
		int coef=tmp*fac[i]%MOD;
		cout<<coef*Ans[i]%MOD<<'\n';
	} 
} 
void Mianclr(){
	
} 
signed main(){
    ios::sync_with_stdio(false); 
    cin.tie(0); cout.tie(0);     
	
	//freopen("ftc1.in","r",stdin);
	//freopen("r.out","w",stdout);
	
    int c,T=1; //cin>>c;
    while(T--){
    	Mian();
		Mianclr();
	}
}

Graph Coloring(Easy ver.)

好题。

首先给出一个结论:若一种颜色的边导出的子图不连通,则另一张颜色的导出子图(其补图)必然是联通的。

所以我们可以直接钦定一种颜色不连通,此时图一定满足题目要求。设 \(dp_n\) 表示 \(n\) 个点的图,钦定蓝色边不连通的总方案数。

直接弄不好转移,图有标号,我们钦定点 \(1\) 所在的极长蓝色连通块大小,不妨设其为 \(j\),则此时这个连通块内部的方案数为 \(dp_j\)(此时此连通块一定是红色不连通,方案数和钦定蓝色不连通相等),对于另外的 \(n-j\) 个点,其与这 \(j\) 个点的连边一定都是红色,且由结论知这 \(n-j\) 个点的导出子图红不连通和蓝不连通都是合法的,故有转移:

\[dp_n=\sum_{j=1}^{n-1}\binom{n-1}{j-1}\times dp_j\times dp_{n-j}\times(2-\left[j=n-1\right]) \]

\(j=n-1\) 时的 case 即为只剩下一个点。复杂度 \(O(n^2)\)

AC Code
// Written by Mi2uk1 
// Try Harder.

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define i28 __int128 
#define ull unsigned long long
#define pii pair<int,int>
#define pll pair<long long,long long>
#define fir first
#define INF (1e18) 
#define sec second
#define pb push_back
#define eb emplace_back

const int N=5e3+9,M=5e6+9;
const int MOD=998244353;
const double eps=1e-14;
inline void chkmax(int &x,int y){x=x<y?y:x;}
inline void chkmin(int &x,int y){x=x<y?x:y;}
inline void chkmax(ll &x,ll y){x=x<y?y:x;}
inline void chkmin(ll &x,ll y){x=x<y?x:y;}
inline int lowbit(int x){return x&(-x);}
int qpow(int a,int b,int p){
    int ret=1;
    while(b){
        if(b&1) ret=1ll*ret*a%p;
        a=1ll*a*a%p;
        b>>=1;
    }
    return ret; 
}

int n,dp[N],C[N][N];
void Mian(){
	cin>>n;
	for(int i=0;i<=n;++i){
		for(int j=0;j<=i;++j){
			if(!j) C[i][j]=1;
			else C[i][j]=(C[i-1][j]+C[i-1][j-1])%MOD; 
		}
	}
	dp[1]=dp[2]=1; 
	for(int i=3;i<=n;++i){
		for(int j=1;j<=i-1;++j){ 
			(dp[i]+=1ll*C[i-1][j-1]*dp[j]%MOD*dp[i-j]%MOD*(2-(j==i-1))%MOD)%=MOD;
		} 
	}
	cout<<1ll*(dp[n]-1+MOD)%MOD*2%MOD; 
} 
void Mianclr(){
	
} 
signed main(){
    ios::sync_with_stdio(false); 
    cin.tie(0); cout.tie(0);     
	
	//freopen("ftc1.in","r",stdin);
	//freopen("r.out","w",stdout);
	
    int c,T=1; //cin>>c;
    while(T--){
    	Mian();
		Mianclr();
	}
}

Graph Coloring(Hard ver.)

\(n\) 变成了 \(5\times 10^4\)

转移其实就是个全在线卷积,直接做就可以了。

代码有时间来补。咕咕咕。

posted @ 2026-04-27 12:57  Mi2uk1  阅读(7)  评论(0)    收藏  举报