算法新模板

FFT

/*
FFT模板 以此题为例
这道题就是给两个序列a,b
求b序列中有多少个数可以表示为a序列中的一个数,或者两个数之和(可以同一个数)
可以构造两个一样的多项式,a序列有相应的数的项的系数为1,否则为0
因为还有1个数的情况,所以看作a中有一个0。
这样用多项式乘法,就可以得出所有的和。
*/
 
#include<bits/stdc++.h>
#define rep(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
 
const int N = 8e5 + 10;  //答案多项式次数的两倍 也是limit的理论最大值
const double pi = acos(-1.0);
 
struct Complex
{
    double x, y;
}a[N], b[N], c[N];
int ans[N], r[N], n, m, l, limit;
 
Complex operator + (Complex a, Complex b) { return Complex{a.x + b.x, a.y + b.y}; }
Complex operator - (Complex a, Complex b) { return Complex{a.x - b.x, a.y - b.y}; }
Complex operator * (Complex a, Complex b) { return Complex{a.x * b.x - a.y * b.y, a.x * b.y + a.y * b.x}; }
 
void FFT(Complex *A, int type)
{
    rep(i, 0, limit)
        if(i < r[i])
            swap(A[i], A[r[i]]);
 
    for(int mid = 1; mid < limit; mid <<= 1)
    {
        Complex Wn{cos(pi / mid), type * sin(pi / mid)};
        for(int R = mid << 1, j = 0; j < limit; j += R)
        {
            Complex w{1, 0};
            for(int k = 0; k < mid; k++, w = w * Wn)
            {
                Complex x = A[j + k], y = w * A[j + mid + k];
                A[j + k] = x + y;
                A[j + mid + k] = x - y;
            }
        }
    }
}
 
void solve()
{
    for(limit = 1, l = 0; limit <= n + m; limit <<= 1, l++);      //FFT只能处理2^t的多项式 limit表示最高次数 n + m即答案多项式次数
    rep(i, 0, limit) r[i] = (r[i >> 1] >> 1) | ((i & 1) << (l - 1));
    FFT(a, 1); FFT(b, 1);                                         //FFT 系数表示法转化为点值表示法
    _for(i, 0, limit) c[i] = a[i] * b[i];                         //点值表示法直接相乘
    FFT(c, -1);                                                   //逆FFT 点值表示法转化为系数表示法 最后要多除个limit(下一行)
    _for(i, 0, n + m) ans[i] = (int)(c[i].x / limit + 0.5);
}
 
int main()
{
    while(~scanf("%d", &n))
    {
        memset(a, 0, sizeof a);                                   //多组数据只用清空a b两个多项式的数组
        memset(b, 0, sizeof b);
 
        a[0].x = b[0].x = 1;
        _for(i, 1, n)
        {
            int x; scanf("%d", &x);
            a[x].x = b[x].x = 1;                                   //a[i].x表示x^i项的系数
        }
        m = n = 2e5;                                              //n表示a多项式的次数 m是b多项式的次数
 
        solve();
 
        int cnt = 0;
        scanf("%d", &m);
        while(m--)
        {
            int x; scanf("%d", &x);
            cnt += (ans[x] > 0);
        }
        printf("%d\n", cnt);
    }
 
    return 0;
}

splay

#include <bits/stdc++.h>
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
#define rep(i, a, b) for(int i = (a); i < (b); i++)
using namespace std;
 
const int N = 1e5 + 10;
int rt, tot, fa[N], ch[N][2], val[N], cnt[N], sz[N];
 
struct Splay
{
    void maintain(int x) { sz[x] = sz[ch[x][0]] + sz[ch[x][1]] + cnt[x]; } //重新计算x的sz值
 
    bool get(int x) { return x == ch[fa[x]][1]; }   //判断x是不是右儿子
 
    void clear(int x)                              //销毁x节点
    {
        ch[x][0] = ch[x][1] = fa[x] = val[x] = sz[x] = cnt[x] = 0;
    }
 
