CF2128游记
前言
CF2128 难度 \(Div.2\)
可惜 E1 没看出是二分。
A.Recycling Center
题面
共 \(t\) 组数据,每组数据:
给定一个大小为 \(n\) 的数组 \(a\) 和一个整数 \(c\) ,你要进行 \(n\) 次操作:
- 不重复地选择一个下标 \(i\) ,若 \(a_i>c\) ,代价为 \(1\) ;否则为 \(0\) ;
- 将所有 \(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\) 。
然后你可以进行若干次操作:
- 选择一个大于 \(\min(a)\) 的数 \(x\) ;
- 寻找最小的下标 \(i\) 使得 \(\forall 1\le j<i,a_j\ge x,a_i<x\) ;
- 令 \(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)\) ,使得:
- \(l_i\le w_i\le r_i\) ;
- \(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;
}

浙公网安备 33010602011771号