杂题专练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-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;
}