绵阳东辰国际test201910.22am

时间:2019-10-23
本文章向大家介绍绵阳东辰国际test201910.22am,主要包括绵阳东辰国际test201910.22am使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

分析:

陷入Tarjan太深,浪费了很多无用的时间

很明显如果是一棵树的话,直接都染黑了

然后就想到要将每一个连通块都变为一棵树

设连通块总数为C,总共有m条边,有用的就只有n-C条

所以要染色的就有m-n+C直接并查集维护

总结:当时真的是脑残,去找环了

分析:

首先答案是很多个数中找一个二分一个答案M

问题转化为求 [1,M] 内有多少个数字在至少一个S(ni) 里面。

如果是可重复的话直接计算有多少个数在S里面就好

是不可重复的当然就要考虑容斥

补充:容斥原理

我们考虑先计算出有多少个 [1,M] 中的数在 S(n1),S(n2),··· ,S(nk) 中,

然后把它们都加起来。 显然这样算多了:

如果某个数字同时在多个 S(ni) 里面,那么他就重复了。

于是 我们考虑再去掉在至少两个 S(ni) 中的,

然后加上至少三个 S(ni) 中的,

去掉至少四 个 S(ni) 中的……

再考虑一个数如果既是2的次幂,又是3的次幂,那么它也一定是lcm(2,3)=6的次幂

code by chitongzi:

#include <bits/stdc++.h>
#define xx first
#define yy second
#define ll long long
#define mp make_pair
#define pb push_back
#define debug(...) fprintf(stderr,__VA_ARGS__)
#define int long long

using namespace std;

inline long long fpow (long long base, long long v)
{
    long long tot = 1;
    while (v)
    {
        if (v & 1) {
            if (1000000000000000000LL / base < tot) return 1LL << 60;
            tot *= base;
        }
        base *= base;
        v >>= 1;
        if (!base) return v ? 1LL << 60 : tot;
        if (v > 1 && tot > 1000000000LL) return (1LL << 60);
    }
    return tot;
}

inline int gcd (int u, int v)
{
    return !v ? u : gcd (v, u % v);
}

const int N = 65;
int g[N][N], n, m, a[N];
long long dp[N][N];

inline long long check (long long k)
{
    long long res = 0;
    for (int i = 1; i <= 60; ++i)
    {
        if (!dp[n][i]) continue;
        long long p = pow (k, 1.0 / i);
        while (fpow (p, i) > k) p--;
        while (fpow (p + 1, i) <= k) p++;
        p--;//here
        res += dp[n][i] * p;
    }
    return res + 1;
}

signed main ()
{
    for (int i = 1; i <= 60; ++i)
        for (int j = 1; j <= 60; ++j)
            g[i][j] = i * j / gcd (i, j);
    int q;
    scanf ("%lld", &q);
    while (q--)
    {
        scanf ("%lld%lld", &m, &n);
        for (int i = 1; i <= n; ++i)
            scanf ("%lld", &a[i]);
        memset (dp, 0, sizeof dp);
        dp[0][0] = -1;
        for (int i = 0; i < n; ++i)
        {
            for (int j = 0; j <= 60; ++j)
            {
                if (!dp[i][j]) continue;
                dp[i + 1][j] += dp[i][j];
                int lcm = j ? j / gcd (j, a[i + 1]) * a[i + 1] : a[i + 1];
                if (lcm > 60) continue;
                dp[i + 1][lcm] -= dp[i][j];
            }
        }
//      for (int i = 0; i <= n; ++i, puts (""))
//          for (int j = 0; j <= 20; ++j)
//              printf ("%lld ", dp[i][j]);
        long long l = 1, r = 100000000000000000LL, mid;
        while (l < r)
        {
            mid = (l + r) / 2;
            if (check (mid) < m) l = mid + 1;
            else r = mid;
        }
        printf ("%lld\n", l);
    }
    return 0;
}

按位与

我们按照从高位到低位的顺序进行贪心

如果当前位有至少两个数字是 1,那么 我们可以让这一位是 1

因此最终答案的这一位一定是 1。

所 以我们可以去掉所有这一位为 0 的数字,然后继续向下贪心即可。

最后的方案数就是 剩余的数字中选出两个的方案数。

按位异或

这样就是01trie树的经典例题,但貌似我打卦了

按位或

同样地在确定了一个数之后从高到低贪心

但是这里又多了一个问题:如果这个 数字的当前位是 1,那么我们仍然没法减少候选范围

然后就是什么高维前缀和乱搞

高维前缀和题外话:

子集和
for(int j=0;j<n;j++)

for(int i=0;i<1<<n;i++)

if(i&(1<<j)) dp[i]+=dp[i^(1<<j)];

超集和

原文地址:https://www.cnblogs.com/wzxbeliever/p/11727121.html