    void rotate(int x)                             //旋转操作 包含左旋和右旋
    {
        int y = fa[x], z = fa[y], chk = get(x);
        ch[y][chk] = ch[x][chk ^ 1];
        if(ch[x][chk ^ 1]) fa[ch[x][chk ^ 1]] = y;
        ch[x][chk ^ 1] = y;
        fa[y] = x; fa[x] = z;
        if(z) ch[z][y == ch[z][1]] = x;
        maintain(y);
        maintain(x);
    }
 
    void splay(int x) //将x旋转到根  每次访问x后都要splay
    {
        for(int f = fa[x]; f = fa[x]; rotate(x))
            if(fa[f]) rotate(get(x) == get(f) ? f : x);
        rt = x;
    }
 
    void ins(int k)   //插入操作
    {
        if(!rt)
        {
            val[++tot] = k;
            cnt[tot]++;
            rt = tot;
            maintain(rt);
            return;
        }
 
        int cur = rt, f = 0;
        while(1)
        {
            if(val[cur] == k)
            {
                cnt[cur]++;
                maintain(cur);
                maintain(f);
                splay(cur);
                break;
            }
 
            f = cur;
            cur = ch[cur][val[cur] < k];
            if(!cur)
            {
                val[++tot] = k;
                cnt[tot]++;
                fa[tot] = f;
                ch[f][val[f] < k] = tot;
                maintain(tot);
                maintain(f);
                splay(tot);
                break;
            }
        }
    }
 
    int rk(int k) //获取k的排名
    {
        int res = 0, cur = rt;
        while(1)
        {
 
            if(k < val[cur]) cur = ch[cur][0];
            else
            {
                res += sz[ch[cur][0]];
                if(k == val[cur])
                {
                    splay(cur);
                    return res + 1;
                }
                res += cnt[cur];
                cur = ch[cur][1];
            }
        }
    }
 
    int kth(int k)  //排名第k的是哪一个数
    {
        int cur = rt;
        while(1)
        {
            if(ch[cur][0] && k <= sz[ch[cur][0]]) cur = ch[cur][0];
            else
            {
                k -= cnt[cur] + sz[ch[cur][0]];
                if(k <= 0)
                {
                    splay(cur);
                    return val[cur];
                }
                cur = ch[cur][1];
            }
        }
    }
 
    int pre() //返回k的前驱 注意函数外要先插入再删除
    {
        int cur = ch[rt][0];
        if (!cur) return cur;
        while(ch[cur][1]) cur = ch[cur][1];
        splay(cur);
        return cur;
    }
 
    int nxt() //返回k的后继 注意函数外要先插入再删除
    {
        int cur = ch[rt][1];
        if (!cur) return cur;
        while(ch[cur][0]) cur = ch[cur][0];
        splay(cur);
        return cur;
    }
 
    void del(int k) //删除k 只删一个
    {
        rk(k);
        if(cnt[rt] > 1)
        {
            cnt[rt]--;
            maintain(rt);
            return;
        }
        if(!ch[rt][0] && !ch[rt][1])
        {
            clear(rt);
            rt = 0;
            return;
        }
        if(!ch[rt][0])
        {
            int cur = rt;
            rt = ch[rt][1];
            fa[rt] = 0;
            clear(cur);
            return;
        }
        if(!ch[rt][1])
        {
            int cur = rt;
            rt = ch[rt][0];
            fa[rt] = 0;
            clear(cur);
            return;
        }
        int cur = rt;
        int x = pre();
        fa[ch[cur][1]] = x;
        ch[x][1] = ch[cur][1];
        clear(cur);
        maintain(rt);
    }
}tree;
 
 
int main()
{
    int n;
    scanf("%d", &n);
    _for(i, 1, n)
    {
        int op, x;
        scanf("%d%d", &op, &x);
        if (op == 1) tree.ins(x);
        if (op == 2) tree.del(x);
        if (op == 3) printf("%d\n", tree.rk(x));
        if (op == 4) printf("%d\n", tree.kth(x));
        if (op == 5) tree.ins(x), printf("%d\n", val[tree.pre()]), tree.del(x);
        if (op == 6) tree.ins(x), printf("%d\n", val[tree.nxt()]), tree.del(x);
    }
    return 0;
}

文艺平衡树(rope)

rope这个STL可以用来解决一些平衡树的问题

它可以时间区间插入,删除,替换,都是logn

