【NOIP2017提高A组模拟8.25】影子

Description:

这里写图片描述
1<=n<=10^5

题解:

首先对于这种树上路径问题是很容易想到点分治的,细节比较复杂,也许需要一个set,常数爆炸。
比较联赛的做法是并查集。
先将点按权值从大到小排序。
依次枚举每个点,将它和与它相邻且权值大于它的点合并成一棵树。
每次合并,新树的直径的端点一定是原来两棵树中的两条直径的四个端点中的两个,这个易证。
直径上的最小值不一定是当前的点,但是这没有关系,因为它一定在前面就被计算过了。所以ans=max(ans,直径长度×当前点权)

Code:

#include<cstdio>
#include<algorithm>
#define ll long long
#define fo(i, x, y) for(int i = x; i <= y; i ++)
using namespace std;

const int mo = 1 << 30;

const int N = 4000;
int T, n, m, p[N], mu[N + 5];
bool bz[N + 5];
ll ans;

void Build() {
    mu[1] = 1;
    fo(i, 2, N) {
        if(!bz[i]) p[++ p[0]] = i, mu[i] = -1;
        fo(j, 1, p[0]) {
            int k = i * p[j];
            if(k > N) break;
            bz[k] = 1;
            if(i % p[j] == 0) {
                mu[k] = 0; break;
            }
            mu[k] = -mu[i];
        }
    }
}

int main() {
    Build();
    for(scanf("%d", &T); T; T --) {
        scanf("%d %d", &n, &m);
        if(n > m) swap(n, m);
        n --; m --;
        ans = 0;
        fo(i, 1, n) if(mu[i] != 0)  {
            int sf, sg;

            int c = n / i, c2 = (n + 1) / 2 / i, s;
            sf = (1 + c2) * c2 / 2 * i;
            sf += (n + 1) * (c - c2) - (c2 + 1 + c) * (c - c2) / 2 * i;

            c = m / i, c2 = (m + 1) / 2 / i, s;
            sg = (1 + c2) * c2 / 2 * i;
            sg += (m + 1) * (c - c2) - (c2 + 1 + c) * (c - c2) / 2 * i;

            int s1 = (n + 1) * (n / i) - (n / i) * (n / i + 1) * i / 2;
            int s2 = (m + 1) * (m / i) - (m / i) * (m / i + 1) * i / 2;
            ans += mu[i] * (sf * s2 + sg * s1 - sf * sg);
        }
        ans *= 2; ans += n + m + 2;
        ans = (ans % mo + mo) % mo;
        printf("%lld\n", ans);
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值