2022牛客多校补题

C

img
img

思路

几何 + 枚举

  • 首先通过画图我们可以知道,同一行上覆盖最多的点由该行第一个被占据的座位决定,所以要预处理出每一行最靠近黑板的点
  • 由询问数量可以知道,对于每次询问需要用O(n)的时间解决
  • 分两个区域解决,第一次解决左上方被挡住的区域,第二次解决右上方被挡住的区域(以面对该座位为参照物)
    遍历每一行最靠近黑板的点,求出最大的斜率。
    以每一行为参照物列出每一行未被挡住的点
    (注意斜率小的会被斜率大的覆盖)
    枚举所有的行(两次求两个区域)
    注意如果算出来的该行的good seat刚好整数,要排除第一个被占据的位置
    img
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
const int N = 2e5 + 5;
int n, m, k, q;
int px[N], py[N], mx[N], m1[N], m2[N];
signed main()
{
    cin >> n >> m >> k >> q;
    for (int i = 1; i <= k; ++i)
        cin >> px[i] >> py[i];
    while (q--)
    {
        long long ans = 0;
        int p, x, y;
        cin >> p >> x >> y;
        px[p] = x, py[p] = y;
        for (int i = 1; i <= m; ++i)
            mx[i] = n + 1;
        for (int i = 1; i <= k; ++i)
            mx[py[i]] = min(mx[py[i]], px[i]);
        double k = 0;
        for (int i = 1; i <= m; ++i)
        {
            k = max(k, double(i - 1) / mx[i]);
            if (k == 0)
                m1[i] = mx[i] - 1;
            else
                m1[i] = double(i - 1) / k - 1e-9;
        }
        k = 0;
        for (int i = m; i >= 1; i--)
        {
            k = min(k, double(i - m) / mx[i]);
            if (k == 0)
                m2[i] = mx[i] - 1;
            else
                m2[i] = (double)(i - m) / k - 1e-9;
            ans += max((int)0, min(n, min(m1[i], m2[i])));
        }
        cout << ans << endl;
    }
}

D

img
img

思路

一道几何题,需要用到数学解决,通过画图可以知道,所求的最大弧长其实是当A点在线段CD上时所得
img
img
利用三角形公式分别求出两个角的大小,相减即是答案,要注意精度的问题

代码

#include<bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    int t;
    cin>>t;
    cout<< fixed << setprecision(12);
    while(t--)
    {
        double r,x,y,d;
        cin >> r >> x >> y >> d;
        double dis = sqrt(x*x+y*y);
        double a = acos((dis-d)/r);
        double b = acos((dis+d)/r);
        cout << fabs(a-b)*r << endl;
    }
    return 0;
}

I

img
img
img
img

思路

img
dp[i][j] 表示还剩j张没有摸过的牌,手上还有i张单牌还需要多少次摸牌才能胡牌的概率
从卡池里摸牌只有两种可能性

  • 第一种是摸到手里的单牌刚好能凑成对子,此时打出一张单牌
  • 第二种是摸到手里的牌不能凑出对子,此时把这张牌打出

第一种的概率:卡池里有三张单牌,所以出现概率是 i*3/j;
第二种的概率:上一种的反面 (j - i*3) / j;
由于要分数取模,注意逆元的运算

代码

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
const int N = 1e9 + 7;
int dp[20][200];
map<string, int> mapp;
int qmi(int a, int b)
{
    int res = 1;
    while (b)
    {
        if (b & 1)
            res = (res * a) % N;
        a = (a * a) % N;
        b >>= 1;
    }
    return res;
}
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    for (int i = 1; i <= 13; i++)
    {
        for (int j = i * 3; j <= 123; j++)
        {
            int p1 = (i * 3 * qmi(j, N - 2)) % N;
            int p2 = ((j - i * 3) * qmi(j, N - 2)) % N;
            if (i == 1)
                dp[i][j] = (1 + p2 * dp[i][j - 1]) % N;
            else
                dp[i][j] = 1 + (p1 * dp[i - 2][j - 1] + p2 * dp[i][j - 1]) % N;
        }
    }
    int t;
    cin >> t;
    for (int i = 1; i <= t; i++)
    {
        mapp.clear();
        string s;
        cin >> s;
        for (int i = 0; i + 1 < s.size(); i += 2)
        {
            string s1 = s.substr(i, 2);
            mapp[s1]++;
        }
        int num = 0;
        for (auto x : mapp)
        {
            if (x.second == 1)
            {
                num++;
            }
        }
        // cout << num << endl;
        cout << "Case #" << i << ": " << dp[num][123] << endl;
    }

    return 0;
}

J

img
img

思路

img
启发式合并,但凡该点的入度为1,就以该点为起点搜索找前驱,进行合并,将边数小的集合合并到边数大的集合上

代码

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
const int maxn = 2e5 + 10;
set<int>fr[maxn], to[maxn];
int fa[maxn], siz[maxn];
int t, n;
struct s {
	int a, b;
};
int find(int x) {
	if (x != fa[x]) fa[x] = find(fa[x]);
	return fa[x];
}
void merge(int x, int y) {
	x = find(x); y = find(y);
	if (x == y)  return;
	if (siz[x] < siz[y]) swap(x, y); 
		siz[x] += siz[y];
		fa[y] = x;
		vector<s> t;
		for (auto i = to[y].begin(); i != to[y].end(); i++) {
			fr[*i].erase(y);
			to[x].insert(*i);
			fr[*i].insert(x);
			if (fr[*i].size() == 1) {
				s tt{ *i,x };
				t.push_back(tt);
			}
		}
		for (auto i = t.begin(); i != t.end(); i++) {
			merge(( * i).a, ( * i).b);
		}
}
signed main()
{
	cin >> t;
	for (int i = 1; i <= t;i++) {
		cout << "Case #" << i << ": ";
			cin >> n;
	for (int i = 1; i <= n; i++) {
		fa[i] = i;
		siz[i] = 1;
		fr[i].clear();
		to[i].clear();
	}
	for (int i = 1; i <= n; i++) {
		int cnt, x;
		//cin >> cnt;
        cin >> cnt;
		while (cnt--) {
			
			cin >> x;
			fr[i].insert(x);
			to[x].insert(i);
		}
	}
	for (int i = 1; i <= n; i++) {
		if (fr[i].size() == 1) merge(i, *fr[i].begin());
	}
	int ans = 0;
	for (int i = 1; i <= n; i++) {
		if (fa[i] == i) ans = max(ans, siz[i]);
	}
	cout << ans << endl;
	}
	return 0;
}
posted @ 2022-07-27 10:21  Sun-Wind  阅读(29)  评论(0编辑  收藏  举报