CF2128游记

前言

CF2128 难度 \(Div.2\)
可惜 E1 没看出是二分。

A.Recycling Center

题面

\(t\) 组数据,每组数据:
给定一个大小为 \(n\) 的数组 \(a\) 和一个整数 \(c\) ,你要进行 \(n\) 次操作:

  1. 不重复地选择一个下标 \(i\) ,若 \(a_i>c\) ,代价为 \(1\) ;否则为 \(0\)
  2. 将所有 \(a_i\) 乘以二。

求最小代价。

思路

贪心。
先将 \(a_i\) 排序。
首先删掉 \(a_i>c\) 并增加等量代价,然后删掉最大的 \(a_i\) ,最后令所有 \(a_i\) 乘以二。
重复上述流程直至 \(a\) 清空。

实现

#include<iostream>
#include<algorithm>
using namespace std;
int t,n,c,ans,m,a[3005];
int main(){
	cin>>t;
	while(t--){
		cin>>n>>c;
		for(int i=1;i<=n;++i)cin>>a[i];
		sort(a+1,a+1+n);
		m=n;ans=0;
		while(m>0){
			for(int i=1;i<=m;++i){
				if(a[i]>c){
					ans+=m-i+1;
					m=i-1;
					break;
				}
				a[i]*=2;
			}
			--m;
		}
		cout<<ans<<endl;
	}
	return 0;
}

B.Deque Process

题面

称一个数组是坏的,当且仅当:
存在一个下标 \(i\) 使得 \(a_i<a_{i+1}<a_{i+2}<a_{i+3}<a_{i+4}\)\(a_i>a_{i+1}>a_{i+2}>a_{i+3}>a_{i+4}\)

\(t\) 组数据,每组数据:
给定一个 \(1-n\) 的排列 \(a\) 和一个空数组 \(q\) ,你需要进行 \(n\) 次操作:
L. 将 \(a\) 最左端的数 \(a_l\)\(a\) 中删除并加入数组 \(q\) 最末端;
R. 将 \(a\) 最右端的数 \(a_r\)\(a\) 中删除并加入数组 \(q\) 最末端。

你需要输出一个只包含 \(L,R\) 的字符串 \(s\) ,使得依次执行对应操作之后得到的数组 \(q\) 不是坏的

思路

首先可以发现:若数组 \(q\) 满足 \(q_{2k}<q_{2k+1}>q_{2k+2}\) 则其一定不是坏的
那么我们第奇数次取 \(a_l,a_r\) 中的最大值,偶数次取 \(a_l,a_r\) 中的最小值,即可满足所述条件。

证明:
不妨设第 \(k\) 次取了 \(a_l\) (\(k\) 为奇数) ,
则有 \(q_k=\max(a_l,a_r)=a_l>a_r\)
那么 \(q_{k+1}=\min(a_{l+1},a_r)\le a_r<q_k\)

实现

#include<iostream>
using namespace std;
const int N=1e5+5;
int t,n,a[N];
int main(){
	cin>>t;
	while(t--){
		cin>>n;
		for(int i=1;i<=n;++i)cin>>a[i];
		int l=1,r=n;
		for(int i=1;i<=n;++i){
			if(i&1){
				if(a[l]>a[r]){
					++l;
					cout<<"L";
				}
				else{
					--r;
					cout<<"R";
				}
			}
			else{
				if(a[l]<a[r]){
					++l;
					cout<<"L";
				}
				else{
					--r;
					cout<<"R";
				}
			}
		}
		cout<<"\n";
	}
	return 0;
}

C.Leftmost Below

题面

\(t\) 组数据,每组数据:
给定一个大小为 \(n\) 的数组 \(b\) 和一个数组 \(a\) ,其中 \(a\) 的所有元素均为 \(0\)
然后你可以进行若干次操作:

  1. 选择一个大于 \(\min(a)\) 的数 \(x\)
  2. 寻找最小的下标 \(i\) 使得 \(\forall 1\le j<i,a_j\ge x,a_i<x\)
  3. \(a_i\)\(x\)

问能否通过若干次操作使得 \(a,b\) 相等。、

