前言
第一次赛时出E!!第一次rank进前一千!!!我就说之前是因为我生病状态不好()
一、A - Candy Cookie Law
cpp
#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
cpp
#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
cpp
#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的二分真用不明白......
cpp
#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
现在也就写写板题了......
cpp
#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
群友是怎么想到这个思路的......
cpp
#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;
}
这个题直接就是注意到每个数字的左右关系可以形成一张有向图,那么就可以在这张有向图上进行拓扑排序,统计出每个数字可以存在的位置区间,然后根据每个数的区间差分一下求前缀和即可。因为同时存在左右关系,所以需要建正反图分别跑一遍拓扑排序。
这种拓扑排序的题多少得对这种关系很敏感才能注意到......
总结
再接再厉,加油!