这道题而言就建立两个rope,一个正的一个反的,替换的时候就替换对应区间就可以了

用取子串相加这个操作

#include <bits/stdc++.h>
#include <ext/rope>
#define rep(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
using namespace __gnu_cxx;
 
rope<int> a, b;
int n, m;
 
void solve(int l, int r)
{
    int ll = n - 1 - r, rr = n - 1 - l;
    rope<int> t = a;
    a = a.substr(a.begin(), a.begin() + l) + b.substr(b.begin() + ll, b.begin() + rr + 1) + a.substr(a.begin() + r + 1, a.end());
    b = b.substr(b.begin(), b.begin() + ll) + t.substr(t.begin() + l, t.begin() + r + 1) + b.substr(b.begin() + rr + 1, b.end());
}
 
int main()
{
 
    scanf("%d%d", &n, &m);
    _for(i, 1, n)
    {
        a.push_back(i);
        b.push_back(n - i + 1);
    }
 
    while(m--)
    {
        int l, r;
        scanf("%d%d", &l, &r);
        solve(l - 1, r - 1);
    }
    for(int x: a) printf("%d ", x);
 
    return 0;
}

点分治

#include <bits/stdc++.h>
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
#define rep(i, a, b) for(int i = (a); i < (b); i++)
using namespace std;
 
const int N = 1e4 + 10;
const int M = 1e7 + 10;
vector<pair<int, int>> g[N];
int q[N], ans[N], maxp[N], vis[N], siz[N], rt, sum, n, m;
vector<int> ve;
bool judge[M];
 
void getrt(int u, int fa)
{
    siz[u] = 1; maxp[u] = 0;               //注意这里初始化maxp
    for(auto x: g[u])
    {
        int v = x.first, w = x.second;
        if(v == fa || vis[v]) continue;
        getrt(v, u);
        siz[u] += siz[v];
        maxp[u] = max(maxp[u], siz[v]);
    }
    maxp[u] = max(maxp[u], sum - siz[u]);
    if(maxp[u] < maxp[rt]) rt = u;
}
 
void getdis(int u, int fa, int d)
{
    if(d <= 1e7) ve.push_back(d);       //询问到1e7 总和会到1e8
    for(auto x: g[u])
    {
        int v = x.first, w = x.second;
        if(vis[v] || v == fa) continue;
        getdis(v, u, d + w);
    }
}
 
void solve(int u)
{
    vector<int> tmp;               //防止memset
    for(auto x: g[u])
    {
        int v = x.first, w = x.second;
        if(vis[v]) continue;
        ve.clear();
        getdis(v, u, w);
 
        for(int x: ve)
            _for(i, 1, m)
                if(q[i] >= x)
                    ans[i] |= judge[q[i] - x];
        for(int x: ve)
        {
            judge[x] = true;
            tmp.push_back(x);
        }
    }
    for(int x: tmp) judge[x] = false;
}
 
void divide(int u)
{
    vis[u] = 1;        //删除u
    solve(u);          //处理经过u的路径
    for(auto x: g[u])
    {
        int v = x.first;
        if(vis[v]) continue;
        maxp[rt = 0] = sum = siz[v];   //这里初始化sum是siz[v]
        getrt(v, 0);
        getrt(rt, 0);
        divide(rt);
    }
}
 
int main()
{
    scanf("%d%d", &n, &m);
    _for(i, 1, n - 1)
    {
        int u, v, w;
        scanf("%d%d%d", &u, &v, &w);
        g[u].push_back({v, w});
        g[v].push_back({u, w});
    }
    _for(i, 1, m) scanf("%d", &q[i]);
 
    judge[0] = true;       //注意要初始化0
    maxp[rt = 0] = sum = n; //初始化rt
    getrt(1, 0);            //找重心
    getrt(rt, 0);           //更新正确的siz数组
    divide(rt);
 
    _for(i, 1, m) puts(ans[i] ? "AYE" : "NAY");
 
    return 0;
}

SAM

#include <bits/stdc++.h>
#define rep(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
 
