Codeforces Round #627 (Div. 3)【ABCDEF】(题解)
[toc]
涵盖知识点:思维、dp、树形dp。
比赛链接:传送门
A - Yet Another Tetris Problem
题意: 俄罗斯方块
题解: 判断所有数的奇偶性
Accept Code:
#include <bits/stdc++.h>
using namespace std;
const int maxn=110;
int a[maxn];
int main(){
int t;
cin>>t;
while(t--){
int n;
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
bool flag=true;
int base=a[1]&1;
for(int i=2;i<=n;i++){
if((a[i]&1)!=base){
flag=false;
break;
}
}
puts(flag?"YES":"NO");
}
return 0;
}
B - Yet Another Palindrome Problem
题意: 问能否找一个子序列,其为长度为3的回文串
题解: 两个相同数中间还有任意的数字即可。
**Accept Code:**写的有点丑。。。讲道理$n^2$扫一遍就完事了
#include <bits/stdc++.h>
using namespace std;
const int maxn=5050;
int a[maxn];
vector<int> v[maxn];
int main(){
int t;
cin>>t;
while(t--){
for(auto & i : v){
i.clear();
}
int n;
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
v[a[i]].push_back(i);
}
bool flag=false;
for(int i=1;i<maxn;i++){
if(v[i].size()==0||v[i].size()==1)continue;
if(v[i].size()>2){
flag=true;
break;
}
for(int j=1;j<v[i].size();j++){
if(v[i][j]-v[i][j-1]>1){
flag=true;
goto st;
}
}
}
st:
puts(flag?"YES":"NO");
}
return 0;
}
C - Frog Jumps
题意: 一个蛤在第$i$块只能根据该块的指定方向跳$[1,d]的距离$求最小的$d$使得蛤能从$0$跳到$n+1$。
题解: 找最长的连续L串。长度再加1就行了。证明自行脑补。
Accept Code:
#include <bits/stdc++.h>
using namespace std;
const int maxn=5050;
int a[maxn];
int main(){
int t;
cin>>t;
while(t--){
string s;
cin>>s;
s='R'+s;
int cnt=0,ans=0;
for(char i:s){
if(i=='L'){
cnt++;
}
else{
ans=max(ans,cnt);
cnt=0;
}
}
ans=max(ans,cnt);
cout<<ans+1<<"\n";
}
return 0;
}
D - Pair of Topics
题意: 问有多少对$(i,j)$使得$a_i+a_j>b_i+b_j$
题解: 移项得到$a_i-b_i>b_j-a_j$。我们令$c_i=a_i-b_i$,然后将其从大到小排序。双指针$i,j$分别从左右扫描。当$c_i>-c_j$时,对答案有$j-i$的贡献。当$i\ge j$时结束扫描即可。
Accept Code:
#include <bits/stdc++.h>
using namespace std;
const int maxn=2e5+10;
int a[maxn],b[maxn],c[maxn];
typedef long long ll;
int main(){
int n;
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
for(int i=1;i<=n;i++) {
cin >> b[i];
c[i]=a[i]-b[i];
}
sort(c+1,c+1+n,greater<int>());
int pos=n;
ll ans=0;
for(int i=1;i<=n;i++){
while (c[i]+c[pos]<=0&&pos>i)pos--;
if(i>=pos)break;
ans+=1ll*pos-i;
}
cout<<ans<<"\n";
return 0;
}
E - Sleeping Schedule
题意: 现在一天$h$小时,总共要睡$n$次,一次睡一天,第$i$次 醒来之后可以选择$a_i$或者$a_i-1$个小时以后接着睡。如果醒来的时候在区间$[l,r]$内,我们认为睡眠是好的。问最多能睡几次好的。
题解: $dp_{i,j}$描述睡了$i$次醒来的时候是$j$的最优情况。初值:\(dp_{0,0}=0,x=(j+a[i]-1)mod\ h,y=(j+a[i])mod\ h\)
转移方程:
$$\begin
dp_{i,x}=max(dp_{i-1,j}+[l\le x \le r])\
dp_{i,y}=max(dp_{i-1,j}+[l\le y \le r])
\end
\[
所求值:$max(dp[n])$
**Accept Code:**
```cpp
#include <bits/stdc++.h>
using namespace std;
const int maxn=2010,inf=0x3f3f3f3f;
int dp[maxn][maxn],a[maxn];
int n,h,l,r;
bool check(int x){
return x>=l&&x<=r;
}
int main(){
cin>>n>>h>>l>>r;
for (int i = 1; i <= n; i++) {
cin>>a[i];
}
memset(dp,-inf,sizeof dp);
dp[0][0]=0;
for(int i=1;i<=n;i++){
for(int j=0;j<h;j++){
int x=(j+a[i]-1)%h;
dp[i][x]=max(dp[i][x],dp[i-1][j]+check(x));
x++;
x%=h;
dp[i][x]=max(dp[i][x],dp[i-1][j]+check(x));
}
}
int ans=0;
for(int i=0;i<h;i++)
ans=max(ans,dp[n][i]);
cout<<ans<<"\n";
return 0;
}
```
## [F - Maximum White Subtree](http://codeforces.com/contest/1324/problem/F)
**题意:** 一棵无根树,有黑白两种点,问每个点的所有子树中白点数-黑点数的最大值。
**题解:** 记黑点为-1,白点为1.默认以1号点为根$dfs$扫一遍,从叶子节点向根做一遍树形dp。
转移方程:$dp_u=\displaystyle\sum_{v\in son_u}max(dp_v,0)$
再次$dfs$扫描一遍,在向下扫描的过程中,我们先假设断开$u,v$之间的连接,那么$ans_u=max(ans_u-max(dp_v,0),0)$,那么$ans_v=dp_v+ans_u$。好像有点绕。。画个图脑补一下就好了。
**Accept Code:**
```cpp
#include <bits/stdc++.h>
using namespace std;
const int maxn=2e5+10;
int a[maxn],ans[maxn],dp[maxn];
vector<int> edg[maxn];
void dfs1(int root,int fa){
dp[root]=a[root];
for(auto v:edg[root]){
if(v==fa)continue;
dfs1(v,root);
dp[root]+=max(dp[v],0);
}
}
void dfs2(int root,int fa,int res){
ans[root]=dp[root]+res;
for(auto v:edg[root]){
if(v==fa)continue;
dfs2(v,root,max(ans[root]-max(dp[v],0),0));
}
}
int main(){
int n;
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
if(!a[i])a[i]=-1;
}
for(int i=1;i<=n-1;i++){
int u,v;
cin>>u>>v;
edg[u].push_back(v);
edg[v].push_back(u);
}
dfs1(1,0);
dfs2(1,0,0);
for(int i=1;i<=n;i++){
cout<<ans[i]<<" ";
}
return 0;
}
```
\]