AtCoder Beginner Contest 214题解
Posted 风去幽墨
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AtCoder Beginner Contest 214题解相关的知识,希望对你有一定的参考价值。
D - Sum of Maximum Weights
题意:
给出一个树,包含n个节点,n-1条边,每条边有一个权重wi。记f(u,v)为点u到点v的最短路径上出现的最大的权重,求n个点两两之间的f(u,v)之和。
题解:
假设有两个树,两个树之间通过一个权重为w的边连接,且两个树中的边的权重都小于w,那么此时满足f(u,v)==w的点对个数就有size(tree1)*size(tree2) .
根据上述假设,我们可以按照边权重大小重新加边来哦构造这颗树,在构造过程中就能够得知有多少个点对是满足f(u,v)==wi的。过程中使用并查集进行合并操作。
代码:
#include <bits/stdc++.h>
using namespace std;
#define LL long long
#define pii pair<int,int>
#define debug(X) std::cerr<<#X<<" = "<<X<<endl
#define io_init ios::sync_with_stdio(false); \\
cin.tie(0); \\
cout.tie(0);
const int maxn = 2e5+5;
const int mod = 1e9+7;
int t;
int n;
int f[maxn],sz[maxn];
struct edge
int u,v,w;
e[maxn];
bool cmp(edge a,edge b)
return a.w<b.w;
int gf(int x)
return x==f[x]?x:f[x]=gf(f[x]);
int main()
io_init;
cin>>n;
for(int i=1;i<n;++i)cin>>e[i].u>>e[i].v>>e[i].w;
for(int i=1;i<=n;++i)f[i]=i,sz[i]=1;
sort(e+1,e+n,cmp);
LL ans = 0;
for(int i=0;i<n;++i)
int x = gf(e[i].u),y=gf(e[i].v);
ans +=1ll*e[i].w*sz[x]*sz[y];
//merge
f[x]=y,sz[y]+=sz[x];
cout<<ans;
return 0;
E - Packing Under Range Regulations
题意:
有n个球,每个球要求必须要放到各自的一个区间【Li,Ri】内,并且保证每个点上最多放一个球,问有没有可行的放置方法?
题解:
个人觉得是个贪心问题。
首先,假设有两个球它们对应的区间是【La,Ra】和【Lb,Rb】,其中Ra==Rb,La<Lb;那么如果想要尽可能的满足题意,应该先安排那个球呢?肯定是b球,因为即使b球安排完后【Lb,Rb】没有空余点了,那a球依然可以安排在【La,Lb-1】上。
第二,假设我们为了尽可能的满足题意,我们每次都把球放在可以放置的最小位置,这样后面的球就会有更多的选择去放,那么能够满足题意的机会也就更大一些。所以我们如果要从小王大去放置的话,就需要优先选择右端点小的球。
所以,最终我们可以将区间按照右端点递增,右端点相同时按左端点递减的顺序排列。这样能够更大机会满足题意。
代码如何记录某个点已经被占用了呢?可以使用map,因为左右端点的范围比较大,而球的个数较小。
如何知道某个点被占用后,下一个可放点在哪里呢?可以在放置球时同时记录每个点的下一个可用点。这个使用并查集能够简单有效地维护。
代码:
#include <bits/stdc++.h>
using namespace std;
#define LL long long
#define pii pair<int,int>
const int maxn = 2e5+5;
const int mod = 1e9+7;
int t;
int n;
map<int,int> mp;
struct edge
int l,r;
e[maxn];
bool cmp(edge a,edge b)
if(a.r==b.r)
return a.l>b.l;
return a.r<b.r;
int gf(int x)
return (mp.count(x)==0||mp[x]==x)?x:mp[x]=gf(mp[x]);
int main()
io_init;
cin>>t;
while(t--)
cin>>n;
for(int i=1;i<=n;++i)cin>>e[i].l>>e[i].r;
sort(e+1,e+n+1,cmp);
mp.clear();
bool ok = true;
for(int i=1;i<=n;++i)
int now = gf(e[i].l);
if(now>e[i].r)
ok=false;
break;
mp[now]=now+1;
if(ok)
cout<<"Yes\\n";
else
cout<<"No\\n";
return 0;
F - Substrings
题意:
给出一个字符串S。现在可以从中删除一些非连续的字符(删除的每两个字符不相邻),剩下的字符构成一个子串。问最多会产生多少种不同的子串?
题解:
首先,先不去考虑删除时每两个字符不相邻的限制,想想该怎么去做?
先来计算一下,进行删除操作后会产生多少个子串(可能相同)?
设dp[i]为前i个字符能产生的子串且以第i个字符作为子串结尾。
转移方程:dp[i] = sum(dp[k]) 0<=k<=i-1
复杂度 O(n^2) / 维护sum则是O(n)
那么该怎么转移才能保证产生子串不同呢?
假设dp[i] == dp[j] 且 j < i ,并且j是离i最近的那一个(小于i的最大的那个)。为了不重复,那么dp[i]没有必要从S[J]之前的状态进行转移,因为那样做会产生重复,dp[j]在进行转移时已经产生了那些可能的子串。那么很容易想到转移方程: dp[i] = sum(dp[k]) j<=k<=i-1 ,j满足上述条件,若是不存在满足条件的j那么j=0。
复杂度 O(n^2)
虽然第一眼看的时间复杂度为O(n^2),但是实际复杂度远远低于O(n^2),实际上应该是O(m*n),其中m为字符串中不同字符的种类数(26)。因为每次在寻找满足dp[i]=dp[j]时,每一个dp[k]只会被二次使用至多26次。
好了,最后再来看一下 删除的字符不能相邻该怎么处理?
其实很简单,状态转移的时候 从i-2开始转移即可。
dp[i] = sum(dp[k]) j<=k<=i-2,j同上
代码:
#include<bits/stdc++.h>
using namespace std;
int main()
string S; cin >> S;
int N = S.size();
vector<long long> dp(N+2); dp[0] = 1;
for(int i = 0; i < N; i++)
for(int j = i-1; ; j--)
dp[i+2] = (dp[i+2] + dp[j+1]) % 1000000007;
if(j == -1 || S[j] == S[i]) break;
long long ans=0;
for(int i = 2; i < N+2; i++) ans += dp[i];
cout << ans%1000000007 << endl;
后续若是继续补题会继续添加题解。
upd 2021/8/25:
差不多就补到F题了。
欢迎评论和指正!
以上是关于AtCoder Beginner Contest 214题解的主要内容,如果未能解决你的问题,请参考以下文章
AtCoder Beginner Contest 115 题解