CF 1260F colored tree

点分治+线段树(过不去)。

把点分治换成DSU ON THE TREE 应该就能过了。

设S为∏(R[i]-L[i]+1),W[i]为(R[i]-L[i]+1)。

假设有一个点u,则它对答案的贡献为∑(disu + disv) * (S / (W[u] * W[v])),条件为u和v的区间有交。

把式子拆开有两个项,分别用线段树维护即可。

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

char gc()
{
    static char buf[100000], *p1 = buf, *p2 = buf;
    return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1++;
}

int getnum()
{
    int x = 0, f = 1;
    char ch = gc();
    while (ch < ‘0‘ || ch > ‘9‘) {
	if (ch == ‘-‘) f = -1; ch = gc();
    }
    while (ch >= ‘0‘ && ch <= ‘9‘) {
	x = x * 10 + ch - ‘0‘; ch = gc();
    }
    return x * f;
}

const int N = 100010, up = 100000, mo = 1e9 + 7;
int n, P;
int cnt;
int head[N];
struct edge
{
    int v, nxt;
} vec[N << 1];
int L[N];
int R[N];
int G[N];

int ksm(int a, int b)
{
    int s = 1;
    while (b) {
	if (b & 1) s = 1LL * s * a % mo;
	a = 1LL * a * a % mo; b >>= 1;
    }
    return s;
}

int add(int a, int b)
{
    a += b;
    if (a >= mo) a -= mo;
    if (a < 0) a += mo;
    return a;
}

pair<int, int> operator + (pair<int, int> a, pair<int, int> b)
{
    return make_pair(add(a.first, b.first), add(a.second, b.second));
}

struct SEG
{
#define lson (o << 1)
#define rson (o << 1 | 1)

    int sum[N << 2];
    int flag[N << 2];
    int ss[N << 2];
    int hh[N << 2];

    void pushup(int o)
    {
	sum[o] = add(sum[lson], sum[rson]);
	ss[o] = add(ss[lson], ss[rson]);
    }

    void pushdown(int o, int l, int r)
    {
	if (flag[o]) {
	    flag[lson] = add(flag[lson], flag[o]);
	    flag[rson] = add(flag[rson], flag[o]);
	    int mid = (l + r) >> 1;
	    sum[lson] = add(sum[lson], 1LL * flag[o] * (mid - l + 1) % mo);
	    sum[rson] = add(sum[rson], 1LL * flag[o] * (r - mid) % mo);
	    flag[o] = 0;
	}
	if (hh[o]) {
	    hh[lson] = add(hh[lson], hh[o]);
	    hh[rson] = add(hh[rson], hh[o]);
	    int mid = (l + r) >> 1;
	    ss[lson] = add(ss[lson], 1LL * hh[o] * (mid - l + 1) % mo);
	    ss[rson] = add(ss[rson], 1LL * hh[o] * (r - mid) % mo);
	    hh[o] = 0;
	}
    }

    void modify(int o, int l, int r, int L, int R, int p, int v)
    {
	if (L <= l && r <= R) {
	    flag[o] = add(flag[o], p); sum[o] = add(sum[o], 1LL * p * (r - l + 1) % mo);
	    hh[o] = add(hh[o], v); ss[o] = add(ss[o], 1LL * v * (r - l + 1) % mo);
	    return;
	}
	pushdown(o, l, r);
	int mid = (l + r) >> 1;
	if (L <= mid) modify(lson, l, mid, L, R, p, v);
	if (R > mid) modify(rson, mid + 1, r, L, R, p, v);
	pushup(o);
    }

    pair<int, int> query(int o, int l, int r, int L, int R)
    {
	if (L <= l && r <= R) return make_pair(sum[o], ss[o]);
	pushdown(o, l, r);
	int mid = (l + r) >> 1;
	pair<int, int> ret = make_pair(0, 0);
	if (L <= mid) ret = ret + query(lson, l, mid, L, R);
	if (R > mid) ret = ret + query(rson, mid + 1, r, L, R);
	pushup(o);
	return ret;
    }

    void output()
    {
	for (int i = 1; i <= 20; ++i) printf("%d ", hh[i]); printf("\n");
	for (int i = 1; i <= 20; ++i) printf("%d ", ss[i]); printf("\n\n");
    }
} SEG;

