杂题专练2 系列题解

CF1763C

容易发现当 \(n\ge 4\) 时可以将左右两端变成 \(0\),随后用最大值覆盖全部,问题转化为 \(n=2\)\(n=3\) 时的答案。

  • \(n=2\) 时,要么进行一次操作,要么不操作,\(ans=\max(a_1+a_2,2|a_1-a_2|)\)

  • \(n=3\) 时,假如最大值在两侧,可以用最大值覆盖全部;当在中间时,要么不操作,要么先左后右,要么先右后左,\(ans=\max(3a_1,3a_3,a_1+a_2+a_3,3|a_1-a_2|,3|a_2-a_3|)\)

时间复杂度 \(O(\sum n)\)

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e5+5;
int t,n,a[N];
void solve(){
	cin>>n;int mx=0;
	for(int i=1;i<=n;i++)
		cin>>a[i],mx=max(mx,a[i]);
	if(n>3) cout<<mx*n<<"\n";
	else if(n==2) cout<<max(a[1]+a[2],2*abs(a[1]-a[2]))<<"\n";
	else cout<<max(a[1]+a[2]+a[3],max({a[1],a[3],abs(a[2]-a[1]),abs(a[3]-a[2])})*3)<<"\n";
}signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>t;
	while(t--) solve();
	return 0;
}

CF1775E

难以发现对数组进行前缀和,那么每次操作相当于在前缀和数组上进行区间加或区间减,那么取前缀和数组正数最大值和负数最小值,相减就是答案。

时间复杂度 \(O(\sum n)\)

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e5+5;
int t,n,a[N];
void solve(){
	cin>>n;int mx=0,mn=0;
	for(int i=1;i<=n;i++)
		cin>>a[i],a[i]+=a[i-1],mx=max(mx,a[i]),mn=min(mn,a[i]);
	cout<<mx-mn<<"\n";
}signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>t;
	while(t--) solve();
	return 0;
}

CF1290C

题目中说的“任意三个集合交集为空集”,实际上可以理解为“每个点最多出现在两个集合里”。

那么考虑对于每个集合建立两个点,即选或不选。

设第 \(i\) 个点被 \(a_i,b_i\) 控制。当 \(s_i=1\) 时,连 \((a_i,b_i),(a_i+k,b_i+k)\) 两条边;反之则连 \((a_i+k,b_i),(a_i,b_i+k)\) 两条边。并查集维护即可。

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

#include<bits/stdc++.h>
using namespace std;
const int N=3e5+5;
int n,k,sz[N*2][2],fa[N*2];
int ans,a[N],b[N];string s;
int find(int x){
	return fa[x]==x?x:fa[x]=find(fa[x]); 
}void unite(int x,int y){
	x=find(x),y=find(y);
	if(x==y) return;
	ans-=min(sz[x][0],sz[x][1]);
	ans-=min(sz[y][0],sz[y][1]);
	sz[x][0]+=sz[y][0],sz[x][1]+=sz[y][1];
	fa[y]=x,ans+=min(sz[x][0],sz[x][1]);
}signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>n>>k>>s,s=" "+s;
	for(int i=1;i<=k;i++){
		int cnt;cin>>cnt;
		while(cnt--){
			int x;cin>>x;
			if(!a[x]) a[x]=i;
			else b[x]=i;
		}
	}for(int i=0;i<=k*2+1;i++)
		sz[i][i/(k+1)]=1,fa[i]=i;
	for(int i=1;i<=n;i++){
		if(s[i]=='1') unite(a[i],b[i]),unite(a[i]+k+1,b[i]+k+1);
		else unite(a[i],b[i]+k+1),unite(b[i],a[i]+k+1);int x=find(0);
		cout<<ans/2+(sz[x][0]<sz[x][1])*(sz[x][1]-sz[x][0])<<"\n";
	}return 0;
}

CF1458D

考虑维护 \(x(i)\),递推公式为:

\[x(i)=\begin{cases}x(i-1)+1\ (s_i=1)\\x(i-1)-1\ (s_i=0)\end{cases} \]

此时在 \(x(i-1)\)\(x(i)\) 建边。那么所有能够进行操作的区间都将成环。问题转变成找一条字典序最小的欧拉路径。直接贪心即可。

时间复杂度 \(O(\sum n)\)

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=5e5+5;
int t,n,cnt[N*2],nw;string s;
void solve(){
	cin>>s,n=s.size();
	s=" "+s,nw=0;
	for(int i=1;i<=n;i++){
		if(s[i]=='0') nw--,cnt[nw+n]++;
		else cnt[nw+n]++,nw++;
	}nw=0;
	for(int i=1;i<=n;i++){
		if(cnt[nw+n-1]&&(!cnt[nw+n]||cnt[nw+n-1]>1))
			nw--,cnt[nw+n]--,cout<<0;
		else cnt[nw+n]--,nw++,cout<<1;
	}cout<<"\n";
	for(int i=0;i<=2*n;i++) cnt[i]=0;
}signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>t;
	while(t--) solve();
	return 0;
}

