2021 ICPC 南京站+上海站 部分题解
Posted 钟钟终
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2021 ICPC 南京站+上海站 部分题解相关的知识,希望对你有一定的参考价值。
再一次发现一个无比无比菜的自己。。。。
H. Crystalfly
本题难得倒不是思路,是代码的实现。看了下其他大佬的实现,真的惊呼太优雅了。
思路:
1.对于这样的一棵树来说,t的值只能取1到3,可发现2秒实在是没有价值。如果一个点是3s,那说明可去其他1s或3s的点拿完,再去拿这个点的值。
2.如果从x点出发,走向z点,而y点的值是3s,说明可以去完z点转向去y点,这样势必会放弃z点孩子结点的点数。
3.因此采用树形dp来记录各种状态。
//dp[u][0]
表示不取以u为根的孩子的最大点数
//dp[u][1]
表示取以u为根获得的最大点数
状态的转移:
一般情况:dp[u][1]=max(dp[u][1],dp[u][0]+a[v]);
表示走向哪个孩子,获得的点数最大
如果一个孩子结点的t值为3:说明这个结点y的权值是可以拿到的,z是另外一个以u为根结点权值最大or次大的点:dp[u][0]+a[y]+dp[z][0]-(dp[z][1]-a[z])
,减去的那一部分是dp[u][0]包含的重复的部分。
此外还需对t为3的情况分两种情况讨论下。
#include <bits/stdc++.h>
#define int long long
#define endl '\\n'
#define ios (ios::sync_with_stdio(false),cin.tie(0),cout.tie(0))
using namespace std;
const int inf=1e18;
const int N=7e5+5;
int n,a[N],t[N],dp[N][2],p[N];
vector<int>e[N];
//dp[u][0]表示不取以u为根的孩子的最大点数
//dp[u][1]表示取以u为根获得的最大点数
void dfs(int u,int fa)
dp[u][1]=dp[u][0]=a[u];
int mx1=-inf,mx2=-inf;
for(int v:e[u])
if(v==fa) continue;
dfs(v,u);
dp[u][0]+=dp[v][1]-a[v];
int tmp=dp[v][0]-dp[v][1]+a[v];
if(tmp>mx1) mx2=mx1,mx1=tmp; //最大值
else if(tmp>mx2) mx2=tmp; //次大值
for(int v:e[u])
if(v==fa) continue;
dp[u][1]=max(dp[u][1],dp[u][0]+a[v]);
if(t[v]==3)
if(dp[v][0]-dp[v][1]+a[v]==mx1)
dp[u][1]=max(dp[u][1],dp[u][0]+mx2+a[v]);
else
dp[u][1]=max(dp[u][1],dp[u][0]+mx1+a[v]);
void solve()
cin>>n;
for(int i=1;i<=n;i++) e[i].clear(),dp[i][0]=dp[i][1]=0;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++) cin>>t[i];
for(int i=1;i<n;i++)
int u,v;cin>>u>>v;
e[u].push_back(v);
e[v].push_back(u);
dfs(1,0);
cout<<dp[1][1]<<endl;
signed main()
ios;
int T;cin>>T;
while(T--)
solve();
return 0;
C. Klee in Solitary Confinement
思路:
1.统计各个数在不同位置上出现次数的前缀和。对于x来说,它可由x和x-k得到,而一个区间内的众数可通过枚举不同的区间得到。
2.sum[i][0]
表示该数字x在下标i出现了几次,sum[i][1]
表示该数字x-k在下标i时出现了几次
公式:sum[e[i].size()][0]+sum[r][1]-sum[l-1][1]-(sum[r][0]-sum[l-1][0])
化简
得到:sum[e[i].size()][0]+sum[r][1]-sum[r][0]+sum[l-1][0]-sum[l-1][1]
#include <bits/stdc++.h>
//#define int long long
#define endl '\\n'
#define ios (ios::sync_with_stdio(false),cin.tie(0),cout.tie(0))
using namespace std;
const int inf=1e18;
const int N=1e6+5;
int n,k,a[N],sum[N*4][2],mx,g,ans;
vector<int>e[N*4];
void solve()
cin>>n>>k;
for(int i=1;i<=n;i++)
cin>>a[i];a[i]+=2e6;
e[a[i]].push_back(a[i]);
e[a[i]+k].push_back(a[i]);
g=max(g,a[i]+k);
mx=max(mx,(int)e[a[i]].size(),(int)e[a[i]+k].size());
if(!k)
cout<<mx/2<<endl;return;
for(int i=0;i<=g;i++)
if(e[i].size()==0) continue;
for(int j=0;j<e[i].size();j++)
sum[j+1][0]=sum[j][0]+(e[i][j]==i);
sum[j+1][1]=sum[j][1]+(e[i][j]!=i);
int tmp=sum[1][0]-sum[1][1];
for(int j=1;j<=e[i].size();j++)
tmp=max(tmp,sum[j-1][0]-sum[j-1][1]);
ans=max(ans,sum[e[i].size()][0]+sum[j][1]-sum[j][0]+tmp);
cout<<ans<<endl;
signed main()
ios;
//int T;cin>>T;
//while(T--)
solve();
return 0;
D Strange_Fractions
注意开根号后的值为正数,并且要保证方程有解。
#include<bits/stdc++.h>
#define int long long
//#define ll long long
#define endl '\\n'
#define For(i,a,b) for(i=(a);i<=(b);++i)
#define ios (ios::sync_with_stdio(false),cin.tie(0),cout.tie(0))
#define down 0.996
using namespace std;
const int N=2e6+5;
const int inf=1e18;
const int mod=1e9+7;
int p,q,a,b;
void solve()
cin>>p>>q;
int gcd=__gcd(p,q);
p=p/gcd;
q=q/gcd;
int g=sqrt(p+2*q);
if(g*g!=p+2*q)
cout<<0<<" "<<0<<endl;return;
int tmp=g*g-4*q;
if(tmp<0)
cout<<0<<" "<<0<<endl;return;
int p=sqrt(tmp);
if(p*p!=tmp)
cout<<0<<" "<<0<<endl;return;
int x1=g-p,x2=g+p;
if(x1%2==0&&x1>=1)
cout<<x1/2<<" "<<g-x1/2<<endl;
else if(x2%2==0&&x2>=1)
cout<<x2/2<<" "<<g-x2/2<<endl;
else
cout<<0<<" "<<0<<endl;
signed main()
//ios;
int T;cin>>T;
while(T--)
solve();
return 0;
Edge Groups
思路:
1.若子树结点v连接有奇数条边,则需要占用连接u的一条边;
2.若为偶数条边,则每次从n条边中选取两条边,则会有n/2个配对,会产生(n/2)!的重复。其实列举出2、4、6……的情况后不难看出,方案数为小于等于n的奇数相乘。
3.子树结点边的选取情况可能会占用上一层偶数结点层的结点,需要统计的是可以对父节点产生贡献的可分配的边
#include <bits/stdc++.h>
#define int long long
#define endl '\\n'
#define ios (ios::sync_with_stdio(false),cin.tie(0),cout.tie(0))
using namespace std;
const int inf=1e18;
const int N=1e6+5;
const int mod=998244353;
int n,a[N],dp[N];
vector<int>e[N];
int dfs(int u,int fa)
dp[u]=1;
int cnt=0;
for(int v:e[u])
if(v==fa) continue;
if(!dfs(v,u)) cnt++;
dp[u]=dp[u]*dp[v]%mod;
for(int i=1;i<=cnt;i+=2) dp[u]=dp[u]*i%mod;
return cnt&1;
void solve()
cin>>n;
for(int i=1;i<n;i++)
int u,v;cin>>u>>v;
e[u].push_back(v);
e[v].push_back(u);
dfs(1,0);
cout<<dp[1]<<endl;
signed main()
//ios;
//int T;cin>>T;
//while(T--)
solve();
return 0;
以上是关于2021 ICPC 南京站+上海站 部分题解的主要内容,如果未能解决你的问题,请参考以下文章
[计蒜客] ACM-ICPC 2018 南京赛区网络预赛 | 部分题解 | 线段树 + 线性筛 + 最短路