Codeforces Round #635 (Div. 1)
A. Linova and Kingdom (CF 1336 A)
题目大意
给定一棵个节点(从开始)的树,号节点为根。每个节点要么是工厂要么是旅游景点(包括根)。给定一个数,表示将个节点变为工厂,最大化每个工厂到根节点的路径上的旅游景点数量的和。
解题思路
很显然可以贪心,但是我们对于工厂位置的选择很显然从叶子节点开始,但稍加思索下可以发现难以抉择,在状态转移的时候比较复杂。
我们可以反过来,考虑旅游景点的选择。很显然我们从根节点开始考虑。当我们选择一个节点设为旅游景点的时候,最优情况下,这个节点到根节点中的所有点一定不是工厂。
基于这个事实,我们可以很容易算出,一个节点设为旅游景点的话,它对答案的贡献即为,其中表示以为根节点的子树的孩子数量(不包括它本身),表示节点的深度(从开始)。
这两个数组一遍即可求出,然后排序,前大的数的和即为答案。
神奇的代码
#include <bits/stdc++.h> using namespace std; typedef long long LL; template <typename T> void read(T &x) { int s = 0, c = getchar(); x = 0; while (isspace(c)) c = getchar(); if (c == 45) s = 1, c = getchar(); while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar(); if (s) x = -x; } template <typename T> void write(T x, char c = ' ') { int b[40], l = 0; if (x < 0) putchar(45), x = -x; while (x > 0) b[l++] = x % 10, x /= 10; if (!l) putchar(48); while (l) putchar(b[--l] | 48); putchar(c); } void DFS(int u,int fa,int deep[],int son[],vector<int> edge[]){ for(auto v:edge[u]){ if (v==fa) continue; deep[v]=deep[u]+1; ++son[u]; DFS(v,u,deep,son,edge); son[u]+=son[v]; } } int main(void) { int n,k; read(n); read(k); vector<int> edge[n+1]; for(int u,v,i=1;i<n;++i){ read(u); read(v); edge[u].push_back(v); edge[v].push_back(u); } int deep[n+1]={0}; int son[n+1]={0}; DFS(1,1,deep,son,edge); vector<int> val; for(int i=1;i<=n;++i){ val.push_back(son[i]-deep[i]); } sort(val.begin(),val.end(),greater<int>()); LL ans=0; for(int i=0;i<n-k;++i) ans+=(LL)val[i]; write(ans,'\n'); return 0; }
B. Xenia and Colorful Gems (CF 1336 B)
题目大意
给定三组数,要求从每组数中选择一个数,使得这三个数的俩俩差的平方和最小。即求。
解题思路
三个数从小到大排序有左中右三种情况。
那么我们枚举组取出来的数是中间的,组是最小的,组是最大的。
然后枚举组里的数,找出组里第一个小于等于的数,找出组里第一个大于等于的数,计算它们俩俩差的平方和取最小值即可。
其实找和可以不用用俩指针扫也行。
神奇的代码
#include <bits/stdc++.h> using namespace std; typedef long long LL; template <typename T> void read(T &x) { int s = 0, c = getchar(); x = 0; while (isspace(c)) c = getchar(); if (c == 45) s = 1, c = getchar(); while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar(); if (s) x = -x; } template <typename T> void write(T x, char c = ' ') { int b[40], l = 0; if (x < 0) putchar(45), x = -x; while (x > 0) b[l++] = x % 10, x /= 10; if (!l) putchar(48); while (l) putchar(b[--l] | 48); putchar(c); } LL cal(LL a,LL b,LL c){ return (a-b)*(a-b)+(a-c)*(a-c)+(b-c)*(b-c); } void work(vector<LL> num[],int a,int b,int c,LL &ans){ for(auto i:num[a]){ auto l=upper_bound(num[b].begin(),num[b].end(),i); if (l==num[b].begin()) continue; --l; auto r=lower_bound(num[c].begin(),num[c].end(),i); if (r==num[c].end()) continue; ans=ans==-1?cal(i,*l,*r):min(ans,cal(i,*l,*r)); } } int main(void) { int t; read(t); while(t--){ LL ans=-1; int cnt[3]={0}; for(int i=0;i<3;++i) read(cnt[i]); vector<LL> num[3]; for(int i=0;i<3;++i){ for(int u,j=0;j<cnt[i];++j){ read(u); num[i].push_back((LL)u); } sort(num[i].begin(),num[i].end()); } work(num,0,1,2,ans); work(num,0,2,1,ans); work(num,1,0,2,ans); work(num,1,2,0,ans); work(num,2,0,1,ans); work(num,2,1,0,ans); write(ans,'\n'); } return 0; }
C. Kaavi and Magic Spell (CF 1336 C)
题目大意
给定两个字符串,要求通过以下两个操作来构造一个字符串
- 取的首字符放到的首部
- 取的首字符放到的尾部
可以进行的操作数小于等于,为串的长度。
如果串是串的前缀,我们说串是具有膜法的。问你有多少种方法去构造一个具有膜法的串。答案模。
解题思路
如果我们从首字母开始考虑构造,当字母放到首部的时候,会对我们判断串是否为串的前缀有很大影响,还会发现转移具有后效性,我们得换个方向取考虑。
从最后一个字母来考虑的话,由于构造的串的长度是已知的,这个字母只能放在当前考虑区间的首部或尾部,然后我们就可以缩小考虑范围继续考虑下一个字母,是同样的子问题。
设表示构造串的(从开始)的所有方法中,满足串是膜法的方法的数量。
转移就很简单了。
对于当前考虑的字母,如果或者(即当前位不会影响判断串是否为的前缀或者填了是保持串是串的前缀的必要条件),则字母可以放在第位,此时。
如果或者(即当前位不会影响判断串是否为的前缀或者填了是保持串是串的前缀的必要条件),则字母可以放在第位,此时
我们枚举操作数量~(为串的长度),求得对应的求和即是答案。
然而时间复杂度为会炸。
因为求一次的时间复杂度为是已经是它的下限了,我们只能从枚举操作数量去优化。
我们发现,如果当操作数量为时,求到了一些能使串具有膜法的方法数量,当操作数变为时,我们把额外的那个字母放到串的最后面,以前求到的方法仍然能够使得串具有膜法。当然放到前面我们就得重新算了。
那么我们可以直接算,在算的时候,我们记录一个数,表示我们把前个字母都放到了后面,第个字母放到了前面,这样,由于我们是从最后考虑的字母,这前个字母显得“可有可无”,它们存不存在不会对我们的判断造成影响,而是会使得答案翻倍(当然)。
当我们知道有个字母放到了后面,而目前考虑的区间的答案已经知道为,那么这个字母的有无(也就是说操作数的减少)就会产生额外的答案为。(有没有它们,能够产生膜法串的方法数都是)。
时间复杂度就变为。
我傻了,求出后就是答案。
代码里的ans就是划线处的体现。
神奇的代码
#include <bits/stdc++.h> using namespace std; typedef long long LL; const LL mo=998244353; char s[3006],t[3006]; int ls,lt; LL dp[3006][3006]; LL ans; LL DP(int pos,int l,int r,int id,int cntt){ if (l==r) { ans=(ans+min(id,ls-lt))%mo;; return 1; } if (dp[l][r]!=-1) { ans=(ans+dp[l][r]*min(id,ls-lt))%mo; return dp[l][r]; } LL qwq=0; if (l>lt-1||s[pos]==t[l]) qwq=(qwq+DP(pos-1,l+1,r,id==12345?cntt:id,cntt+1))%mo; if (r>lt||s[pos]==t[r-1]) qwq=(qwq+DP(pos-1,l,r-1,id,cntt+1))%mo; return dp[l][r]=qwq; } int main(void) { scanf("%s%s",s,t); memset(dp,-1,sizeof(dp)); ls=strlen(s); lt=strlen(t); ans=(ans+DP(ls-1,0,ls,12345,0))%mo; LL qwq=0; for(int i=lt;i<=ls;++i) qwq=(qwq+dp[0][i])%mo; printf("%lld\n",qwq); return 0; }
本文作者:~Lanly~
本文链接:https://www.cnblogs.com/Lanly/p/12714023.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步