"蔚来杯"2022牛客暑期多校训练营1 GADICJ (对官方题姐的一些理解,新手向)(未完)
"蔚来杯"2022牛客暑期多校训练营1
https://ac.nowcoder.com/acm/contest/33186#question
我队战绩:3/11(其实是队友战绩。。因为我太菜了)
Problem A. Villages: Landlines
题意
整晕了。。反正就是给n个区间,然后求把所有区间连在一起的代价
解法
也就是看没有被区间覆盖到的长度是多少即可
Code
主要是题目太长吓死人了
#include <bits/stdc++.h>
#define int long long
using namespace std;
typedef pair<int, int> pii;
const int N = 2e5 + 5;
int n;
int l[N], r[N];
signed main () {
cin >> n;
for (int i = 0; i < n; i ++) {
int x, y;
cin >> x >> y;
l[i] = x - y, r[i] = x + y;
}
sort (l, l + n), sort (r, r + n);
int ans = 0;
for (int i = 1; i < n; i ++) {
if (l[i] > r[i-1]) ans += l[i] - r[i-1];
}
cout << ans << endl;
}
//把所有区间连起来的代价
Problem B. Spirit Circle Observation
Problem C. Grab the Seat!
题意
(结合样例的图来说明)
给定 n,m,k,q, 那么 座位的范围就是1-n(横),1-m(竖),
在y轴上有一段屏幕(范围是1-m)(图上最左边黑的那一块),然后现在有k 个学生已经占了位置(表示为图中的红点),占了位置的学生会挡住后面的位置(也就是图中黄色区域的部分)。然后有 q 次修改(已经占了的位置)。问每次修改过后,没被挡住的位置有多少个(也就是图中绿色的点的个数)。
分析
所以问题就转化为 求解每一次被挡住的并集。
观察上图,不难得出一条性质:每次只需要考虑当行横坐标最小的(被占的)点挡住的区域即可(因为在同一行内,横坐标大的能覆盖的区域 一定会小于 横坐标小的能覆盖的区域,随便找两个点画画图就知道了)
所以就是黑色的段分上下,挨个统计,更新最大的斜率,然后计算该斜率下能被用到的点有多少
至于具体怎么统计这个点,可以找延长线的交点。
即 第i列交到第i+1列的 点的横坐标与第i+1列的第一个点进行比较,取较小的那个
Code
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2e5 + 5;
int n, m, k, q;
int x[N], y[N], minx[N], ans[N];
void init () {
for (int i = 1; i <= m; i ++)
ans[i] = n, minx[i] = n + 1;
for (int i = 1; i <= k; i ++)
minx[y[i]] = min (minx[y[i]], x[i]);
}
int count () {
init ();
int dx, dy;
//统计上半部分的斜率
dx = 1, dy = 0;
for (int i = 1; i <= m; i ++) {
//第一列,斜率为0
if (i == 1) ans[i] = min (ans[i], minx[i] - 1);
else {
if ((i-1)*dx > minx[i]*dy) dx = minx[i], dy = i-1;
int cnt = ((i-1)*dx + dy-1)/dy - 1;
ans[i] = min (ans[i], cnt);
}
}
//统计下半部分的斜率
dx = 1, dy = 0;
for (int i = m; i >= 1; i --) {
//第m列,斜率为0
if (i == m) ans[i] = min (ans[i], minx[i] - 1);
else {
if ((m-i)*dx > minx[i]*dy) dx = minx[i], dy = m-i;
int cnt = ((m-i)*dx + dy-1)/dy - 1;
ans[i] = min (ans[i], cnt);
}
}
int sum = 0;
for (int i = 1; i <= m; i ++) sum += ans[i];
return sum;
}
signed main () {
cin >> n >> m >> k >> q;
for (int i = 1; i <= k; i ++)
cin >> x[i] >> y[i];
while (q --) {
int id; cin >> id;
cin >> x[id] >> y[id];
cout << count () << endl;
}
}
Problem D. Mocha and Railgun
疯狂都错题之我看不懂字 =_=
题意
给定Q点坐标和半径d(会形成一个小圆),以及一个圆心在原点,半径为r的大圆,求投射在小圆上对应的大圆的弧长
不懂的看这个图
Q是会动的!!!
这有一篇十分牛的题姐
Code
#include <bits/stdc++.h>
#define int long long
#define pi 3.1415926535
using namespace std;
void solve () {
int R, x, y, d;
cin >> R >> x >> y >> d;
double base = sqrt (x*x + y*y);
double cos1 = (base-d) / R, cos2 = (base+d) / R;
cout << fixed << setprecision (15) << R * (acos(cos1) - acos(cos2)) << endl;
}
signed main () {
int t; cin >> t;
while (t --) solve ();
}
//求AB对应在圆上半部分的弧长
//Q为AB中点,先求出A,B坐标
//AB 只会包含在圆的里面
//x^2+y^2=r^2
//只能是劣弧
//Q点是会动的啊
Problem E. LTCS
Problem F. Cut
Problem G. Lexicographical Maximum
题意
看半天都看不懂英文。。。
输出 \(1-n\) 中字典序最大的数字
解法
首先9肯定要排在前面(才能满足字典序最大),如果该序列除了最后一位不是全9,那么就可以输出 \(n-1\) 位9。
注意除了最后一位这里,一开始就因为这个WA了,举个例子:
998->998
979->99
所以即使最后一位不是9也有可能字典序最大
(可能还是比较抽象,看代码吧)
Code
#include <bits/stdc++.h>
using namespace std;
int main () {
string s;
cin >> s;
int n = s.size();
bool suc = true;
// if (n == 1) {
// cout << s << endl;
// return 0;
// }
for (int i = 0; i < n-1; i ++) {
if (s[i] != '9') {
suc = false;
break;
}
}
if (!suc) {
for (int i = 0; i < n-1; i ++)
cout << '9';
}
else cout << s;
}
//1-n中字典序最大的
Problem H. Fly
Problem I. Chiitoitsu
题意
所有的牌有34种,每种牌都有4张
初始牌有 13 张麻将牌,相同牌至多出现 2 张
每轮可以从牌堆摸牌,若达成七对子则赢
若不然则选择手牌中某张牌并丢弃
给定初始手牌,求最优策略下达成七对子的期望轮数
分析
最优策略:如果摸到的牌能和手上的牌凑成对子,则留下,否则丢掉
关于这里如何表示分数,就要用到逆元的知识了
a/b mod p 相当于 a*(pow(a,p-2) mod p)
(1)当a与p互质时,用快速幂求逆元(费马小定理):ksm(a, b, p);
(2)当a与p不互质时,用扩展欧几里得算法求逆元:exgcd(a, p, x, y)。
Code
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int p = 1e9 + 7;
int dp[20][150]; //单排 剩余的排
int ksm(int a, int k) {
int res = 1;
while(k){
if(k & 1) res = res * a % p;
k >>= 1;
a = a * a % p;
}
return res;
}
void pre () {
for(int i = 3;i <= 123;i ++)
dp[1][i] = (1 + (((i - 3) * ksm(i,p - 2) % p) * dp[1][i-1] % p)) % p;
for(int i = 3;i <= 13;i += 2){
for(int j = 3;j <= 123;j ++){
dp[i][j] = (1 + (((i * 3) * ksm(j,p - 2) % p) * dp[i - 2][j - 1] % p) + (((j - i * 3) * ksm(j,p - 2) % p) * dp[i][j - 1] % p)) % p;
}
}
}
void solve () {
string s;
cin >> s;
int s0 = 0;
map<string, int> mp;
string t[s.size()];
for (int i = 0; i < s.size(); i += 2) {
//string t = "";
t[i] += s[i];
t[i] += s[i+1];
mp[t[i]] ++;
}
for (int i = 0; i < s.size(); i += 2) {
if (mp[t[i]] == 1) s0 ++;
}
cout << dp[s0][123] << endl;
}
signed main () {
pre();
int t;
cin >> t;
for (int i = 1; i <= t; i ++) {
cout << "Case #" << i << ": ";
solve ();
}
}
//策略:
//现有两个的一定不丢
//把单张的丢掉