AtCoder Beginner Contest 408(ABCDE)
前言
啊啊啊什么时候能突破四题大关啊我是fw……T^T
A - Timeout
#include <bits/stdc++.h>
using namespace std;typedef long long ll;
typedef pair<int,int> pii;void solve()
{int n,s;cin>>n>>s;vector<int>t(n+1);for(int i=1;i<=n;i++){ cin>>t[i];}for(int i=1;i<=n;i++){if(t[i-1]+s<t[i]){cout<<"No";return ;}}cout<<"Yes";return ;
}int main()
{ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);int t=1;//cin>>t;while(t--){solve(); }return 0;
}
这个题看着描述挺复杂,其实就是看两次拍拍之间是否连续,注意一开始是清醒的。
B - Compression
#include <bits/stdc++.h>
using namespace std;typedef long long ll;
typedef pair<int,int> pii;void solve()
{int n;cin>>n;int Max=1e-9;vector<int>nums(n);for(int i=0;i<n;i++){cin>>nums[i];Max=max(Max,nums[i]);}vector<bool>b(Max+1);int cnt=0;for(int i=0;i<n;i++){if(!b[nums[i]]){cnt++;}b[nums[i]]=true;}cout<<cnt<<endl;for(int i=1;i<=Max;i++){if(b[i]){cout<<i<<" ";}}
}int main()
{ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);int t=1;//cin>>t;while(t--){solve(); }return 0;
}
这个题也好说,就是先把最大值抓出来,然后开个桶过一遍,是新数就cnt++。最后再过一遍桶,如果出现过就输出即可。
C - Not All Covered
#include <bits/stdc++.h>
using namespace std;typedef long long ll;
typedef pair<int,int> pii;void solve()
{int n,m;cin>>n>>m;vector<int>l(m);vector<int>r(m);for(int i=0;i<m;i++){cin>>l[i]>>r[i];}vector<int>t(n+2);for(int i=0;i<m;i++){t[l[i]]++;t[r[i]+1]--;}int ans=1e9;for(int i=1;i<=n;i++){t[i]+=t[i-1];ans=min(ans,t[i]);}cout<<ans;
}int main()
{ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);int t=1;//cin>>t;while(t--){solve(); }return 0;
}
这个题其实就是一眼差分,就是根据区间两端点差分,然后求一遍前缀和统计最小值即可。
D - Flip to Gather
#include <bits/stdc++.h>
using namespace std;typedef long long ll;
typedef pair<int,int> pii;void solve()
{int n;cin>>n;string s;cin>>s;//枚举区间[l,r),求使其前缀和后缀全为0的操作数//ans=((l-1)-pre[l-1])+(pre[r]-pre[l-1])+((n-r)-(pre[n]-pre[r]))//分离含l的项和含r的项:L[i]=(i-1)-2pre[i-1] R[i]=(n-i)-pre[n]+2pre[i]//所以ans=L[l]+R[r]//所以可以先求出L数组和R数组//又因为r必然在l到n范围上,所以可以先求出对于每个l的R[r]的最小值,再枚举一遍L[l]+R[r]的最小值//时间复杂度O(n)//pre[i+1]:0~i范围上有几个0vector<int>pre(n+1);for(int i=1;i<=n;i++){pre[i]=pre[i-1]+(s[i-1]=='0'?1:0);}//全删情况int sufMin=pre[n];int ans=n-pre[n];//枚举l的位置for(int i=n;i>=1;i--){sufMin=min(sufMin,(n-i)-pre[n]+2*pre[i]);//后缀R的最小值ans=min(ans,sufMin+(i-1)-2*pre[i-1]);}cout<<ans<<endl;
}int main()
{ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);int t=1;cin>>t;while(t--){solve(); }return 0;
}
真不行了……
因为要求有且只有一个区间使得区间内全是1,区间外全是0,所以可以考虑枚举每个区间,求使这个区间内全是1和区间外全是0的最少操作次数,那就是让区间的前缀后缀都为0,区间内全是1。
由此可得,因为要快速求出前缀和后缀的0的个数,所以考虑对0的次数构建前缀和数组pre。那么对于每个区间,所需的操作次数就是,前缀1的个数(l-1)-pre[l-1],区间内0的个数pre[r]-pre[l-1],后缀1的个数(n-r)-(pre[n]-pre[r])。之后考虑分离含l的项和含r的项,所以答案就是L[i]+R[i]。
再观察可以发现,因为r必然位于从l位置开始到n这个范围上,所以可以考虑从后往前枚举每个l的位置,在这个过程中每步都可以求出R[i]的最小值,再和此时的L[i]相加即可。要注意的就是要先讨论把1全删了的情况(这个思路真的想不到一点……)
E - Minimum OR Path
#include <bits/stdc++.h>
using namespace std;typedef long long ll;
typedef pair<int,int> pii;const int MAXN=2e5+5;//并查集
vector<int>father(MAXN);int find(int i)
{if(i!=father[i]){father[i]=find(father[i]);}return father[i];
}void Union(int x,int y)
{int fx=find(x);int fy=find(y);if(fx!=fy){father[fx]=fy;}
}//判断是否存在一条路径使当前位为0
bool check(int x,int n,int m,vector<vector<int>>&edges)
{//找出所有能使x当前位为0的边,然后检查节点1和节点n是否连通 -> 并查集//buildfor(int i=0;i<=n;i++){father[i]=i;}//枚举每一条边for(int i=1;i<=m;i++){//此时之前的位都已经确定//所以能使当前位为0的边就是之前和当前是0的位同样是0,是1的位随意,后续位随意//方法是先设置x的后续位全为1,然后判断是否边权w或上x后仍为xif((edges[i][2]|x)==x){Union(edges[i][0],edges[i][1]);}}return find(1)==find(n);
}void solve()
{int n,m;cin>>n>>m;vector<vector<int>>edges(m+1,vector<int>(3));for(int i=1;i<=m;i++){cin>>edges[i][0]>>edges[i][1]>>edges[i][2];}//从高位到低位考虑,每次去看能否让当前位为0int ans=0;for(int i=30;i>=0;i--){//设置ans当前位为0,后续位全为1if(!check(ans|((1<<i)-1),n,m,edges))//不能使当前位为0{ans|=(1<<i);}}cout<<ans;
}int main()
{ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);int t=1;//cin>>t;while(t--){solve(); }return 0;
}
这个题的思路也很逆天,拼尽全力想不到用并查集……
因为要求让最后或起来的结果最小,那么大思路就是从高位到低位看是否存在一条路径使得当前位或起来的结果为0,如果存在,那肯定让当前位为0,如果不存在,那就只能让当前位为1。
所以之后的思路就是,从高位到低位去填ans的每一位。每次来到当前位时,前缀的位都已经确定,此时需要判断是否存在一条从节点1到节点n的路径,使得当前ans的前缀位里是0的位仍然保持0,是1的位无所谓,还要满足当前位是0,后续所有位无所谓。如果存在这么一条路径,就可以让ans的当前位为0,否则只能让ans的当前位是1。
那么判断是否存在一条从节点1到节点n的路径就可以使用并查集,每次判断前都初始化,然后去考察每一条边,如果边权满足条件就Union当前边所连接的两节点,最后判断节点1和节点n是否在同一集合里连通即可。此外,判断边权是否满足条件的方法是,因为要求从高位到当前位里是0的位必须还是0,是1的位无所谓,且后续位无所谓,所以就可以考虑先让x等于ans或上(1<<i)-1,这样x就是ans前缀位不改变,当前位为0,后续位都为1。接着只需要判断边权或上这个x是否仍等于x本身即可。因为此时,边权前缀和当前是0的位和x或完还是0,而其余位不论边权是多少,最后的结果都等于x本身。
总结
还是菜就多练……