CH Round #65 - MFOI杯水题欢乐赛 day2 树形DP+SG函数+线段树合并+DP

题目太长不粘贴了,链接在这:
CH Round #65 - MFOI杯水题欢乐赛 day2

题目链接 -> 密码:exdb
题解

T1算叶子之间的距离算成深度相加忘了减去LCA了,我傻X

T2不会,弃题,没学过SG函数

T3dfs10分,dp不会,我傻X

总结:10分滚粗,人弱就不该作死

T1题意:

给你两棵树,从a树中选两个不同叶子,b树中选两个不同的叶子,连接起来组成一个环,问所有不同方案这种环的大小总和是多少,边权为1。若选的叶子不同或者叶子相同但连接方式不同则视为不同方案。n<=100w。

记树的叶子间路径总数为A,叶子间路径总距离为B,叶子总数为C。

容易看出,a树中某两个叶子间的路径长度被计算了 (Cb1)Cb 次,而 A=(C1)C/2 ,然后对于所有路径求和,再加上连的边的长度,答案则是:

AaBb+AbBa+AaAb22

要除以2。

然后我们发现A可以用C在O(1)的时间里求出来,关键在B上。

这时候考虑每条边对B的贡献。很显然若v的子树中有k个叶子,则u-v边的贡献是 k(ck)2 ,这样B就可以树形DP去O(n)求了。

总结:傻X题,我更傻X所以不会做

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;

typedef long long LL;
const int SZ = 3000010;
const int INF = 1000000010;
const double eps = 1e-6;

const int mod = 1000000007;
const int ni = 500000004;

int head[SZ],nxt[SZ],tot = 1,to[SZ];

void build(int f,int t)
{
    to[++ tot] = t;
    nxt[tot] = head[f];
    head[f] = tot;
}

LL sz[SZ],du[SZ],c;

void dfs(int u,int fa,LL a,LL &b)
{
    if(du[u] == 1) sz[u] = 1;
    for(int i = head[u];i;i = nxt[i])
    {
        int v = to[i];
        if(v == fa) continue;
        dfs(v,u,a,b);
        sz[u] += sz[v];
        b = (b + ((sz[v] * (c - sz[v])) % mod * 2) % mod) % mod;
    }
}

LL a1,b1,a2,b2;

void init()
{
    memset(head,0,sizeof(head));
    memset(sz,0,sizeof(sz));
    memset(du,0,sizeof(du));
    tot = 1;
}

void init_tree(LL &a,LL &b,LL &c)
{
    c = 0;
    init();
    int n;
    scanf("%d",&n);
    for(int i = 1;i < n;i ++)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        build(a,b); build(b,a);
        du[a] ++; du[b] ++;
    }
    for(int i = 1;i <= n;i ++)
        if(du[i] == 1)
            c ++;
    a = c * (c - 1) % mod;
    if(n == 2)
    {
        b = 2;
    }
    else
    {
        for(int i = 1;i <= n;i ++)
            if(du[i] != 1)
            {
                dfs(i,0,a,b);
                break;
            }
    }
}
int main()
{
//  freopen("input.txt","r",stdin);
    init_tree(a1,b1,c);
//  printf("%lld %lld %lld\n",a1,b1,c);
    init_tree(a2,b2,c);
//  printf("%lld %lld %lld\n",a2,b2,c);
    LL ans1 = (a1 * b2) % mod;
    LL ans2 = (a2 * b1) % mod;
    LL ans3 = (a1 * a2 * 2) % mod;
    LL ans = (ans1 + ans2 + ans3) % mod;
    printf("%lld",(ans * (LL)ni % mod ) % mod);
    return 0;
}
/*
581299242
521290183
*/



T2题意:

给你一棵有根树,一些节点上有蚂蚁,AB两人轮流把某一只蚂蚁往远离根的方向移动[1,d]的距离,问先手的A是否有必胜策略。多组询问。n,d<=100w

然后我不会做,SG函数能吃吗?还要有线段树合并?那是啥?


T3题意:

m个字符,组成长度为n的串,要求最长连续相同子段的长度不能大于k,问方案数。
m<=109 ,1<=k<=n<=100w

DP,推了半天没推出来…还是爆搜好写…

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值