typedef long long ll;
const int N = 2e6 + 10; 						//字符串总长两倍
int t[N][26], len[N], fa[N], c[N], k[N];  		//t为自动机上的边,即fail,fa为parent tree上的边
int siz[N], cnt = 1, last = 1, n;               //len表示节点i的类中最长串的长度
vector<int> endpos[N];
char s[N];
ll ans;
 
void add(int c, int pos) //题目不同更新u,r两处 不只有小写字母也要改
{
	int p = last;
	int u = last = ++cnt;    //u是新建的节点
	len[u] = len[p] + 1;
	for(; p && !t[p][c]; p = fa[p]) t[p][c] = u;
	if(!p) fa[u] = 1;
	else
	{
		int q = t[p][c];
		if(len[q] == len[p] + 1) fa[u] = q;
		else
		{
			int r = ++cnt;    //r是多建的虚空点
			_for(i, 0, 25) t[r][i] = t[q][i];          //有些题不只是小写字母 注意
			fa[r] = fa[q];
			len[r] = len[p] + 1;
			fa[q] = fa[u] = r;
			for(; p && t[p][c] == q; p = fa[p])
				t[p][c] = r;
			//更新r
 
 
		}
	}
 
	//更新u
	siz[u] = 1;   					//注意siz的初始化只有siz[u] = 1  不要写siz[q] = 1
	endpos[u].push_back(pos);       //siz表示有多少个终止位置,也是子串出现次数
}
 
int main()
{
	scanf("%s", s + 1);
	n = strlen(s + 1);
	_for(i, 1, n) add(s[i] - 'a', i);
 
	_for(i, 1, cnt) c[len[i]]++;               //根据len桶排序 也可以dfs求siz数组
	_for(i, 1, cnt) c[i] += c[i - 1];
	_for(i, 1, cnt) k[c[len[i]]--] = i;
	for(int i = cnt; i >= 1; i--)
	{
		int id = k[i];
		siz[fa[id]] += siz[id];
		for(int x: endpos[id]) endpos[fa[id]].push_back(x);
	}
 
	_for(i, 1, n)
		if(siz[i] > 1)
			ans = max(ans, 1LL * siz[i] * len[i]);
	printf("%lld\n", ans);
 
	return 0;
}

单调栈

#include <bits/stdc++.h>
#define rep(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
 
const int N = 3e6 + 10;
int a[N], ans[N], s[N], top, n;
 
int main()
{
	scanf("%d", &n);
	_for(i, 1, n) scanf("%d", &a[i]);
	for(int i = n; i >= 1; i--)
	{
		while(top && a[s[top]] <= a[i]) top--; //注意这里是while 不是 if  维持单调性
		ans[i] = top ? s[top] : 0;             //通过当前栈顶记录答案
		s[++top] = i;                          //将当前元素压入栈
	}
	_for(i, 1, n) printf("%d ", ans[i]);
 
	return 0;
}

珂朵莉树模板

应用条件

1.有区间赋值操作

2.数据随机 否则会被构造数据卡成暴力

本质上就是应用set,维护三元组(l, r, v)

因为有区间赋值操作导致区间个数小,保证复杂度

struct node
{
	int l, r;
	mutable int v;          //注意关键字mutable
	bool operator < (const node& rhs) const
	{
		return l < rhs.l;
	}
};
set<node> s;
int n;
 
auto split(int x)
{
    auto it = s.lower_bound({x, 0, 0});
    if (it != s.end() && it->l == x) return it;
    --it;
    int l = it->l, r = it->r, v = it->v;
    s.erase(it);
    s.insert({l, x - 1, v});
    return s.insert({x, r, v}).first;
}
 
void assign(int l, int r, int v)
{
	auto itr = split(r + 1), itl = split(l);   //注意先r后l
	s.erase(itl, itr);
	s.insert({l, r, v});
}
 
void add(int l, int r, int x)
{
	auto itr = split(r + 1), itl = split(l);
	for(auto it = itl; it != itr; it++) 
	{
		//做某种操作
		it->v += x;
	}
}

ST表

#include <bits/stdc++.h>
#define rep(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
 
const int N = 3e5 + 10;
int stgcd[N][21], stmin[N][21], Log[N], n; 
 
int gcd(int a, int b) { return !b ? a : gcd(b, a % b); }
 
