Educational Codeforces Round 180 (Rated for Div. 2)
A
题意
宝藏在X或者Y,已知A的位置,问B能不能选定一个位置使得无论宝藏位置在哪都比A更快到达宝藏
思路
分类讨论
(1)A—X----Y B在X点肯定会比A位置优
(2)X—A—Y 因为在X,Y中间,B没有稳赢的策略
(3)X—Y----A B在Y点肯定会比A位置优
代码
#include<bits/stdc++.h>
using namespace std;
void solve(){
int a,x,y;
cin>>a>>x>>y;
if(x>y)swap(x,y);
if(a<x||a>y){
cout<<"YES\n";
return;
}
cout<<"NO\n";
}
signed main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int T__=1;cin>>T__;
while(T__--)solve();
return 0;
}
B
题意
通过若干次操作使得数组变成合法,每次操作可以移除相邻两个元素,并且在这个位置插入一个新的元素x满足
m
i
n
(
a
[
i
]
,
a
[
i
+
1
]
)
<
=
x
<
=
m
a
x
(
a
[
i
]
,
a
[
i
+
1
]
)
min(a[i],a[i+1])<=x<=max(a[i],a[i+1])
min(a[i],a[i+1])<=x<=max(a[i],a[i+1])
合法数组的要求:至少存在有一对相邻位置的元素绝对值差值为1
思路
首先我们可以一步都不操作,判断数组是否天然合法(是否有相邻两个数绝对值差值小于等于1)
模拟一下样例发现,如果有连续三个数A,B,C,满足min(B,C)<=A<=max(B,C),则能够在最多一次之内使数组变成合法,即删除BC再添加A,这个新产生的A和原来的A相邻,差值为0符合合法数组的要求
那么什么时候无法操作,发现只有当连续的三个数A,B,C满足A<min(B,C)或者A>max(B,C)时不能操作,那么这三个数一定满足A>B>C或者A<B<C(并且任意两个数的绝对值差值大于1,这在一开始判断是否天然合法时已经判过),那么对于整个数组来说,如果所有位置都不符合说明这个数组一定是单调递增或者单调递减的,否则就一定能在转折点及其左右两侧各一个点总共三个点满足min(B,C)<=A<=max(B,C)这样就可以操作1次就合法
综上只需要判断是否数组单调即可,如果单调则意味着无解,不单调则一定能在1次操作之内解决
代码
/*
通过若干次操作使得数组变成合法,每次操作移除相邻两个数,并且在这个位置放入x,使得min(a[i],a[i+1])<x<max(a[i],a[i+1])
合法数组的要求:至少存在有一对相邻数差值为1
如果单调递增或者单调递减且相邻两个元素绝对值>1,则无解
否则答案为1或0
*/
#include<bits/stdc++.h>
using namespace std;
// #define int long long
void solve(){
int n;
cin>>n;
vector<int>a(n+1,0);
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=2;i<=n;i++){
if(abs(a[i]-a[i-1])<=1){
cout<<"0\n";
return;
}
}
int fla=1;
for(int i=2;i<=n;i++){
if(a[i]<a[i-1]){
fla=0;
break;
}
}
if(fla){
cout<<-1<<"\n";
return;
}
fla=1;
for(int i=2;i<=n;i++){
if(a[i]>a[i-1]){
fla=0;
break;
}
}
if(fla){
cout<<-1<<"\n";
return;
}
cout<<1<<"\n";
}
signed main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int T__=1;cin>>T__;
while(T__--)solve();
return 0;
}
C
题意
AB两个人在数组上对元素进行染色。A选择三个位置上的数染红色,B选择一个位置染蓝色(可覆盖红色),A先染色完后B再染色。问A有多少种染色方案能使得B无论怎么操作都能使红色覆盖的元素和比蓝色覆盖的元素和要大
思路
假定A选择的三个元素值染色,这三个元素值为X,Y,Z且人为规定X<=Y<=Z,
则如果A要获胜需满足必须满足X+Y>Z(如果B对最大的Z染色,则A需要保证剩余两个元素的和要比Z大),并且X+Y+Z>MAX(A[])(如果B不对A染色的位置操作,而去找剩下所有位置中最大的元素,那么也要保证三个元素的值大于一个未选的最大元素值)
即有不等式 M A X ( A [ ] ) − X − Y < Z < X + Y MAX(A[])-X-Y<Z<X+Y MAX(A[])−X−Y<Z<X+Y
考虑到n不大(N<=1000),我们可以直接枚举X,Y,然后二分找有多少个满足条件的Z
还有一个隐藏条件:无论大小,在查找过程中Z一定之内出现在当前遍历到的Y的右方
代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
void solve(){
int n;
cin>>n;
vector<int>a(n+1,0);
for(int i=1;i<=n;i++)cin>>a[i];
sort(a.begin()+1,a.end());
int res=0;
for(int i=1;i<n;i++){
for(int j=i+1;j<=n;j++){
int maxa=a[n];//剩余未被A染色中的最大值
if(i==n-1&&j==n)maxa=a[n-2];
else if(j==n)maxa=a[n-1];
int it1=upper_bound(a.begin()+1,a.end(),maxa-a[i]-a[j])-a.begin();//左边界
int it2=lower_bound(a.begin()+1,a.end(),a[i]+a[j])-a.begin()-1;//右边界
it1=max(j+1,it1);
if(it1>it2)continue;
res+=it2-it1+1;
}
}
cout<<res<<"\n";
}
signed main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int T__=1;cin>>T__;
while(T__--)solve();
return 0;
}
D
题意
给定一棵树构造树上边的方向,需满足只有n对(u,v)满足u到v可达
思路
如果有一个节点度数==2,那么只需要将这个节点下方的所有子树边全部翻转即可
默认奇数层节点连向父亲节点,偶数层的父亲节点连向偶数层节点(注意根节点)
#include<bits/stdc++.h>
using namespace std;
const int mxn=1e6+5;
vector<int>edge[mxn];
int in[mxn];
vector<pair<int,int>>ans;
int fla=-1;
void dfs(int u,int fa,int dep,int zs){//当前在哪个节点,他的父亲节点是谁,当前节点深度,是否是标记节点的子树
if(u==fla)zs=1;
for(auto v:edge[u]){
if(v==fa)continue;
if((dep+1)%2==1){//v节点在dep+1层,考虑v和他的父亲节点u的边的连向
if(zs)ans.push_back({u,v});
else ans.push_back({v,u});
}
else{
if(zs)ans.push_back({v,u});
else ans.push_back({u,v});
}
dfs(v,u,dep+1,zs);
}
}
void solve(){
int n;
cin>>n;
ans.clear();
for(int i=0;i<=n;i++)edge[i].clear(),in[i]=0;
for(int i=1;i<n;i++){
int u,v;
cin>>u>>v;
edge[u].push_back(v);
edge[v].push_back(u);
in[u]++,in[v]++;
}
fla=-1;
for(int i=1;i<=n;i++){
if(in[i]==2){
fla=i;
break;
}
}
if(fla==-1){
cout<<"NO\n";
return;
}
cout<<"YES\n";
int rt=1;
if(fla==rt)rt++;
dfs(rt,0,0,0);
for(auto [u,v]:ans)cout<<u<<" "<<v<<"\n";
}
signed main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int T__=1;cin>>T__;
while(T__--)solve();
return 0;
}
代码
#include<bits/stdc++.h>
using namespace std;
// #define int long long
const int mxn=1e6+5;
vector<int>edge[mxn];
int in[mxn];
vector<pair<int,int>>ans;
int fla=-1;
void dfs(int u,int fa,int dep,int zs){//当前在哪个节点,他的父亲节点是谁,当前节点深度,是否是标记节点的子树
if(u==fla)zs=1;
for(auto v:edge[u]){
if(v==fa)continue;
if((dep+1)%2==1){//v节点在dep+1层,考虑v和他的父亲节点u的边的连向
if(zs)ans.push_back({u,v});
else ans.push_back({v,u});
}
else{
if(zs)ans.push_back({v,u});
else ans.push_back({u,v});
}
dfs(v,u,dep+1,zs);
}
}
void solve(){
int n;
cin>>n;
ans.clear();
for(int i=0;i<=n;i++)edge[i].clear(),in[i]=0;
for(int i=1;i<n;i++){
int u,v;
cin>>u>>v;
edge[u].push_back(v);
edge[v].push_back(u);
in[u]++,in[v]++;
}
fla=-1;
for(int i=1;i<=n;i++){
if(in[i]==2){
fla=i;
break;
}
}
if(fla==-1){
cout<<"NO\n";
return;
}
cout<<"YES\n";
int rt=1;
if(fla==rt)rt++;
dfs(rt,0,0,0);
for(auto [u,v]:ans)cout<<u<<" "<<v<<"\n";
}
signed main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int T__=1;cin>>T__;
while(T__--)solve();
return 0;
}
E
题意
考虑一棵有根树,每个结点可以被染成蓝色、绿色或黄色。一个染色方案是美丽的,当且仅当:
- 根节点被染成绿色;
- 所有的蓝色和绿色结点构成一个点集,点集中的点两两之间的路径上都没有黄色结点;
- 所有的黄色和绿色结点构成一个点集,点集中的点两两之间的路径上都没有蓝色结点。
给你一个整数 m,问一棵恰有 m 种美丽的染色方案的有根树最少有多少个结点?
思路
我们发现如果一个节点染蓝色或者黄色,那么这个节点及其子树颜色都是确定下来的,只有当该节点染绿色时,才会再考虑他的儿子节点怎么染色,对于一棵以u为根的树来说,他的方案数是各个(子节点所在子树的方案数+2)的乘积,即为:
f u = ∏ v ∈ son of u ( f v + 2 ) f_u = \prod_{v \in \text{son of } u} (f_v + 2) fu=∏v∈son of u(fv+2)(根节点只能为绿色,一个节点从根节点变成非根节点对于他的子树来说的影响是方案数+2)
方案数大的树可以由方案数为小的树合并得到,所以可以递推
代码
#include<bits/stdc++.h>
using namespace std;
const int mxn=5e5+5;
int dp[mxn];//恰好有i种染色方案所需的最少点数
void init(){
memset(dp,0x3f,sizeof dp);
dp[1]=1;//显然
//假设当前子树的方案数为i,那么我可以再选一棵方案数为j的子树合并为方案数(i+2)*j
for(int i=1;i<mxn;i++){//枚举染色方案为i的子树1
//因为子树1在合并的过程中是主动合并到子树2上,根节点是可以活动的,所以造成多2种方案(根节点还能染除绿色外的其他两种颜色,对于其他两种颜色来说只要根节点颜色确定,整棵树的颜色就会和根节点颜色相同)
for(int j=1;(i+2)*j<mxn;j++){//枚举染色方案为j的子树2
dp[(i+2)*j]=min(dp[(i+2)*j],dp[i]+dp[j]);//两棵树合并后方案数为(i+2)*j,节点数为dp[i]+dp[j]
}
}
}
void solve(){
int m;
cin>>m;
cout<<(dp[m]==0x3f3f3f3f ?-1:dp[m])<<"\n";
}
signed main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int T__=1;cin>>T__;
init();
while(T__--)solve();
return 0;
}