Codeforces Round #572 (Div. 2) 简要题解
A. Keanu Reeves
- 0,1数目不相等表示满足条件,答案为1
- 相等,则从第一个字符分割即可
B. Number Circle
- 排序,若满足(a[n]<a[n-1]+a[n-2]), 则可以构造,否则不可构造(这是显然的)
- 构造方法,假设(a[n])放在位置(k),则(a[n-1],a[n-2])放在(k-1,k+1),依次这样(a[i])成对枚举放在(k-t,k+t) 位置;注意环状和奇偶讨论即可
C. Candies!
- (a_i+a_j<20) ,所以本质糖果数是区间和/10
- 前缀和处理即可
D1. Add on a Tree
- 题意: 每次可对叶子节点x,y之间的简单路径上的边权进行同一操作(add val);问是否每条边权都能取任意值;即任意边不会存在互相约束;
- 结论: 存在度为2,则不可能(显然);反之可行(给出构造)
- 考虑叶子节点x和普通节点u,下面证明可以实现x-u路径上所有边加任意值(假设y);
考虑u的子树(以u为根)的另外叶子节点(l_1,l_2),要求(l_1,l_2,x)不再(u)的同一个子树上。如下操作:
(x-l_1 quad +y/2)
(x-l_2 quad +y/2)
(l_1-l_2 quad -y/2)
即可实现x-u路径+y(可画图观察)
-
有3可知,对于v-u边的权值可通过 x-v和x-u进行3中的处理
-
D1只需2即可解,但3,4两点对D2很有帮助
D2. Add on a Tree: Revolution
解法
- 如 D1所只,我们已经掌握了如何构造更改u-v边权的操作
- 均为非叶子节点,转化为x-u,x-v 需要6次操作
- 均为叶子节点(实际是两个节点的情况)
- u位叶子节点,需要3次操作
编程要点
- 至少需要获得非叶节点u的每个子树的一个叶子节点(以u为根);但一般dfs一次是固定的根
- 叶子节点x需要同时满足u,v的叶子节点,是u的v子树的叶子节点,而不是v的u子树叶子节点
- 自己的实现:
- 以某一节点r为根求得非叶子节点的子树叶子节点集
- 由于根的存在,某些非叶节点可能子树叶子节点集<3,那么从父节点的子树叶子节点集加入新的
- 保证了至少有3个不同的叶子节点即很好处理了
- 下面是代码
code
#include <bits/stdc++.h>
using namespace std;
#pragma GCC optimize("O3")
#define ll long long
#define ull unsigned long long
#define db(x) cout<<#x"=["<<(x)<<"]"<<endl
#define CL(a,b) memset(a,b,sizeof(a))
#define mp make_pair
#define fast() ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define fr0(i,m) for(int i=0;i<m;i++)
#define fr1(i,m) for(int i=1;i<=m;i++)
//author:fridayfang
//date:19 7月 06
const double esp=1e-8;
const int mod=1e9+7;
const double pi=acos(-1);
const int inf=0x3f3f3f3f;
const int maxn = 1e3 + 5;
const int maxm = 1e6+5;
vector<pair<int,int>> G[maxn];//带权图
vector<pair<pair<int,int>,int>> edgs;
int par[maxn];//记录父亲
bool vis[maxn];
vector<int> leaf[maxn];//每次只选取子树的叶子节点leaf[]集的第一个
vector<pair<pair<int,int>,int>> ops;//存储操作
int n;
int root;
void dfs2(int u,int fa){
par[u] = fa;
if(G[u].size()==1){leaf[u].push_back(u);return ; }
for(auto& tmp: G[u]){
int t = tmp.first;
if(t!=fa){dfs2(t,u);leaf[u].push_back(leaf[t][0]);}
}
}
void pre(){
dfs2(root,0);
for(int i=1;i<=n;i++){
if(G[i].size()>2&&leaf[i].size()==2){
int u = par[i];
for(auto& tmp: leaf[u]){
if(tmp!=leaf[i][0]&&tmp!=leaf[i][1]){
leaf[i].push_back(tmp);break;
}
}
}
}
}
void addPath(int t,int v,int val){//表示对从t(v的某个叶子节点)-v 路径上全部加x
int u = leaf[v][0], l1 = leaf[v][1], l2 = leaf[v][2];
if(t==l1) swap(u,l1);// u=t
if(t==l2) swap(u,l2);// u=t
if(u!=t) swap(u,t);
ops.push_back(mp(mp(u,l1),val/2));
ops.push_back(mp(mp(u,l2),val/2));
ops.push_back(mp(mp(l1,l2),-val/2));
}
void addEdge(int u,int v,int val){//对边u,v +x
if(par[v]==u) swap(u,v);// keep v is father of u
if(G[u].size()==1){addPath(u,v,val);return ;}
if(G[v].size()==1){addPath(v,u,val);return ;}
int t = leaf[u][0];
addPath(t,v,val);
addPath(t,u,-val);
}
void solve(){
for(int i=1;i<=n;i++){
root = i;
dfs1(root,0);
}
for(int i=0;i<n-1;i++){
int u = edgs[i].first.first, v = edgs[i].first.second, val = edgs[i].second;
addEdge(u,v,val);
}
int opNum = ops.size();
cout<<opNum<<endl;
for(int i=0;i<opNum;i++){
cout<<ops[i].first.first<<" "<<ops[i].first.second<<" "<<ops[i].second<<endl;
}
}
int main(){
fast();cin>>n;int u,v,val;
if(n==2){
cin>>u>>v>>val;
cout<<"YES
1
"<<u<<" "<<v<<" "<<val<<endl;
return 0;
}
for(int i=1;i<n;i++){
cin>>u>>v>>val;
G[u].push_back(mp(v,val)); G[v].push_back(mp(u,val));
edgs.push_back(mp(mp(u,v),val));
}
bool OK = true; int j=-1;
for(int i=1;i<=n;i++){
if(G[i].size()==2) OK = false;
if(G[i].size()>2) j = i;
}
if(!OK){cout<<"NO
";return 0;}
if(j==-1){return 0;}// no reach
cout<<"YES
";
root = j; pre();
solve();
return 0;
}
E. Count Pairs
- 小技巧分离(i,j)即可,使得(i,j)同余;
code
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define db(x) cout<<#x"=["<<(x)<<"]"<<endl
#define CL(a,b) memset(a,b,sizeof(a))
#define fast() ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define fr0(i,m) for(int i=0;i<m;i++)
#define fr1(i,m) for(int i=1;i<=m;i++)
//author:fridayfang
//date:19 7月 06
const double esp=1e-8;
const int mod=1e9+7;
const double pi=acos(-1);
const int inf=0x3f3f3f3f;
const int maxn = 3e5 + 5;
const int maxm = 1e6+5;
map<ll,ll> cnt;
ll n,p,k;
ll a[maxn];
ll pre(ll x){
ll t = x;
x = (x*x)%p;
x = (x*x)%p;
x = (x+p-(k*t)%p)%p;
return x;
}
ll solve(){
ll ans = 0;
for(ll i=1;i<=n;i++){
ll t = pre(a[i]);
ans = (ans+cnt[t]);
cnt[pre(a[i])]++;
}
return ans;
}
int main(){
fast();
cin>>n>>p>>k;
for(ll i=1;i<=n;i++)cin>>a[i];
ll res = solve();
cout<<res<<endl;
return 0;
}
F. Array Beauty
- 题意很重要:(min_{1<=i<j<=n}{|a_i-a_j|}); 而不是(i,i+1)
- 记(q_i)为(beauty>=i)的方案数;那么(sum{q_i})即为所求,因为(beauty==i)的方案被统计了(i)次
- 记(dp[i][j])表述以(a[j])结尾的长度为i的方案数;具体见代码
code
#include <bits/stdc++.h>
using namespace std;
#pragma GCC optimize("O3")
#define ll long long
#define ull unsigned long long
#define db(x) cout<<#x"=["<<(x)<<"]"<<endl
#define CL(a,b) memset(a,b,sizeof(a))
#define fast() ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define fr0(i,m) for(int i=0;i<m;i++)
#define fr1(i,m) for(int i=1;i<=m;i++)
//author:fridayfang
//date:19 7月 06
const double esp=1e-8;
const int mod=998244353;
const double pi=acos(-1);
const int inf=0x3f3f3f3f;
const int maxn = 1e3 + 5;
const int maxm = 1e6+5;
int a[maxn],n,k;
int dp[maxn][maxn],tsum[maxn][maxn];
//dp[i][j] 表示 长度为i以a[j]结尾的满足 beauty>=x 的 方案数
//tsum[i][j] 表是dp[i][1]+dp[i][2]+....dp[i][j]
int bound[maxn];// bound[i] = j 表示 a[i]...a[j] <= a[i]-x
inline int add(int x,int y){return (x+y)>=mod?(x+y-mod):(x+y);}
int solve(int x){//x>0
/*
for (int i = 0; i <= k; i++) {
for (int j = 0; j <= n; j++) {
dp[i][j] = 0;
}
}
*/
bound[0] = 0;
for(int i=1;i<=n;i++){
bound[i] = bound[i-1];
while(a[i]-a[bound[i]+1]>=x) bound[i]++;
}
tsum[1][0]=0;
for(int i=1;i<=k;i++){
for(int j=i;j<=n;j++){
if(i==1){dp[i][j] = 1; tsum[i][j] = (tsum[i][j-1]+dp[i][j]); continue;}
dp[i][j] = tsum[i-1][bound[j]];
// printf("db dp[%d][%d]=%d
",i,j,dp[i][j]);
tsum[i][j] = add(tsum[i][j-1],dp[i][j]);
}
}
//db(tsum[k][n]);
return tsum[k][n];
}
int main(){
fast();cin>>n>>k;
for(int i=1;i<=n;i++) cin>>a[i];
sort(a+1,a+1+n);
int ans = 0;
int maxt = (a[n]-a[1])/(k-1);
for(int t=1;t<=maxt;t++){
ans = add(ans, solve(t));
}
cout<<ans<<endl;
return 0;
}