int ask_gcd(int l, int r)
{
    int k = Log[r - l + 1];            //长度位2 ^ k
    return gcd(stgcd[l][k], stgcd[r - (1 << k) + 1][k]);
}
 
int ask_min(int l, int r)
{
    int k = Log[r - l + 1];
    return min(stmin[l][k], stmin[r - (1 << k) + 1][k]);
}
 
bool check(int key)
{
    _for(i, 1, n)
    {
        int j = i + key;
        if(j > n) break;
        int g = ask_gcd(i, j), mi = ask_min(i, j);
        if(g == mi) return true;
    }
    return false;
}
 
void init()
{
    Log[1] = 0;
    _for(i, 2, n) Log[i] = Log[i / 2] + 1;
    _for(j, 1, 20)                                    //注意先枚举j后枚举i 因为每次更新要用到j - 1
        for(int i = 1; i + (1 << j) - 1 <= n; i++)    //注意f[i][j]不要超范围
        {
            stgcd[i][j] = gcd(stgcd[i][j - 1], stgcd[i + (1 << (j - 1))][j - 1]);
            stmin[i][j] = min(stmin[i][j - 1], stmin[i + (1 << (j - 1))][j - 1]);
        }
}
 
int main()
{   
    scanf("%d", &n);
    _for(i, 1, n) 
    {
        scanf("%d", &stgcd[i][0]);
        stmin[i][0] = stgcd[i][0];
    }
 
    init();
 
    int l = 0, r = n + 1;
    while(l + 1 < r)
    {
        int m = l + r >> 1;
        if(check(m)) l = m;
        else r = m;
    }
 
    vector<int> ans;
    _for(i, 1, n)
    {
        int j = i + l;
        if(j > n) break;
        int g = ask_gcd(i, j), mi = ask_min(i, j);
        if(g == mi) ans.push_back(i);
    }
    printf("%d %d\n", ans.size(), l);
    for(int x: ans) printf("%d ", x);
   
	return 0;
}

poj3261(后缀数组 + 二分)

同样是二分一个答案,然后根据height来分组

一个组内,如果长度有t次,说明有一个串重复了t次,且长度大于等于ans

这个串就是每一个后缀的前ans个字符   可以画图理解一下

所以只要判断组内的后缀个数大于等于k即可

#include <cstdio>
#include <algorithm>
#define rep(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
 
const int N = 2e4 + 10;
const int M = 1e6 + 10;
int s[N], sa[N], rak[N], tp[N], tax[M], height[N], n, m, K; //注意M
 
void Qsort()
{
	_for(i, 0, m) tax[i] = 0;
	_for(i, 1, n) tax[rak[i]]++;
	_for(i, 1, m) tax[i] += tax[i - 1];
	for(int i = n; i >= 1; i--) sa[tax[rak[tp[i]]]--] = tp[i];
}
 
void SuffixSort()
{
	m = 1e6;            //s[i]的元素范围在0~m内
	_for(i, 1, n) rak[i] = s[i], tp[i] = i;
	Qsort();
	for(int w = 1, p = 0; p < n; m = p, w <<= 1)
	{
		p = 0;
		_for(i, 1, w) tp[++p] = n - i + 1;
		_for(i, 1, n) if(sa[i] > w) tp[++p] = sa[i] - w;
		Qsort(); swap(tp, rak);
		rak[sa[1]] = p = 1;
		_for(i, 2, n)
			rak[sa[i]] = (tp[sa[i]] == tp[sa[i - 1]] && tp[sa[i] + w] == tp[sa[i - 1] + w]) ? p : ++p;
	}
 
	int k = 0;
	_for(i, 1, n)
	{
		if(k) k--;
		int j = sa[rak[i] - 1];
		while(i + k <= n && j + k <= n && s[i + k] == s[j + k]) k++;
		height[rak[i]] = k;
	}
}
 
bool check(int key)
{
	int cnt = 1;
	_for(i, 2, n)
	{
		if(height[i] >= key) cnt++;
        else cnt = 1;
		if(cnt >= K) return true;
	}
	return false;
}
 
