zoukankan      html  css  js  c++  java
  • 绵阳东辰国际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
    ", 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)];

    超集和

  • 相关阅读:
    Python学习笔记:pip使用技巧
    机器学习笔记:训练集、验证集和测试集区别
    MySQL学习笔记:3道面试题小测
    Python学习笔记:精确的四舍五入
    Hive学习笔记:列转行之collect_list/collect_set/concat_ws
    Python学习笔记:6个代码性能坏习惯
    爬虫学习笔记:打造自己的代理池
    Mysql学习笔记:5.5升级至8.0版本
    机器学习笔记:sklearn.model_selection.train_test_split切分训练、测试集
    HashSet其实就那么一回事儿之源码浅析
  • 原文地址:https://www.cnblogs.com/wzxbeliever/p/11727121.html
Copyright © 2011-2022 走看看