CF1389F

直接 \(dp\),设 \(f_{i,0/1}\),表示在离散化 \(i\) 位置处,最后挑选的线段是 \(1/2\) 两种颜色时的最优解。可以将维护转化为区间加和区间最大值。

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

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=4e5+5;
int n,b[N],lx[N],rx[N],tx[N];
int m,mx[2][N*4],tg[2][N*4];
unordered_map<int,int>mp;
vector<int>g[N];
void down(int x,int v,int id){
	mx[id][x]+=v,tg[id][x]+=v;
}void push_down(int x,int id){
	down(x*2+1,tg[id][x],id);
	down(x*2,tg[id][x],id),tg[id][x]=0;
}void chg(int x,int l,int r,int L,int R,int k,int id){
	if(L<=l&&r<=R)
		return mx[id][x]+=k,tg[id][x]+=k,void();
	int mid=(l+r)/2;push_down(x,id);
	if(L<=mid) chg(x*2,l,mid,L,R,k,id);
	if(R>mid) chg(x*2+1,mid+1,r,L,R,k,id);
	mx[id][x]=max(mx[id][x*2],mx[id][x*2+1]);
}signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>n,++m,mp[0]=1;
	for(int i=1;i<=n;i++){
		cin>>lx[i]>>rx[i]>>tx[i],tx[i]--;
		if(!mp[lx[i]]) mp[lx[i]]=1,b[++m]=lx[i];
		if(!mp[rx[i]]) mp[rx[i]]=1,b[++m]=rx[i];
	}sort(b+1,b+m+1);
	for(int i=1;i<=m;i++) mp[b[i]]=i;
	for(int i=1;i<=n;i++){
		lx[i]=mp[lx[i]];
		g[rx[i]=mp[rx[i]]].push_back(i);
	}for(int i=2;i<=m;i++){
		for(int x:g[i])
			chg(1,1,m,1,lx[x]-1,1,tx[x]);
		chg(1,1,m,i,i,mx[1][1],0);
		chg(1,1,m,i,i,mx[0][1],1);
	}cout<<max(mx[0][1],mx[1][1]);
	return 0;
}

CF1580D

显然区间 \(max\) 是不好维护的,直接建立笛卡尔树跑树形 \(dp\) 即可。

时间复杂度 \(O(n^2)\)

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=4005;
int n,m,f[N][N],vis[N],st[N];
int rt,a[N],ls[N],rs[N],sz[N];
void work(int x,int y){
	for(int i=sz[x];~i;i--) for(int j=0;j<=sz[y];j++)
		f[x][i+j]=max(f[x][i+j],f[x][i]+f[y][j]-2*a[x]*i*j);
	sz[x]+=sz[y];
}void dfs(int x){
	f[x][1]=(m-1)*a[x],sz[x]=1;
	if(ls[x]) dfs(ls[x]),work(x,ls[x]);
	if(rs[x]) dfs(rs[x]),work(x,rs[x]);
}signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>n>>m;
	for(int i=1,tp=0,j=0;i<=n;i++){
		cin>>a[i],tp=j;
		while(tp&&a[st[tp]]>a[i]) --tp;
		if(tp) rs[st[tp]]=i;
		if(tp<j) ls[i]=st[tp+1];
		st[++tp]=i,j=tp;
	}for(int i=1;i<=n;i++)
		vis[ls[i]]=vis[rs[i]]=1;
	for(int i=1;i<=n;i++) if(!vis[i])
		dfs(i),cout<<f[i][m];
	return 0;
}

CF1720 D2

不太理解,先贴代码。

#include<bits/stdc++.h>
using namespace std;
const int N=3e5+5,M=1e7+5;
int t,n,tot,a[N],tr[M][2],dp[M][2];
void add(int x,int ad){
	int nw=1,vl=a[x]^x;
	for(int i=30;~i;i--){
		int ly=(vl>>i)&1,wz=(x>>i)&1;
		if(!tr[nw][ly]) tr[nw][ly]=++tot;
		dp[tr[nw][ly]][wz]=max(dp[tr[nw][ly]][wz],ad);
		nw=tr[nw][ly];
	}
}int maxn(int x){
	int nw=1,vl=a[x]^x,re=0;
	for(int i=30;~i;i--){
		int ly=(vl>>i)&1,wz=(a[x]>>i)&1;
		re=max(re,dp[tr[nw][ly^1]][wz^1]);
		if(!tr[nw][ly]) return re;
		nw=tr[nw][ly];
	}return re;
}void solve(){
	for(int i=1;i<=tot;i++)
		dp[i][0]=dp[i][1]=tr[i][0]=tr[i][1]=0;
	cin>>n,tot=1;int mx=0;
	for(int i=0,f;i<n;i++)
		cin>>a[i],mx=max(mx,(f=maxn(i)+1)),add(i,f);
	cout<<mx<<"\n";
}int main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>t;
	while(t--) solve();
	return 0;
}

