Codeforces Round #635 (Div. 1) / contest 1336


题目地址:https://codeforces.com/contest/1336



A Linova and Kingdom

题意:一棵树根结点为 1,要选出 k 个结点,使得这 k 个结点到根结点的路径中非选中结点的总数最大。

思路:考虑贪心的思路,如果只选 1 个,那么肯定选择深度最深的最优,如果选很多个,那么某个结点肯定没有它的儿子更优,所以如果我们选了一个结点,那么它的儿子一定全都被选了。所以考虑选择某个结点 u 的时候,它对答案的贡献为 d[u]-(size[u]-1),其中 d 为深度,size 为子树大小。按照贡献排序,从大到小选即可。

代码

#include <bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define REP_(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
typedef long long LL;
int read()
{
    int x=0,flag=1;
    char c=getchar();
    while((c>'9' || c<'0') && c!='-') c=getchar();
    if(c=='-') flag=0,c=getchar();
    while(c<='9' && c>='0') {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
    return flag?x:-x;
}

const int maxn=2e5+5;
vector<int> G[maxn];
int n,k,d[maxn],s[maxn],a[maxn];

void dfs(int u,int fa,int de)
{
    d[u]=de; s[u]=1;
    for(int v:G[u]) if(v!=fa)
    {
        dfs(v,u,de+1);
        s[u]+=s[v];
    }
}

int main()
{
    //freopen("input.txt","r",stdin);
    n=read(); k=read();
    REP(i,1,n-1)
    {
        int u=read(),v=read();
        G[u].push_back(v); G[v].push_back(u);
    }
    dfs(1,0,0);
    REP(i,1,n) a[i]=d[i]-s[i]+1;
    sort(a+1,a+n+1,greater<int>());
    LL ans=0;
    REP(i,1,k) ans+=a[i];
    cout<<ans;

    return 0;
}



B Xenia and Colorful Gems

题意:有三个数组,要求从三个数组中各选一个数 x, y, z 出来,使得 ( x − y ) 2 + ( y − z ) 2 + ( z − x ) 2 (x-y)^2+(y-z)^2+(z-x)^2 (xy)2+(yz)2+(zx)2 的值最小,并求出这个最小值。

思路:一个朴素的想法就是,遍历其中一个数组,对于这个数组中的 x,我们从另外两个数组中分别二分选出两个与 x 最接近的数(一个大于一个小于),然后更新答案。当然不能只遍历一个数组,三个都要。

代码

#include <bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define REP_(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
typedef long long LL;
int read()
{
    int x=0,flag=1;
    char c=getchar();
    while((c>'9' || c<'0') && c!='-') c=getchar();
    if(c=='-') flag=0,c=getchar();
    while(c<='9' && c>='0') {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
    return flag?x:-x;
}

const int maxn=1e5+5;
LL r[maxn],g[maxn],b[maxn];
int nr,ng,nb;

LL cal(int i,int j,int k)
{
    LL x=(r[i]-g[j])*(r[i]-g[j]);
    LL y=(r[i]-b[k])*(r[i]-b[k]);
    LL z=(g[j]-b[k])*(g[j]-b[k]);
    return x+y+z;
}

LL solve()
{
    LL ret=3e18;
    REP(i,1,nr)
    {
        int g1=min(lower_bound(g+1,g+ng+1,r[i])-g,ng);
        int g2=max(g1-1,1);
        int b1=min(lower_bound(b+1,b+nb+1,r[i])-b,nb);
        int b2=max(b1-1,1);
        ret=min(ret,cal(i,g1,b1));
        ret=min(ret,cal(i,g1,b2));
        ret=min(ret,cal(i,g2,b1));
        ret=min(ret,cal(i,g2,b2));
    }
    return ret;
}

int main()
{
    //freopen("input.txt","r",stdin);
    int T=read();
    while(T--)
    {
        nr=read(),ng=read(),nb=read();
        LL ans=3e18;
        REP(i,1,nr) r[i]=read();
        REP(i,1,ng) g[i]=read();
        REP(i,1,nb) b[i]=read();
        sort(r+1,r+nr+1);
        sort(g+1,g+ng+1);
        sort(b+1,b+nb+1);
        REP(i,1,3)
        {
            ans=min(ans,solve());
            swap(r,g); swap(g,b);
            swap(nr,ng); swap(ng,nb);
        }
        printf("%lld\n",ans);
    }

    return 0;
}



C Kaavi and Magic Spell

题意:有两个字符串 S 和 T,我们遍历 S 来构造 A,每遇到一个字符,可以放在 A 的前面,也可以放在 A 的后面。问有多少种构造 A 的方式,使得最后 A 的前缀是 T 。

思路:很明显的dp(然而状态式一直设不对)。设 f ( l , r ) f(l,r) f(l,r) 表示从 S 已经选择了 r-l+1 个字符,并且 A[l…r] 与 T[l…r] 匹配的方案数,这里要注意对于 A 中比 T 长的那些位置,一定是匹配的,然后就是很简单的转移。

代码

#include <bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define REP_(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
typedef long long LL;
int read()
{
    int x=0,flag=1;
    char c=getchar();
    while((c>'9' || c<'0') && c!='-') c=getchar();
    if(c=='-') flag=0,c=getchar();
    while(c<='9' && c>='0') {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
    return flag?x:-x;
}

const int maxn=3005;
const int M=998244353;
int n,m,f[maxn][maxn];
char s[maxn],t[maxn];

int main()
{
    //freopen("input.txt","r",stdin);
    scanf("%s %s",s+1,t+1);
    n=strlen(s+1); m=strlen(t+1);
    REP(i,1,n+1) f[i][i-1]=1;
    for(int i=1;i<=n;i++)
        for(int l=1;l+i-1<=n;l++)
        {
            int r=l+i-1;
            if(l>m || s[i]==t[l]) f[l][r]+=f[l+1][r];
            if(r>m || s[i]==t[r]) f[l][r]+=f[l][r-1];
            f[l][r]%=M;
        }
    int ans=0;
    REP(r,m,n) ans=(ans+f[1][r])%M;
    cout<<ans;

    return 0;
}



D

题意

思路

代码




E

题意

思路

代码




F

题意

思路

代码


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值