思路

不妨设当前 \(a_i=0\)
首先, \(a_i\) 能取多大只和其前面的数最大的数,假设其为 \(a_j\)
那么,我们先取 \(x=a_j-1\) ,则 \(a_i\leftarrow a_j-1\) ,然后再取 \(x=a_j\)\(a_i\leftarrow 2\times a_j-1\) ,这是合法的最大的 \(a_i\)

反证
假设 \(2\times a_j-1\) 并非合法的最大的 \(a_i\) ,则不妨取 \(a_i=2\times a_j\)
假设取 \(x\le a_j\) ,则原来的 \(a_i=a_i-x\ge a_j\)\(a_i<x\) 矛盾;
假设取 \(x>a_j\) ,则最小的下标不为 \(i\)\(j\) ,矛盾。
所以假设不成立,即 \(2\times a_j-1\) 为最大的 \(a_i\)

所以,直接线性跑一遍即可。

实现

#include<iostream>
using namespace std;
const int N=2e5+5;
int t,n,a[N],minn,flag;
int main(){
	cin>>t;
	while(t--){
		cin>>n;
		flag=0;
		minn=1e9+1;
		for(int i=1;i<=n;++i){
			cin>>a[i];
			if(a[i]>=minn*2){flag=1;break;}
			minn=min(a[i],minn);
		}
		if(flag)cout<<"NO\n";
		else cout<<"YES\n";
	}
	return 0;
}

D.Sum of LDS

题面

\(t\) 组数据,每组数据:
给定一个 \(1-n\) 的排列 \(p\) ,其中 \(\max(p_i,p_{i+1})>p_{i+2}\)
你需要计算 \(p\) 的所有子序列 \([p_l...p_r]\) 的最长递减子序列的长度之和。

思路

朴素的算法是 \(O(n^3)\) 的,不可取。
我们考虑每一位的贡献。
从后往前推,设 \(f_i\) 表示 \([p_i...p_r]\) 的最长递减子序列的长度之和。
那么若 \(a_i>a_{i+1}\)\(f_i=f_{i+1}+n-i+1\) ,否则 \(f_i=f_{i+1}+1\)
答案 \(ans=\sum f_i\)

实现

#include<iostream>
using namespace std;
#define ll long long
const int N=5e5+5;
int t,n,a[N];
ll ans,f;
int main(){
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	cin>>t;
	while(t--){
		cin>>n;
		for(int i=1;i<=n;++i)cin>>a[i];
		ans=f=0;
		a[n+1]=0;
		for(int i=n;i>=1;--i){
			if(a[i]>a[i+1])f+=n-i+1;
			else ++f;
			ans+=f;
		}
		cout<<ans<<endl;
	}
	return 0;
}

E1.Submedians (Easy Version)

题面

称整数 \(v\) 是大小为 \(m\) 的数组 \(b\)中位数,当且仅当:

  • \(v\) 不小于 \(b\) 中至少 \(\left\lfloor\frac{m}{2}\right\rfloor\) 个数;
  • \(v\) 不大于 \(b\) 中至少 \(\left\lfloor\frac{m}{2}\right\rfloor\) 个数。

如果存在至少一对引索 \((l,r),r-l+1\ge k\) 使得 \(v\)\(b\[l...r\]\)中位数,则称 \(v\)次中位数
\(t\) 组数据,每组数据:
给定一个大小为 \(n\) 的数组 \(a\) (\forall 1\le a_i\le n) 和一个整数 \(k\)
求其最大的次中位数 \(v\) 以及对应的引索 \((l,r)\)

思路

考虑到 \(1\le b_i\le n\) ,直接二分,将所有所有不小于 \(x\) 的数设为 \(1\) ,所有小于 \(x\) 的数设为 \(-1\) ,寻找长度不小于 \(k\) 的和为 \(k\) 的子段即可。

实现