CF1876F

大坑待补……

CF193D

判断值域范围,依次加入数值。

当加入新数时,所有情况 \(+1\)

假如在原序列中,新数前的数小于新数,那么左端点为 \(1\) 到新数前的部分 \(-1\)

维护最小值,最小值情况和次小值情况,用于更新答案。

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

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=3e5+5,M=2e6+5;
int n,mn[M],p1[M],p2[M];
int a[N],ps[N],ans,tg[M];
void push_up(int x){
	int ls=x*2,rs=x*2+1;
	mn[x]=min(mn[ls],mn[rs]);
	p1[x]=p2[x]=0;
	if(mn[ls]==mn[x])
		p1[x]+=p1[ls],p2[x]+=p2[ls];
	if(mn[rs]==mn[x])
		p1[x]+=p1[rs],p2[x]+=p2[rs];
	if(mn[ls]==mn[x]+1) p2[x]+=p1[ls];
	if(mn[rs]==mn[x]+1) p2[x]+=p1[rs];
}void down(int x,int v){
	mn[x]+=v,tg[x]+=v;
}void push_down(int x){
	down(x*2+1,tg[x]);
	down(x*2,tg[x]),tg[x]=0;
}void build(int x,int l,int r){
	if(l==r) return p1[x]=1,void();
	int mid=(l+r)/2;build(x*2,l,mid);
	build(x*2+1,mid+1,r),push_up(x); 
}void chg(int x,int l,int r,int L,int R,int v){
	if(L>R) return;
	if(L<=l&&r<=R)
		return mn[x]+=v,tg[x]+=v,void();
	int mid=(l+r)/2;push_down(x);
	if(L<=mid) chg(x*2,l,mid,L,R,v);
	if(R>mid) chg(x*2+1,mid+1,r,L,R,v);
	push_up(x);
}int sum(int x,int l,int r,int L,int R){
	if(L>R) return 0;
	if(L<=l&&r<=R)
		return p1[x]*(mn[x]<3)+p2[x]*(mn[x]<2);
	int mid=(l+r)/2,re=0;push_down(x);
	if(L<=mid) re=sum(x*2,l,mid,L,R);
	if(R>mid) re+=sum(x*2+1,mid+1,r,L,R);
	return re;
}signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>n;
	for(int i=1;i<=n;i++)
		cin>>a[i],ps[a[i]]=i;
	build(1,1,n);
	for(int i=1;i<=n;i++){
		chg(1,1,n,1,i,1);
		if(a[ps[i]-1]<i&&ps[i]>1)
			chg(1,1,n,1,a[ps[i]-1],-1);
		if(a[ps[i]+1]<i&&ps[i]<n)
			chg(1,1,n,1,a[ps[i]+1],-1);
		ans+=sum(1,1,n,1,i-1);
	}cout<<ans;
	return 0;
}

AGC056C

也是构建 \(x\) 数组,然后差分约束即可。

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

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+5;
int n,m,dis[N],fa[N],sz[N];
queue<int>q;vector<int>g[N];
void init(){
	for(int i=0;i<=n;i++) fa[i]=i,sz[i]=1;
}int find(int x){
	return x==fa[x]?x:fa[x]=find(fa[x]);
}void unite(int x,int y){
	x=find(x),y=find(y);
	if(x==y) return;
	if(sz[x]<sz[y]) swap(x,y);
	sz[x]+=sz[y],fa[y]=x;
}signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>n>>m,init();
	while(m--){
		int l,r;cin>>l>>r;
		unite(l-1,r);
	}for(int i=1;i<=n;i++){
		g[find(i)].push_back(find(i-1));
		g[find(i-1)].push_back(find(i));
	}memset(dis,-1,sizeof(dis));
	q.push(find(0)),dis[find(0)]=0;
	while(q.size()){
		int x=q.front();q.pop();
		for(auto y:g[x]) if(dis[y]<0)
			q.push(y),dis[y]=dis[x]+1;
	}for(int i=1;i<=n;i++)
		cout<<(dis[find(i-1)]>dis[find(i)]);
	return 0;
}
posted @ 2024-11-24 11:28  长安一片月_22  阅读(6)  评论(0编辑  收藏  举报