int S, rt, minn;
int ans;
int siz[N];
bool vis[N];

void addedge(int u, int v)
{
    vec[++cnt] = (edge){v, head[u]}; head[u] = cnt;
}

void get_read()
{
    n = getnum();
    for (int i = 1; i <= n; ++i) L[i] = getnum(), R[i] = getnum();
    for (int i = 1; i <= n - 1; ++i) {
	int u = getnum(), v = getnum();
	addedge(u, v); addedge(v, u);
    }
}

void getroot(int u, int fa)
{
    int ret = 0; siz[u] = 1;
    for (int i = head[u]; i; i = vec[i].nxt) {
	int v = vec[i].v;
	if (v == fa || vis[v]) continue;
	getroot(v, u);
	siz[u] += siz[v];
	ret = max(ret, siz[v]);
    }
    ret = max(ret, S - siz[u]);
    if (ret < minn) minn = ret, rt = u;
}

void pd(int u, int fa, int w)
{
    pair<int, int> X = SEG.query(1, 1, up, L[u], R[u]);
    ans = add(ans, 1LL * G[u] % mo * P % mo * add(1LL * X.second * w % mo, X.first) % mo);
    for (int i = head[u]; i; i = vec[i].nxt) {
	int v = vec[i].v;
	if (v == fa || vis[v]) continue;
	pd(v, u, w + 1);
    }
}

void solve(int u, int fa, int w, int c)
{
    SEG.modify(1, 1, up, L[u], R[u], 1LL * c * w % mo * G[u] % mo, 1LL * c % mo * G[u] % mo);
    for (int i = head[u]; i; i = vec[i].nxt) {
	int v = vec[i].v;
	if (v == fa || vis[v]) continue;
	solve(v, u, w + 1, c);
    }
}

void calc(int u)
{
    for (int i = head[u]; i; i = vec[i].nxt) {
	int v = vec[i].v;
	if (vis[v]) continue;
	pd(v, u, 1); solve(v, u, 1, 1);
    }
    ans = add(ans, 1LL * SEG.query(1, 1, up, L[u], R[u]).first * P % mo * G[u] % mo);
    for (int i = head[u]; i; i = vec[i].nxt) {
	int v = vec[i].v;
	if (vis[v]) continue;
	solve(v, u, 1, -1);
    }
}

void divide(int u)
{
    vis[u] = true; calc(u);
    for (int i = head[u]; i; i = vec[i].nxt) {
	int v = vec[i].v;
	if (vis[v]) continue;
	S = siz[v]; rt = v; minn = siz[v];
	getroot(v, u); divide(rt);
    }
}

void get_work()
{
    P = 1;
    for (int i = 1; i <= n; ++i) {
	P = 1LL * P * (R[i] - L[i] + 1) % mo;
	G[i] = ksm(R[i] - L[i] + 1, mo - 2);
    }
    S = n; rt = 1; minn = n;
    getroot(1, 0);
    divide(rt);
    printf("%d", (ans % mo + mo) % mo);
}

signed main()
{

    get_read();

    get_work();

    return 0;
}

原文地址:https://www.cnblogs.com/calvin99/p/12037682.html

时间: 2024-10-13 16:33:06

CF 1260F colored tree的相关文章

CF 570 D. Tree Requests

D. Tree Requests http://codeforces.com/problemset/problem/570/D 题意: 一个以1为根的树,每个点上有一个字母(a-z),每次询问一个子树内深度为h的点是否可以构成回文串.(深度是到1的深度,没有也算,空回文串) 分析: dsu on tree.询问子树信息. 判断是否构成回文:出现奇数次的字符小于等于1个. 代码: 1 #include<cstdio> 2 #include<algorithm> 3 #include&

CF 277E Binary Tree on Plane (拆点 + 费用流) (KM也可做)

