zoukankan      html  css  js  c++  java
  • 2019正睿csp-s赛前冲刺

    主席遗留下来的遗产~

    (Day5)

    杜爷删代码的时候找到了一个去年他不会的题,所以拿来看看觉得还挺有意思,就是(C)

    然后切完(C)就顺便把(A)(B)也补了。

    吐槽下奇怪的难度顺序:(B > A > C)

    (A)

    如题每个连通子图最后只会剩下(n - 1)条边,所以答案就随便算了。

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    
    using namespace std;
    
    const int N = 100000;
    const int M = 200000; 
    
    int n, m, head[N + 50], num, vis[N + 50], ans, cnt;
    
    struct Node
    {
    	int next, to;
    } edge[M * 2 + 50];
    
    void Addedge(int u, int v)
    {
    	edge[++num] = (Node){head[u], v};
    	head[u] = num;
    	return;
    } 
    
    void Read(int &x)
    {
    	x = 0; int p = 0; char st = getchar();
    	while (st < '0' || st > '9') p = (st == '-'), st = getchar();
    	while (st >= '0' && st <= '9') x = (x << 1) + (x << 3) + st - '0', st = getchar();
    	x = p ? -x : x;
    	return;
    }
    
    void Dfs(int x, int fa)
    {
    	vis[x] = 1; cnt++;
    	for (int i = head[x]; i; i = edge[i].next)
    	{
    		int v = edge[i].to;
    		if (v == fa) continue;
    		if (!vis[v]) Dfs(v, x);
    	}
    	return;
    }
    
    int main()
    {
    	Read(n); Read(m);
    	for (int i = 1, u, v; i <= m; i++) 
    	{
    		Read(u), Read(v);
    		if (u == v) continue; 
    		Addedge(u, v), Addedge(v, u);
    	}
    	for (int i = 1; i <= n; i++) 
    		if (!vis[i])
    		{
    			cnt = 0;
    			Dfs(i, 0);
    			ans += cnt - 1;
    		}
    	printf("%d", m - ans);
    	return 0;
    }
    

    (B)

    大概是最难的一题。

    直接算显然非常困难,看到(k)很小于是考虑二分判断答案。

    那么就是求出(<= n)的集合中有多少个数。

    发现每个单独算加起来是不行的,有很多数是重合的,于是容斥。

    但是(2^50)显然不行,注意到值域也很小,然后计算有多少个数的时候实际上要对(n)开根,这样容斥集合里的数的(lcm)如果(> 60)就没用了。

    所以只需要关心(lcm <= 60)的容斥系数,这样直接背包转移。

    但是(1)要单独拿出来算,因为如果(lcm > 60)(1)还是会存在,这样容斥的时候就会漏不少东西。

    #include <iostream> 
    #include <cstdio>
    #include <cstring>
    #include <cmath>
    
    using namespace std;
    
    #define ll long long
    #define int long long
    
    int n, k, a[70], dp[70][70], m, lcm[70][70];
    
    void Read(int &x)
    {
    	x = 0; int p = 0; char st = getchar();
    	while (st < '0' || st > '9') p = (st == '-'), st = getchar();
    	while (st >= '0' && st <= '9') x = (x << 1) + (x << 3) + st - '0', st = getchar();
    	x = p ? -x : x;
    	return; 
    }
    
    int Gcd(int a, int b)
    {
    	return a % b == 0 ? b : Gcd(b, a % b);
    }
    
    int Lcm(int a, int b)
    {
    	return a * b / Gcd(a, b);
    }
    
    void Prework()
    {
    	for (int i = 1; i < k; i++)
    		for (int j = 60; j >= 1; j--)
    		{
    			if (lcm[a[i + 1]][j] <= 60) dp[i + 1][lcm[a[i + 1]][j]] -= dp[i][j];
    			dp[i + 1][j] += dp[i][j];
    		} 
    	return;
    }
    
    ll Ksm(ll a, int b)
    {
    	ll tmp = 1;
    	while (b)
    	{
    		if (b & 1) tmp = tmp * a;
    		a = a * a;
    		b >>= 1;
    	}
    	return tmp;
    }
    
    ll Div(ll x,int y)
    {
    	int k = pow(x, 1.0 / (double)y), k1 = k - 1, k2 = k + 1;
    	ll p = Ksm(k, y), p1 = Ksm(k1, y), p2 = Ksm(k2, y);
    	if(p2 > p && p2 <= x)return k2;
    	if(p <= x) return k;
    	return k1;
    }
    
    int Check(ll pd)
    {
    	ll tmp = 0;
    	for (int j = 1; j <= 60; j++)
    		if (dp[k][j]) tmp = tmp + (ll)(Div(pd, j) - 1LL) * dp[k][j];
    	return tmp + 1 >= (ll)m;
    }
    
    signed main()
    {
    	int t, flag1;
    	Read(t);
    	for (int i = 1; i <= 60; i++)
    		for (int j = 1; j <= 60; j++) 
    			lcm[i][j] = Lcm(i, j); 
    	while (t--)
    	{
    		flag1 = 0;
    		Read(m); Read(k);	
    		memset(dp, 0, sizeof(dp));	
    		for (int i = 1; i <= k; i++) 
    		{	
    			Read(a[i]), dp[i][a[i]] = 1;
    			if (a[i] == 1) flag1 = 1;
    		}
    		if (flag1 || m == 1) { printf("%lld
    ", m); continue; }
     		ll l = 1, r = 1e17;
    		Prework();
    		while (l < r)
    		{
    			ll mid = (l + r) >> 1;
    			if (Check(mid)) r = mid;
    			else l = mid + 1;
    		}
    		printf("%lld
    ", l);
    	}
    	return 0;
    }
    

    (C)

    杜爷说完之后秒了异或和按位与的情况,按位或的情况大概也想出来了,但是没系统学过(SOS dp)所以没法实现。

    学完(SOS dp)就直接从高位往地位贪心,随便算算贡献就好了。

    反正是板子题。

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    
    using namespace std;
    
    const int N = 100000;
    const int M = 8388608; 
    
    int n, q;
    
    void Read(int &x)
    {
    	x = 0; int p = 0; char st = getchar();
    	while (st < '0' || st > '9') p = (st == '-'), st = getchar();
    	while (st >= '0' && st <= '9') x = (x << 1) + (x << 3) + st - '0', st = getchar();
    	x = p ? -x : x;
    	return;
    }
    
    namespace Sub1
    {
    	int a[N + 50], p[N + 50];
    	void Solve()
    	{
    		int flag;
    		for (int i = 1; i <= n; i++) Read(a[i]);
    		int ans = 0, num = n;
    		for (int i = 22; i >= 0; i--)
    		{
    			flag = 0;
    			for (int j = 1; j <= n; j++)
    				if (!p[j] && ((a[j] >> i) & 1)) flag++;
    			if (flag >= 2) 
    			{
    				ans |= (1 << i);
    				for (int j = 1; j <= n; j++)
    					if (!((a[j] >> i) & 1) && !p[j])
    						p[j] = 1, num--;					
    			}
    		}
    		printf("%d %lld", ans, 1LL * num * (num - 1) / 2);
    		return;
    	}
    }
    
    namespace Sub2
    {
    	long long ans = 0;
    	int trie[30000050][2], cnt[30000050], tot = 0, max = 0;
    	void Insert(int x)
    	{
    		int now = 0;
    		for (int i = 22; i >= 0; i--)
    		{
    			int c = (x >> i) & 1;
    			if (!trie[now][c]) trie[now][c] = ++tot;
    			now = trie[now][c];
    		}
    		cnt[now]++;
    		return;
    	}
    	void Qmax(int x)
    	{
    		int tmp = 0, now = 0;
    		for (int i = 22; i >= 0; i--)
    		{
    			int c = (x >> i) & 1;
    			if (trie[now][!c]) tmp |= (1 << i), now = trie[now][!c];
    			else now = trie[now][c];
     		}
     		if (max == tmp) ans += cnt[now];
     		else if (max < tmp) max = tmp, ans = cnt[now];
     		return;
    	}
    	void Solve()
    	{
    		for (int i = 1, x; i <= n; i++) Read(x), Insert(x), Qmax(x);
    		printf("%d %lld", max, ans);
    		return;
    	}
    }
    
    namespace Sub3
    {
    	int dp[M + 50], max = 0, a[N + 50];
    	long long ans = 0;
    	void Solve()
    	{
    		for (int i = 1; i <= n; i++) Read(a[i]), dp[a[i]]++;
    		for (int i = 0; i < 23; i++)
    			for (int j = 0; j < M; j++)
    				if (!((j >> i) & 1)) dp[j] += dp[j | (1 << i)];
    		for (int i = 1; i <= n; i++)
    		{
    			int t = 0;
    			for (int j = 22; j >= 0; j--)
    			{
    				if ((a[i] >> j) & 1) continue;
    				if (dp[t | (1 << j)]) t |= (1 << j);		
    			}
    			if (max < (a[i] | t)) max = (a[i] | t), ans = dp[t] - (!t);
    			else if (max == (a[i] | t)) ans += dp[t] - (!t);
    		}
    		printf("%d %lld", max, ans / 2);
    		return;
    	}
    }
    
    int main()
    {
    	Read(n); Read(q);
    	if (q == 1) Sub1::Solve();
    	else if (q == 2) Sub2::Solve();
    	else if (q == 3) Sub3::Solve();
    	return 0;
    }
    
  • 相关阅读:
    spark系列-6、对Application,Driver,Job,Task,Stage的理解
    spark系列-5、RDD、DataFrame、Dataset的区别和各自的优势
    spark系列-4、spark序列化方案、GC对spark性能的影响
    spark系列-2、Spark 核心数据结构:弹性分布式数据集 RDD
    nginx学习(九):跨域配置和防盗链配置
    nginx学习(八):nginx配置gzip
    nginx学习(七):nginx提供静态资源服务
    nginx学习(六):日志切割
    nginx学习(五):nginx.conf 核心配置文件详解
    nginx学习(四):nginx处理web请求机制
  • 原文地址:https://www.cnblogs.com/Tian-Xing-Sakura/p/13803486.html
Copyright © 2011-2022 走看看