AtCoder Beginner Contest 430(ABCDEF)
前言
第一次赛时出E!!第一次rank进前一千!!!我就说之前是因为我生病状态不好()
一、A - Candy Cookie Law
#include <bits/stdc++.h>
using namespace std;/* /\_/\
* (= ._.)
* / > \>
*/#define endl '\n'
#define dbg(x) cout<<#x<<" "<<x<<endl
#define vdbg(a) cout<<#a<<endl; for(auto x:a) cout<<x<<" ";cout<<endl
#define INF 1e9
#define INFLL 1e18
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;void solve()
{int a,b,c,d;cin>>a>>b>>c>>d;if(c<a||c>=a&&d>=b){cout<<"No"<<endl;}else{cout<<"Yes"<<endl;}
}void init()
{
}signed main()
{ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);int t=1;//cin>>t;init();while(t--){solve(); }return 0;
}
这个题就是根据化简后的条件判断一下即可,注意合法的时候要输出No()
二、B - Count Subgrid
#include <bits/stdc++.h>
using namespace std;/* /\_/\
* (= ._.)
* / > \>
*/#define endl '\n'
#define dbg(x) cout<<#x<<" "<<x<<endl
#define vdbg(a) cout<<#a<<endl; for(auto x:a) cout<<x<<" ";cout<<endl
#define INF 1e9
#define INFLL 1e18
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;void solve()
{int n,m;cin>>n>>m;vector<string>a(n);for(int i=0;i<n;i++){cin>>a[i];}set<string>st;for(int i=0;i<=n-m;i++){for(int j=0;j<=n-m;j++){string s;for(int l=i;l<i+m;l++){for(int r=j;r<j+m;r++){s+=a[l][r];}}st.insert(s);}}cout<<st.size()<<endl;
}void init()
{
}signed main()
{ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);int t=1;//cin>>t;init();while(t--){solve(); }return 0;
}
因为数据范围不大,所以直接暴力枚举网格的两点往set里加即可。
三、C - Truck Driver
#include <bits/stdc++.h>
using namespace std;/* /\_/\
* (= ._.)
* / > \>
*/#define endl '\n'
#define dbg(x) cout<<#x<<" "<<x<<endl
#define vdbg(a) cout<<#a<<endl; for(auto x:a) cout<<x<<" ";cout<<endl
#define INF 1e9
#define INFLL 1e18
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;void solve()
{int n,a,b;cin>>n>>a>>b;string s;cin>>s;s=" "+s;ll ans=0;vector<int>pa;vector<int>pb;for(int i=1;i<=n;i++){if(s[i]=='a'){pa.push_back(i);}else{pb.push_back(i);}int left=0;if(pb.size()>=b){left=pb[pb.size()-b];}int right=0;if(pa.size()>=a){right=pa[pa.size()-a];}if(left<right){ans+=right-left;}}cout<<ans<<endl;
}void init()
{
}signed main()
{ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);int t=1;//cin>>t;init();while(t--){solve(); }return 0;
}
这个题肯定还是考虑固定右端点,然后去找所有合法的左端点。所以考虑分别记录每个a和b的出现位置,那么从当前点往左找b个b的位置就是左端点的最左边界,往左找a个a的位置就是左端点的最右边界,那么中间的每个位置都可以作为左端点产生贡献。
四、D - Neighbor Distance
set的二分真用不明白……
#include <bits/stdc++.h>
using namespace std;/* /\_/\
* (= ._.)
* / > \>
*/#define endl '\n'
#define dbg(x) cout<<#x<<" "<<x<<endl
#define vdbg(a) cout<<#a<<endl; for(auto x:a) cout<<x<<" ";cout<<endl
#define INF 1e9
#define INFLL 1e18
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;void solve()
{int n;cin>>n;vector<ll>a(n+1);for(int i=1;i<=n;i++){cin>>a[i];}set<ll>st;st.insert(0);map<ll,ll>dis;dis[0]=INFLL;for(int i=1;i<=n;i++){dis[a[i]]=INFLL;}ll ans=dis[0];for(int i=1;i<=n;i++){ll pos=a[i];st.insert(pos);auto pre=st.lower_bound(pos);pre=prev(pre);auto nxt=st.upper_bound(pos);if(nxt==st.end()){ans-=dis[*pre];dis[*pre]=min(dis[*pre],pos-*pre);dis[pos]=pos-*pre;ans+=dis[*pre]+dis[pos];}else{ans-=dis[*pre]+dis[*nxt];dis[*pre]=min(dis[*pre],pos-*pre);dis[*nxt]=min(dis[*nxt],*nxt-pos);dis[pos]=min(pos-*pre,*nxt-pos);ans+=dis[*pre]+dis[*nxt]+dis[pos];}cout<<ans<<endl;}
}void init()
{
}signed main()
{ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);int t=1;//cin>>t;init();while(t--){solve(); }return 0;
}
很容易可以注意到,在一个位置插入一个人,可能影响的只有其相邻的两人。所以考虑直接用一个set维护目前的所有人,然后每次去二分前一个人和后一个人,只对这两个人进行修改即可。
五、E - Shift String
现在也就写写板题了……
#include <bits/stdc++.h>
using namespace std;/* /\_/\
* (= ._.)
* / > \>
*/#define endl '\n'
#define dbg(x) cout<<#x<<" "<<x<<endl
#define vdbg(a) cout<<#a<<endl; for(auto x:a) cout<<x<<" ";cout<<endl
#define INF 1e9
#define INFLL 1e18
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;const int base=499;const int MAXN=1e6+6;vector<ll>power(MAXN);ll subHash(int l,int r,vector<ll>&hash)
{ll ans=hash[r];if(l>1){ans-=hash[l-1]*power[r-l+1];}return ans;
}void solve()
{string a,b;cin>>a>>b;int n=a.length();a=" "+a;b=" "+b;vector<ll>hash1(n+1);vector<ll>hash2(n+1);for(int i=1;i<=n;i++){hash1[i]=hash1[i-1]*base+a[i]-'a'+1;hash2[i]=hash2[i-1]*base+b[i]-'a'+1;}if(hash1[n]==hash2[n]){cout<<0<<endl;return ;}for(int i=1;i<=n;i++){if(subHash(1,i,hash1)==subHash(n-i+1,n,hash2)&&subHash(i+1,n,hash1)==subHash(1,n-i,hash2)){cout<<i<<endl;return ;}} cout<<-1<<endl;
}void init()
{power[0]=1;for(int i=1;i<MAXN;i++){power[i]=power[i-1]*base;}
}signed main()
{ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);int t=1;cin>>t;init();while(t--){solve(); }return 0;
}
这个题其实看到的第一眼就能发现是字符串哈希的板题。不难发现,当把A串若干个字符移动到后面之后,若此时能和B串匹配,那么就说明A串原始的后部分和B串前部分一样,A串原始的前部分和B串的后部分一样。那么为了快速匹配两字符串,就可以使用字符串哈希,之后枚举一下A串把哪些字符移动到后面即可。
六、F - Back and Forth Filling
群友是怎么想到这个思路的……
#include <bits/stdc++.h>
using namespace std;/* /\_/\
* (= ._.)
* / > \>
*/#define endl '\n'
#define dbg(x) cout<<#x<<" "<<x<<endl
#define vdbg(a) cout<<#a<<endl; for(auto x:a) cout<<x<<" ";cout<<endl
#define INF 1e9
#define INFLL 1e18
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;void solve()
{int n;cin>>n;string s;cin>>s;s=" "+s;vector<vector<int>>g(n+1);vector<int>deg(n+1);for(int i=1;i<n;i++){if(s[i]=='L'){g[i+1].push_back(i);deg[i]++;}else{g[i].push_back(i+1);deg[i+1]++;}}queue<int>q;for(int i=1;i<=n;i++){if(deg[i]==0){q.push(i);}}vector<int>left(n+1);while(!q.empty()){int u=q.front();q.pop();for(auto v:g[u]){left[v]+=left[u]+1;if(--deg[v]==0){q.push(v);}}}for(int i=1;i<=n;i++){g[i].resize(0);}deg.resize(n+1);for(int i=1;i<n;i++){if(s[i]=='R'){g[i+1].push_back(i);deg[i]++;}else{g[i].push_back(i+1);deg[i+1]++;}}for(int i=1;i<=n;i++){if(deg[i]==0){q.push(i);}}vector<int>right(n+1);while(!q.empty()){int u=q.front();q.pop();for(auto v:g[u]){right[v]+=right[u]+1;if(--deg[v]==0){q.push(v);}}}vector<int>ans(n+2);for(int i=1;i<=n;i++){int l=1+left[i];int r=n-right[i];ans[l]++;ans[r+1]--;}for(int i=1;i<=n;i++){ans[i]+=ans[i-1];}for(int i=1;i<=n;i++){cout<<ans[i]<<" ";}cout<<endl;
}void init()
{
}signed main()
{ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);int t=1;cin>>t;init();while(t--){solve(); }return 0;
}
这个题直接就是注意到每个数字的左右关系可以形成一张有向图,那么就可以在这张有向图上进行拓扑排序,统计出每个数字可以存在的位置区间,然后根据每个数的区间差分一下求前缀和即可。因为同时存在左右关系,所以需要建正反图分别跑一遍拓扑排序。
这种拓扑排序的题多少得对这种关系很敏感才能注意到……
总结
再接再厉,加油!