int main()
{
    scanf("%d%d", &n, &K);
	_for(i, 1, n) scanf("%d", &s[i]);
	SuffixSort();
 
	int l = 1, r = n;
	while(l + 1 < r)
	{
		int m = l + r >> 1;
		if(check(m)) l = m;
		else r = m;
	}
	printf("%d\n", l);
 
	return 0;
}

回文自动机(PAM)

两颗trie树,一颗代表偶数长度回文串,一颗代表奇数长度回文串

一个节点代表一个回文串

fail指针代表当前回文串的最长回文后缀

代码量挺小,可以用来求本质不同的回文串数目,即新建的节点数目cnt-1

可以用来求每个节点出现的次数,每次先sum[last]++

然后再倒序sum[fail[j]] += sum[j] 这样就可以求出

这道题是求每个节点为结尾的回文串数目

每次新建节点的时候,num[cnt] = num[fail[cnt]] + 1
 

#include <bits/stdc++.h>
#define rep(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
 
const int N = 5e5 + 10;
int ch[N][26], len[N], fail[N], sum[N], n;
char s[N];
 
int main()
{
	scanf("%s", s + 1);
	n = strlen(s + 1);
 
	s[0] = '#';
	fail[0] = 1; len[1] = -1;
	int last = 0, cnt = 1, pre;
	_for(i, 1, n)
	{
		if(i > 1) s[i] = (s[i] - 97 + pre) % 26 + 97;
		while(s[i] != s[i - len[last] - 1]) last = fail[last];
		if(!ch[last][s[i] - 'a'])
		{
			len[++cnt] = len[last] + 2;
			int j = fail[last];
			while(s[i] != s[i - len[j] - 1]) j = fail[j];
			fail[cnt] = ch[j][s[i] - 'a'];
			sum[cnt] = sum[fail[cnt]] + 1;
			ch[last][s[i] - 'a'] = cnt;
		}
		last = ch[last][s[i] - 'a'];
		printf("%d ", pre = sum[last]);
	}
	
	return 0;
}

CF165E Compatible Numbers(高维前缀和)

对于一个询问,显然有1的地方都不能有1

所以也就是当前的a[i]取反,问有没有元素是它的子集

二进制,子集,可以用高维前缀和

用高维前缀和的思路,处理出每一个数的子集中存不存在题目给的数,存在的话用任意一个数就可以。

#include <bits/stdc++.h>
#define rep(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
 
const int N = 1e6 + 10;
int f[1 << 22], a[N], w = 22, n;
 
int main()
{
    scanf("%d", &n);
    _for(i, 1, n)
    {
        scanf("%d", &a[i]);
        f[a[i]] = a[i];
    }
 
    rep(i, 0, w)
        rep(j, 0, 1 << w)
            if(j & (1 << i) && f[j ^ (1 << i)]) f[j] = f[j ^ (1 << i)];
    
    _for(i, 1, n)
    {
        int cur = ((1 << w) - 1) ^ a[i];
        printf("%d ", f[cur] ? f[cur] : -1);
    }
 
	return 0;
}

CF1092F Tree with Maximum Cost(换根dp)

#include <bits/stdc++.h>
#define rep(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
 
typedef long long ll;
const int N = 2e5 + 10;
vector<int> g[N];
ll sum[N], d[N], a[N], ans, cur;
int n;
 
void dfs(int u, int fa)
{
    sum[u] = a[u];
    for(int v: g[u])
    {
        if(v == fa) continue;
        d[v] = d[u] + 1;
        dfs(v, u);
        sum[u] += sum[v];
    }
}
 
void dfs2(int u, int fa, ll val)
{
    ans = max(ans, val);
    for(int v: g[u])
    {
        if(v == fa) continue;
        dfs2(v, u, val - sum[v] + sum[1] - sum[v]);
    }
}
 
int main()
{
    scanf("%d", &n);
    _for(i, 1, n) scanf("%lld", &a[i]);
    _for(i, 1, n - 1)
    {
        int u, v;
        scanf("%d%d", &u, &v);
        g[u].push_back(v);
        g[v].push_back(u);
    }
 
    dfs(1, 0);
    _for(i, 1, n) cur += a[i] * d[i];
    dfs2(1, 0, cur);
    printf("%lld\n", ans);
 
	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值