#include<iostream>
using namespace std;
const int N=3e5+5;
int tt,n,k,a[N],L,R,b[N];
bool chk(int m){
	for(int i=1;i<=n;++i){
		if(a[i]<m)b[i]=b[i-1]-1;
		else b[i]=b[i-1]+1;
	}
	int ll=0;
	for(int i=k;i<=n;++i){
		if(b[i-k]<b[ll])ll=i-k;
		if(b[i]>=b[ll]){
			L=ll+1;R=i;
			return 1;
		}
	}
	return 0;
}
int main(){
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	cin>>tt;
	while(tt--){
		cin>>n>>k;
		for(int i=1;i<=n;++i)cin>>a[i];
		int l=0,r=n+1,mid;
		while(l+1<r){
			mid=l+r>>1;
			if(chk(mid))l=mid;
			else r=mid;
		}
		cout<<l<<" "<<L<<" "<<R<<endl;
	}
	return 0;
}

E2.Submedians (Hard Version)

题面

与 E1 差不多,但是 E2 需要求所有的次中位数及对应引索。

思路

首先我们发现,所有中位数次中位数一定是连续的。
如数组 \(1,2,3,4\)中位数\([2,3]\)
那么我们可以类似于 E1 一样处理(还是有小细节上的区别的)出最大和最小的次中位数 \(v_1,v_2\) ,对应引索 \((l_1,r_1),(l_2,r_2)\)
可以证明所有次中位数均出现在 \((l_1,r_1)\) 移动到 \((l_2,r_2)\) 的莫队过程中。
在莫队过程中,我们要如同 \(chk\) 函数一般计算计算子段和 \(sum\) ,在 \(sum\ge 0\) 时输出即可.
需要注意的是,在 \(v_1\rightarrow v_1+1\) 的过程中 \(sum\) 需要减去两倍的 \(cnt[v_1]\)

实现

#include<iostream>
using namespace std;
const int N=3e5+5;
int tt,n,k,a[N],l1,l2,r1,r2,b[N],L,R,cnt[N],sum;
bool chk(int m,bool xn){
	for(int i=1;i<=n;++i){
		if(xn){
			if(a[i]<m)b[i]=b[i-1]-1;
			else b[i]=b[i-1]+1;
		}
		else{
			if(a[i]>m)b[i]=b[i-1]-1;
			else b[i]=b[i-1]+1;
		}
	}
	int ll=0;
	for(int i=k;i<=n;++i){
		if(b[i-k]<b[ll])ll=i-k;
		if(b[i]>=b[ll]){
			if(xn)l2=ll+1,r2=i;
			else  l1=ll+1,r1=i;
			return 1;
		}
	}
	return 0;
}
void add(int x,int k){
	cnt[x]+=k;sum+=x>=L?k:-k;
}
int main(){
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	cin>>tt;
	while(tt--){
		cin>>n>>k;
		for(int i=1;i<=n;++i)cin>>a[i],cnt[i]=0;
		sum=0;
		int l=0,r=n+1,mid;
		while(l+1<r){
			mid=l+r>>1;
			if(chk(mid,1))l=mid;
			else r=mid;
		}
		R=l;l=0;r=n+1;
		while(l+1<r){
			mid=l+r>>1;
			if(chk(mid,0))r=mid;
			else l=mid;
		}
		L=r;
		cout<<R-L+1<<endl;
		for(int i=l1;i<=r1;++i)sum+=a[i]>=L?1:-1,++cnt[a[i]];
		while(L<=R){
			while(sum>=0){
				cout<<L<<" "<<l1<<" "<<r1<<endl;
				sum-=2*cnt[L++];
			}
			if(l1>l2)add(a[--l1], 1);
			else if(r1<r2)add(a[++r1], 1);
			else if(l1<l2)add(a[l1++],-1);
			else if(r1>r2)add(a[r1--],-1);
		}
	}
	return 0;
}

F.Strict Triangle

这居然是一道黑题。

题面

\(t\) 组数据,每组数据:
给定一个 \(n\) 个点, \(m\) 条边的无向图以及一个整数 \(k\) ,其中,每一条边 \((u_i,v_i)\) 有一个限制 \((l_i,r_i)\) 与未赋值的权值 \(w_i\)
问是否存在一种赋值方法 \(w=(w_1,w_2,...,w_m)\) ,使得:

  1. \(l_i\le w_i\le r_i\)
  2. \(dis_w(1,n)\ne dis_w(1,k)+dis_w(k,n)\) ,即 \(1-n\) 的最短路不经过 \(k\)

