2022牛客多校补题
C
思路
几何 + 枚举
- 首先通过画图我们可以知道,同一行上覆盖最多的点由该行第一个被占据的座位决定,所以要预处理出每一行最靠近黑板的点
- 由询问数量可以知道,对于每次询问需要用O(n)的时间解决
- 分两个区域解决,第一次解决左上方被挡住的区域,第二次解决右上方被挡住的区域(以面对该座位为参照物)
遍历每一行最靠近黑板的点,求出最大的斜率。
以每一行为参照物列出每一行未被挡住的点
(注意斜率小的会被斜率大的覆盖)
枚举所有的行(两次求两个区域)
注意如果算出来的该行的good seat刚好整数,要排除第一个被占据的位置
#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
思路
一道几何题,需要用到数学解决,通过画图可以知道,所求的最大弧长其实是当A点在线段CD上时所得。
利用三角形公式分别求出两个角的大小,相减即是答案,要注意精度的问题
代码
#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
思路
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
思路
启发式合并,但凡该点的入度为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;
}