J. 挑选队列

题目链接:http://www.acmicpc.sdnu.edu.cn/problem/show/1637
看了题解,出题人把1-n的数转化为图上的点去考虑的。如果两个数互质就连一条边,这样我实际上就在求两两都连边或者两两都不连边的三元组有多少个?
直接求麻烦,考虑容斥,用所有情况的三元组减去不符合条件的三元组。
不符合条件的三元组有哪些呢?
图片说明
图1,三个点中只有一对点之间有边
图2,三个点中只有两对点之间有边
考虑一个点的度:
图片说明
我们发现图一与图二两种情况其实可以放到一起去算:
不符合条件的情况就是d[i]*(n-d[i]-1)/2,d[i]是i相连的点,(n-d[i]-1)是i不连的点,除2是因为重复计算了一次。
下面就是计算d[i]
图片说明
如果按照因数来求的话,时间复杂度为O(n√n),求d的时候维护对倍数的贡献时间复杂度降低至O(nlogn)
代码:(oj有点奇怪,有时ac有时tle,但是如果不用defineintlonglong而是typedef longlongll肯定会过)

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const int maxn = 2e6+7;

int prime[maxn];
bool vis[maxn];
int mu[maxn];
inline void init(){
    int cnt = 0;
    vis[0] = vis[1] = 1;
    mu[1] = 1;
    for(int i=2; i<maxn; i++){
        if(!vis[i]){
            prime[cnt++]=i;
            mu[i]=-1;
        }
        for(int j=0; j<cnt && i*prime[j]<maxn; j++){
            vis[i*prime[j]] = 1;
            if(i%prime[j]){
                mu[i*prime[j]] = -mu[i];
            }
            else{
                mu[i*prime[j]] = 0;
                break;
            }
        }
    }
}

inline ll cn3(int n){
    return 1LL* n * (n-1) * (n-2) / 6;
}

ll d[maxn];
int main(){
    init();
    int n;
    ll ans = 0;
    scanf("%d",&n);
    for(int i=1; i<=n; i++){
        for(int j=i; j<=n; j+=i){
            d[j] += mu[i]*(n/i);
        }
    }
    d[1]--;
    for(int i=1; i<=n; i++){
        ans += d[i] * (n-d[i]-1);
    }
    printf("%lld\n",cn3(n)-(ans/2));
}
全部评论

相关推荐

小红书 后端选手 n*16*1.18+签字费期权
点赞 评论 收藏
分享
11-01 08:48
门头沟学院 C++
伤心的候选人在吵架:佬你不要的,能不能拿户口本证明过户给我。。球球了
点赞 评论 收藏
分享
10-11 17:45
门头沟学院 Java
走吗:别怕 我以前也是这么认为 虽然一面就挂 但是颇有收获!
点赞 评论 收藏
分享
评论
4
收藏
分享
牛客网
牛客企业服务