思路

很显然,如果存在合法方案,则一定存在一种方案使得 \(w_i\in \{l_i,r_i\}\)

证明
考虑一个任意的合法方案 \(w\)\(P\) 则是 \(w\)\(1-n\) 的最短路径。
\(w_i(i\in P)\)\(1\) ,则 \(dis_w(1,n)\) 正好减 \(1\) ,而 \(dis_w(1,k)+dis_2(k,n)\) 至多减 \(1\)
而令 \(w_j(j\in P)\) 增加,则 \(dis_w(1,n)\) 不会增加,而 \(dis_w(1,k)+dis_2(k,n)\) 不会减少。
于是仍有 \(dis_w(1,n)\ne dis_w(1,k)+dis_w(k,n)\)

那么显然,如果 \(u,v\in P\) ,则一定有 \(dis_L(u,v)<dis_R(u,k)+dis_R(k,v)\)

反证
\(dis_L(u,v)\ge dis_R(u,k)+dis_R(k,v)\)
那么有 \(dis_w(1,n)=dis_w(1,u)+dis_L(u,v)+dis_w(v,n)\ge dis_w(1,u)+dis_R(u,k)+dis_R(k,v)+dis_w(k,v)\ge dis_w(1,k)+dis_w(k,n)\)
\(dis_w(1,n)\ge dis_w(1,k)+dis_w(k,n)\) ,而 \(dis_w(1,n)< dis_w(1,k)+dis_w(k,n)\) ,矛盾。

先从 \(k\) 跑一个 \(dis_R(u,k)\)
同时对限制进行移项发现: \(dis_R(k,v)>dis_L(u,v)-dis_R(u,k)\)
定义 \(f(v)=\max \{f(u)+l_i,-dis_R(v,k)\}\) ,对于 \(f(u)\ge dis_R(v,k)\) 的直接跳过。

实现

#include<iostream>
#include<queue>
#include<vector>
using namespace std;
#define ll long long
#define pr pair<ll,int>
#define mp make_pair
const int N=2e5+5;
const ll inf=1e18;
int tt,n,m,k;
ll dr[N],g[N];
struct node{int v,l,r;};
vector<node>e[N];
bool vis[N];
void bfs1(){
	priority_queue<pr>q;
	for(int i=1;i<=n;++i)dr[i]=inf,vis[i]=0;
	dr[k]=0;q.push(mp(0,k));
	while(!q.empty()){
		int u=q.top().second;q.pop();
		if(vis[u])continue;
		vis[u]=1;
		for(auto x:e[u]){
			if(dr[u]+x.r<dr[x.v]){
				dr[x.v]=dr[u]+x.r;
				q.push(mp(-dr[x.v],x.v));
			}
		}
	}
}
void bfs2(){
	priority_queue<pr>q;
	for(int i=1;i<=n;++i)g[i]=inf,vis[i]=0;
	g[1]=-dr[1];
	q.push(mp(dr[1],1));
	while(!q.empty()){
		int u=q.top().second;q.pop();
		if(vis[u]||g[u]>=dr[u])continue;
		vis[u]=1;
		for(auto x:e[u]){
			if(g[x.v]>max(g[u]+x.l,-dr[x.v])){
				g[x.v]=max(g[u]+x.l,-dr[x.v]);
				q.push(mp(-g[x.v],x.v));
			}
		}
	}
}
int main(){int x,y,l,r;
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	cin>>tt;
	while(tt--){
		cin>>n>>m>>k;
		for(int i=1;i<=n;++i)e[i].clear();
		for(int i=1;i<=m;++i){
			cin>>x>>y>>l>>r;
			e[x].push_back({y,l,r});
			e[y].push_back({x,l,r});
		}
		bfs1();
		bfs2();
		puts(g[n]>=dr[n]?"NO":"YES");
	}
	return 0;
}
posted @ 2025-08-12 22:23  Xie2Yue  阅读(17)  评论(0)    收藏  举报