题目大意: 平面上有n个点,两两不同.现在给出二叉树的定义,要求树边一定是从上指向下,即从y坐标大的点指向小的点,并且每个结点至多有两个儿子.现在让你求给出的这些点是否能构成一棵二叉树,如果能,使二叉树的树边长度(欧几里德长度)总和最小,输出这个总和.如果不能,输出-1.答案与标准答案相差1e-6内都认为是正确的. 算法讨论: 起初是这样想的,肯定是MCMF,费用是距离,然后流量一开始我是这样搞的:从父亲向儿子连流量为2的边.但是你会发现这样有一个问题,就是如果某个结点如果真的有两个儿子的话,那

CF 383C Propagating tree [想法+树状数组]

题意: 给一棵树 给出两种操作: 1.在某个结点上加上一个值,在这个结点所有的儿子结点上减去这个值,在这个结点的所有孙子结点上加上这个值,在所有曾孙子结点上减去这个值,直到底. 2.查询某个结点上的值 分析: 把这个问题转化为树状数组的区间求和 样例经过dfs处理后如下,每个结点处理出了两个值l,r,层数1,2,3...,层数为奇数的属性为0,层数为偶数的属性为1 可以看到,子树下的结点的两个数值都是包含在子数的根结点的两数范围内的 而子树根结点的兄弟结点的两数范围则不是包含在子树根结点的两数范

数据结构与算法(八)-二叉树(斜二叉树、满二叉树、完全二叉树、线索二叉树)

前言:前面了解了树的概念和基本的存储结构类型及树的分类,而在树中应用最广泛的种类是二叉树 一.简介 在树型结构中,如果每个父节点只有两个子节点,那么这样的树被称为二叉树(Binary tree).其中,一个父结点的两个字节点分别叫做“左子节点”和“右子节点”.不过也不是所有父节点都有两个子节点,只有左子节点或者只有右子节点的情况也存在.另外,也会存在叶子结点,也就是一个子节点都没有的节点,唯一的限制就是每一个节点的子节点不能超过两个. 之前谈过的单向链表,是一种通过“指向下一个元素的指针”来连接

线索二叉树C+Java

1.线索二叉树的原理: 为什么会有线索二叉树呢?我们观察一个普通的二叉树: 这正是我们平常见到的二叉树,可以发现A,B,C,D这四个节点它们的左孩子和有孩子都有节点,而E,F,G,H,I,J它们都有空闲的指针,这些空闲的指针不存储任何事物 白白浪费了内存的资源. 当我们中序遍历二叉树:HDIBJEAFCG .遍历完之后可以指到每个节点的前驱和后继,如D的前驱是H,后继是I. 可是这是建立在已经遍历完之后的基础上,这里需要注意的是每一种遍历的结果前驱和后继都会不一样的. 在二叉链表创建完后,我们只

CF 461B Appleman and Tree 树形DP

Appleman has a tree with n vertices. Some of the vertices (at least one) are colored black and other vertices are colored white. Consider a set consisting of k (0 ≤ k < n) edges of Appleman's tree. If Appleman deletes these edges from the tree, then

CF 500D New Year Santa Network tree 期望 好题

New Year is coming in Tree World! In this world, as the name implies, there are n cities connected by n - 1 roads, and for any two distinct cities there always exists a path between them. The cities are numbered by integers from 1 to n, and the roads

CF R309C 554C Kyoya and Colored Balls

554C Kyoya and Colored Balls 即求 复合条件的序列数量 序列最后一位必定是K. 那么对于c[k]-1个剩下的K,有n-1个位置去放置. 同理,对于颜色为k-1的球 在剩下的n-c[k]个位置中,必有一个在最靠右的空位置上. 剩下的c[k-1]-1个球放在剩下的位置上 类推 core code: while(k--){ res *= C[n-1][c[k]-1]%MOD; n -= c[k]; }

CF 161D Distance in Tree 树形DP

一棵树,边长都是1,问这棵树有多少点对的距离刚好为k 令tree(i)表示以i为根的子树 dp[i][j][1]:在tree(i)中,经过节点i,长度为j,其中一个端点为i的路径的个数dp[i][j][0]:在tree(i)中,经过节点i,长度为j,端点不在i的路径的个数 则目标:∑(dp[i][k][0]+dp[i][k][1])初始化:dp[i][0][1]=1,其余为0 siz[i]:tree(i)中,i与离i最远的点的距离递推:dp[i][j][0]+=dp[i][j-l][1]*dp[