At408 ABCDE
A.Timeout
题意简述
满足任意时刻序列 \(a_{i+1}-a_{i} \leq s\) 输出\(yes\),否则\(no\),注意 \(0-idx\)
AC code
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef long long ll;
int main(){
int n,s;cin>>n>>s;
vector<int>a(n+1);
for(int i=1;i<=n;i++) cin>>a[i];
bool f=1;
for(int i=0;i<n;i++){
if(a[i+1]-a[i]>s) f=0;
}
if(f) cout<<"Yes"<<endl;
else cout<<"No"<<endl;
return 0;
}
B.Compression
题意简述
对输入序列升序排序后去重
AC code
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef long long ll;
int main(){
int n;cin>>n;
vector<int>a(n+1);
for(int i=1;i<=n;i++) cin>>a[i];
sort(a.begin()+1,a.end());
a.erase(unique(a.begin()+1,a.end()),a.end());
cout<<a.size()-1<<endl;
for(int i=1;i<a.size();i++) cout<<a[i]<<' ';
cout<<endl;
return 0;
}
C.Not All Covered
题意简述
有 \(n\) 条线段,第 \(i\)条线段覆盖 \(l_i,r_i\)的区间,求区间最小被覆盖的点被覆盖的次数
解题思路
范围十分小,甚至不需要离散化,直接差分后前缀和统计即可
AC code
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef long long ll;
int main(){
int n,m;cin>>n>>m;
vector<int>a(n+2);
for(int i=1;i<=m;i++){
int l,r;cin>>l>>r;
++a[l],--a[r+1];
}
for(int i=1;i<=n;i++) a[i]+=a[i-1];
ll minl=INT_MAX;
for(int i=1;i<=n;i++) minl=min(minl,(ll)a[i]);
cout<<minl<<endl;
return 0;
}
D.Flip to Gather
题意简述
维护01字符串,进行任意次操作,每次操作可以修改任意一个位置的 \(0\) 变成 \(1\),或 \(1\) 变成 \(0\) 求满足使得01串中的连续1区间最大出现1次的极小修改次数。
解题思路1
不妨考虑\(01\)串最后状态的可能性,全0,前缀0后跟随若干1,前缀0后跟随若干1后跟随若干0,不妨根据这三种状态规划动态规划数组,\(dp_{i,0}\)表示前 \(i\) 个位置是全0的最小操作次数,\(dp_{i,1}\)表示前 \(i\) 个位置是前缀0和若干1组合的最小操作次数,\(dp_{i,2}\) 表示前
\(i\) 个位置是前缀0和若干1组合再加上若干0的最小操作次数,则有状态转移方程 \(dp_{i,0}=dp_{i-1,0}+(s_i \neq 0)\),\(dp_{i,1}=min(dp_{i-1,0},dp_{i-1,1})+(s_i \neq 1)\),\(dp_{i,2}=min(dp_{i-1,1},dp_{i-1,2})+(s_i \neq 0)\).
AC code1
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef long long ll;
void solve(){
int n;cin>>n;
string s;cin>>s;
vector<vector<ll> >dp(n+1,vector<ll>(3,INT_MAX));
dp[0][0]=0;
for(int i=1;i<=n;i++){
dp[i][0]=dp[i-1][0]+(s[i-1]!='0');
dp[i][1]=min(dp[i-1][0],dp[i-1][1])+(s[i-1]!='1');
dp[i][2]=min(dp[i-1][1],dp[i-1][2])+(s[i-1]!='0');
}
cout<<min({dp[n][0],dp[n][1],dp[n][2]})<<endl;
}
int main(){
cin.tie(0)->ios::sync_with_stdio(false);
int T;cin>>T;
while(T--) solve();
return 0;
}
解题思路2
注意到,最终状态一定是中间一段 \(1\) 两边为 \(0\) ,其中中间1的个数可以取 \([0,n]\),考虑对于每个 \([l_i,r_i]\)是1的区间如何修改,使得满足条件,显然有操作次数等于区间内的 \(0\) 的个数和区间外 \(1\) 的个数之和,假设 \(sum_0\) 是前缀0的个数,\(sum_1\) 是前缀1的个数,则当前修改次数为 \(sum_{0,r}-sum_{0,l-1}+sum_{1,n}-(sum_{1,r}-sum_{1,l-1})\),记 \(C_i=sum_{0,i}-sum_{1,i}\),则有修改次数为 \(C_r-C_{l-1}+sum_{1,n}\),又因为对一个字符串 \(sum_{1,n}\)的值是固定的,那么我们只需要枚举 \(C_r-C_{l-1}\),记录最小值即可
AC code2
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef long long ll;
void solve(){
int n;cin>>n;
string s;cin>>s;
int Sum=count(s.begin(),s.end(),'1');
int pre=0;
int res=0;
for(char &c:s){
pre=min(0,pre)+(c=='1'?-1:1);
res=min(res,pre);
}
cout<<Sum+res<<endl;
}
int main(){
cin.tie(0)->ios::sync_with_stdio(false);
int T;cin>>T;
while(T--) solve();
return 0;
}
E.Minimum OR Path
题意简述
给定一个 \(n\) 个点 \(m\) 条边的无向联通图,保证无自环,求从 \(1\) 到 \(n\) 的所有简单路径中,对其边权连续按位或的最小值。
解题思路
因为是位运算,可以拆位考虑。又要求最小值,不妨贪心的想,最开始初始化 \(ans\) 为一个极大值 ,其二进制为 \(111...1\) ,然后从高位逐位判断,如果当前位是 \(0\) 是否可以更新一条从 \(1\) 到 \(n\) 的通路。具体做法是,遍历每一条边,判断当前边权是不是 \(ans\) 的子集,是则连边,最后有通路则这一位为 \(0\),否则为 \(1\),
AC code
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef long long ll;
struct DSU{
vector<int>f,siz;
DSU(int n){
init(n);
}
void init(int n){
f.resize(n+1);
siz.assign(n+1,1);
iota(f.begin(),f.end(),0);
}
int find(int x){
return x==f[x]?x:f[x]=find(f[x]);
}
bool Union(int x,int y){
int fx=find(x),fy=find(y);
if(fx==fy) return 0;
f[fx]=fy;
siz[fx]+=siz[fy];
return 1;
}
bool Same(int x,int y){
return find(x)==find(y);
}
};
struct edge{
int x,y;ll w;
};
int main(){
cin.tie(0)->ios::sync_with_stdio(false);
int n,m;cin>>n>>m;
vector<edge>a(m+1);
for(int i=1;i<=m;i++) cin>>a[i].x>>a[i].y>>a[i].w;
int ans=(1<<30)-1;
for(int k=29;k>=0;k--){
int t=(ans^(1<<k));
DSU dsu(n);
for(int i=1;i<=m;i++){
if((a[i].w|t)==t) dsu.Union(a[i].x,a[i].y);
}
if(dsu.Same(1,n)) ans=t;
}
cout<<ans<<endl;
}