• 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;
    }
    
  • 相关阅读:
    10 个深恶痛绝的 Java 异常。。
    为什么公司宁愿 25K 重新招人,也不给你加到 20K?原因太现实……
    推荐一款代码神器,代码量至少省一半!
    Spring Cloud Greenwich 正式发布,Hystrix 即将寿终正寝。。
    hdu 3853 LOOPS(概率 dp 期望)
    hdu 5245 Joyful(期望的计算,好题)
    hdu 4336 Card Collector(期望 dp 状态压缩)
    hdu 4405 Aeroplane chess(概率+dp)
    hdu 5036 Explosion(概率期望+bitset)
    hdu 5033 Building (单调栈 或 暴力枚举 )
  • 原文地址:https://www.cnblogs.com/Tian-Xing-Sakura/p/13803486.html
Copyright © 2020-2023  润新知