牛客多校第四场个人补题A D H J K L N
N
题意
带有a,b能量的两粒子在碰撞之后能量变为a AND b和a OR b ,已知在此种碰撞规则下所有粒子能量方差不会减少,并最后会趋于一个稳定值,求最后所有粒子碰撞趋于稳定后的方差
思路
在比赛的时候先很容易得到与和或运算不会改变能量和,所以平均值是一定的,但是模拟样例一直都没有过样例,直到队友把样例模拟出来了,就猜了一发将所有的位数的1统计一下并尽可能集中的放置,就过了。现在想的话证明似乎也不困难,相当于与和或运算就是将两个数二进制表示的各位1尽可能的移到一边去,0移到另一边去,最后模拟整个过程就好了。另外要注意的是这个数据会爆long long要用___int128读入。
代码
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
using namespace std;
const int INF = 0x3f3f3f3f3f3f3f3f;
const int N = 2e6 + 10;
const int mod = 1e9 + 7;
typedef pair<int, int> PII;
inline __int128 read() {
__int128 x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {
if (ch == '-') f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = x * 10 + ch - '0';
ch = getchar();
}
return x * f;
}
inline void print(__int128 x) {
if (x < 0) {
putchar('-');
x = -x;
}
if (x > 9) print(x / 10);
putchar(x % 10 + '0');
}
int cnt[N];
int qpow(int a, int b) {
int ans = 1, base = a;
while (b) {
if (b & 1) ans = ans * base;
base = base * base;
b >>= 1;
}
return ans;
}
void solve() {
int n;
cin >> n;
int sum = 0;
for (int i = 1; i <= n; i++) {
int x;
cin >> x;
sum += x;
string s;
while (x) {
if (x & 1) s += '1';
else s += '0';
x >>= 1;
}
for (int j = 0; s[j]; j++) {
if (s[j] == '1') cnt[j]++;
}
}
__int128 ans = 0;
for (int i = 1; i <= n; i++) {
__int128 res = 0;
for (int j = 63; j >= 0; j--) {
if (cnt[j]) {
cnt[j]--;
res += qpow(2, j);
}
}
ans += (res * n - sum) * (res * n - sum);
}
if (ans != 0) {
print(ans / __gcd(ans, (__int128)n * n * n));
cout << '/';
print(n * n * n / __gcd(ans, (__int128)n * n * n));
} else {
cout << "0/1";
}
}
signed main() {
// IOS;
int t = 1;
// cin >> t;
for (int i = 1; i <= t; i++) {
solve();
}
}
K
题意
有n个数,一个人从0出发,初始价值是0,要求只有当价值与所在点模n同余时才能通过,在任意点都可以进行任意次将价值乘以10+x(0≤x≤9),现在问操作次数最少是多少
思路
一开始看这题过的最多以为是签到,结果一直耗在这题,想到了和位数有关,后来写了一个暴力,结果wa了,卡了一个小时发现是1没有特判掉(太坑了吧),然后又想了一会优化了一下暴力过了,大概思路就是不断枚举最少需要将当前价值往后移动几位,看是否有使得位数不变的数满足同余的条件(不知道为啥每次操作在枚举10位内一定有解),另外要注意的是每次价值其实都可以更新为i-1,这和原值是等价的,不然会爆long long。调了半天,最后刚刚封榜才过。。。
代码
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
using namespace std;
const int INF = 0x3f3f3f3f3f3f3f3f;
const int N = 2e6 + 10;
const int mod = 1e9 + 7;
typedef pair<int, int> PII;
int qpow(int a, int b) {
int ans = 1, base = a;
while (b) {
if (b & 1) ans = ans * base ;
base = base * base ;
b >>= 1;
}
return ans ;
}
void solve() {
int n;
cin >> n;
if (n == 1) {
cout << 0 << endl;
return;
}
int res = 1;
for (int i = 2; i <= n; i++) {
int p=i-1;
for(int j=1;j<=10;j++) {
p*=10;
if(p%n-i%n<0) {
if((i%n-p%n)>=0&&(i%n-p%n)<=(qpow(10,j)-1)) {
res+=j;
break;
}
}
else if(p%n-i%n==0){
res+=j;
break;
}
else {
if((n-(p%n-i%n))>=0&&(n-(p%n-i%n))<=(qpow(10,j)-1)) {
res+=j;
break;
}
}
}
}
cout << res << endl;
}
signed main() {
IOS;
int t = 1;
// cin >> t;
for (int i = 1; i <= t; i++) {
solve();
}
}
D
题意
给n个公司,每个公司有不同个数个职位(≤10),每个职位对IQ,EQ,AQ有最低限度要求,现在有q个人,每个人都随机生成三个值作为IQ,AQ,EQ(≤400),第i个人能被 a n s i ans_i ansi 个公司录用,求 ∑ i = 1 q a n s i ⋅ s e e d q − i m o d 998244353 \sum_{i=1}^q ans_i \cdot seed^{q-i} \quad mod998244353 i=1∑qansi⋅seedq−imod998244353
思路
先想着用二分做,,用排列组合得到六种按不同排列规则所得的最低值,只要有一个不能满足就不行,结果不知道为什么wa了,后来注意到数据范围很小,就尝试开四维数组用三维的前缀和做,结果发现开不下,后来干脆想着用一位代表一个公司就可以优化成三维的了,最后半个小时本来过了这题的,但是我敲代码的时候少敲了一个等号,赛后才发现。属实背大锅。
代码
#include <bits/stdc++.h>
#include <random>
using namespace std;
#define mod 998244353
#define int long long
int qpow(int b, int p){
int res = 1;
while(p){
if(p & 1){
res = (res * b) % mod;
}
b = (b * b) % mod;
p >>= 1;
}
return res%mod;
}
int a[410][410][410];
int n,q;
int solve(int x,int y,int z) {
int res=0;
for(int i=1;i<=n;i++) {
if(a[x][y][z]&(1<<i)) res++;
}
return res;
}
signed main(){
cin >> n >> q;
for(int i = 1;i <= n;i++){
int sz;
cin >> sz;
for(int j=1;j<=sz;j++) {
int x,y,z;
cin>>x>>y>>z;
a[x][y][z]|=(1<<i);
}
for(int p=1;p<=400;p++) {
for(int m=1;m<=400;m++) {
for(int k=1;k<=400;k++) {
a[p][m][k]=a[p-1][m][k]|a[p][m-1][k]|a[p][m][k-1]|a[p][m][k];
}
}
}
}
int seed;
cin >> seed;
std::mt19937 rng(seed);
std::uniform_int_distribution<> u(1,400);
int lastans=0;
int ans = 0;
for (int i=1;i<=q;i++)
{
int IQ=(u(rng)^lastans)%400+1; // The IQ of the i-th friend
int EQ=(u(rng)^lastans)%400+1; // The EQ of the i-th friend
int AQ=(u(rng)^lastans)%400+1; // The AQ of the i-th friend
lastans=solve(IQ,EQ,AQ); // The answer to the i-th friend
ans = (ans + (lastans * qpow(seed, q - i))%mod) % mod;
}
cout << ans << "\n";
return 0;
}
H
题意
给你一堆积木,问所有积木拼起来周长最小是多少
思路
很快想到在面积一定的情况下一定是长宽差越小周长越小,但是到现在为止还是不会证明为什么一定能拼出来这样的矩形(长宽一定能凑出来这样的长度吗???),另外求出最小周长的长宽之后卡在了如何拼的问题上,后来先是猜了一发是先取大的再取小的过了,之后看题解发现这貌似是cf上一题的结论。。。现在看的话赛时榜还是歪了,不盲目跟榜的话这题应该可以做出来的
代码
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
using namespace std;
const int INF = 0x3f3f3f3f3f3f3f3f;
const int N = 1e6 + 10;
const int mod = 1e9 + 7;
typedef pair<int, int> PII;
int a[N];
void solve() {
int n;
cin>>n;
int s=0;
for(int i=1;i<=n;i++) {
s+=i*(n+1-i);
a[n+1-i]=i;
}
int len=sqrt(s);
while(s%len) len++;
cout<<2*(len+s/len)<<endl;
for(int i=1;i<=s/len;i++) {
int k=0;
while(k<len) {
for(int j=len-k;j>=1;j--) {
if(a[j]) {
a[j]--;
cout<<k<<' '<<i-1<<' '<<k+j<<' '<<i<<endl;
k+=j;
break;
}
}
}
}
}
signed main() {
IOS;
int t = 1;
cin >> t;
for (int i = 1; i <= t; i++) {
solve();
}
}
A
题意
从n个任务中选m个( a 1 a_1 a1, a 2 a_2 a2, . . . ... ..., a m a_m am)出来并任意排序,收益是 ∑ i = 1 m w a i ∏ j = 0 i − 1 p a j \sum_{i=1}^m w_{a_i} \prod_{j=0}^{i-1}p_{a_j } ∑i=1mwai∏j=0i−1paj,求最大收益
思路
赛后没有思路看的ygg题解,发现是对任意两个相邻的比较可以自定义排序,排在前面的先取比较好,就变成了一个背包问题(倒着找比正着找方便)
代码
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
using namespace std;
const int INF = 0x3f3f3f3f3f3f3f3f;
const int N = 1e5 + 10;
const int mod = 1e9 + 7;
typedef pair<int, int> PII;
struct node{
int w;
double q;
bool operator<(node a) const{
return a.w+a.q*w<w+q*a.w;
}
}a[N];
double dp[N][50];
void solve() {
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>a[i].w;
for(int i=1;i<=n;i++) cin>>a[i].q,a[i].q/=10000;
sort(a+1,a+n+1);
for(int i=1;i<=n+1;i++) {
for(int j=0;j<=m;j++) {
dp[i][j]=-INF;
}
}
dp[n+1][0]=0;
for(int i=n;i>=1;i--) {
for(int j=0;j<=m;j++) {
dp[i][j]=max(dp[i][j],dp[i+1][j]);
}
for(int j=0;j<=m;j++) {
dp[i][j+1]=max(dp[i][j+1],dp[i+1][j]*a[i].q+a[i].w);
}
}
cout<<fixed<<setprecision(10)<<dp[1][m]<<endl;
}
signed main() {
IOS;
int t = 1;
// cin >> t;
for (int i = 1; i <= t; i++) {
solve();
}
}
L
题意
求边长为a的凸正n面体收缩k次后的面数和边长。一次收缩定义为作该几何体所有面中心的三维凸包。
思路
这题感觉是一个小trick,数据范围给的是n从1一直到200,但是实际上可以形成正多面体的一共只有五种,这一点可以通过欧拉公式证明。这样题目就变得简单了,画图可以发现四面体转化后还是四面体,六面体转化后是八面体,八面体转化后是六面体,十二面题和二十面体互相转化(画图太难画了感觉这一组多面体需要记结论,其他几组都很好推)
代码
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
using namespace std;
const int INF = 0x3f3f3f3f3f3f3f3f;
const int N = 1e5 + 10;
const int mod = 1e9 + 7;
typedef pair<int, int> PII;
void solve() {
int n, k;
double a;
cin >> n >> a >> k;
if (n == 4) {
while (k--) a /= 3;
cout << "possible " << n << ' ' << fixed << setprecision(10) << a << endl;
} else if (n == 6) {
int ok = 0;
while (k--) {
if (!ok) {
a /= sqrt(2);
} else {
a = a / 3 * sqrt(2);
}
ok ^= 1;
if (n == 6) n = 8;
else n = 6;
}
cout << "possible " << n << ' ' << fixed << setprecision(10) << a << endl;
} else if (n == 8) {
int ok = 1;
while (k--) {
if (!ok) {
a /= sqrt(2);
} else {
a = a / 3 * sqrt(2);
}
ok ^= 1;
if (n == 6) n = 8;
else n = 6;
}
cout << "possible " << n << ' ' << fixed << setprecision(10) << a << endl;
} else if (n == 12) {
int ok = 1;
while (k--) {
if (!ok) {
a = a / 6 * (sqrt(5) + 1);
} else {
a = a / 10 * (3 * sqrt(5) + 5);
}
ok ^= 1;
if (n == 12) n = 20;
else n = 12;
}
cout << "possible " << n << ' ' << fixed << setprecision(10) << a << endl;
} else if (n == 20) {
int ok = 0;
while (k--) {
if (!ok) {
a = a / 6 * (sqrt(5) + 1);
} else {
a = a / 10 * (3 * sqrt(5) + 5);
}
ok ^= 1;
if (n == 12) n = 20;
else n = 12;
}
cout << "possible " << n << ' ' << fixed << setprecision(10) << a << endl;
} else {
cout << "impossible\n";
}
}
signed main() {
IOS;
int t = 1;
cin >> t;
for (int i = 1; i <= t; i++) {
solve();
}
}
J
题意
给一个无穷大的网格图,左下角为 (0, 0)。有 m 次操作,每次添加或删除一条斜率为 -1 的线段(给出端点坐标),并输出图中“鱼”的数量。“鱼”定义为一个正方形加上一个等腰直角三角形,且正方形边长等于三角形直角边边长、三角形与正方形恰有一个顶点重合。
思路
主要思想感觉不是很难,画一画可以发现其实就是等腰直角三角形和以两个斜边的顶点分别引斜率为- 1 2 \frac{1}{2} 21,-2的线内所包含的点,但是麻烦的是维护每个斜率为-1直线所在的线段是哪一部分,计算几何思路很简单但是写起来要讨论的东西太多了,写麻了。。。
代码
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
using namespace std;
const int INF = 0x3f3f3f3f3f3f3f3f;
const int N = 1e6 + 10;
const int mod = 1e9 + 7;
typedef pair<int, int> PII;
set<PII> S[N];
int n;
int C(int x, int y) {
if (x > y) return 0;
return (y + x) * (y - x + 1) / 2;
}
int CC(int x, int y) {
int ans = 0;
if (x <= y && x % 2 == 0) {
ans += ceil(1.0 * x / 2);
x++;
}
if (x <= y && y % 2 == 1) {
ans += ceil(1.0 * y / 2);
y--;
}
if (x < y) {
x = (x + 1) / 2, y = (y + 1) / 2;
ans += C(x, y) * 2;
}
return ans;
}
int calc(int x1, int y1, int x2, int y2) {
int n = x1 + y1;
int num1 = (x2 - x1) * (x2 - x1 + 1) / 2;
num1 += (x2 - x1 + 1) * (x2 + x1) / 2;
int t = 2 * n / 3;
t++;
t = max(t, x1);
int t2 = (x1 + n) / 2;
t2 = min(t2, x2);
num1 -= max((int)0, t2 - t + 1) * x1;
t2 = (x1 + n) / 2;
t2++;
t2 = max(t2, x1);
t2 = max(t2, 2 * n / 3 + 1);
num1 -= 2 * C(t2, x2) - max(x2 - t2 + 1, (int)0) * n;
int k1 = min(2 * n / 3, 2 * x1);
k1 = min(k1, x2);
num1 -= max((int)0, k1 - x1 + 1) * x1;
num1 -= CC(2 * x1 + 1, min(2 * n / 3, x2));
return num1;
}
signed main() {
IOS;
cin >> n;
int ans = 0;
while (n--) {
int op, X1, Y1, X2, Y2;
cin >> op >> X1 >> Y1 >> X2 >> Y2;
if (X1 > X2) swap(X1, X2), swap(Y1, Y2);
int t = X1 + Y1;
if (op == 1) {
auto it = S[t].lower_bound({X1, -1e9});
if (it != S[t].end() && (*it).first == X1) {
auto it2 = prev(it);
ans -= it2->second;
S[t].erase(it);
X1 = it2->first;
S[t].erase(it2);
}
it = S[t].lower_bound({X2, -1e9});
if (it != S[t].end() && (*it).first == X2) {
auto it2 = next(it);
ans -= it->second;
S[t].erase(it);
X2 = it2->first;
S[t].erase(it2);
}
int ans2 = calc(X1, t - X1, X2, t - X2);
S[t].insert({X1, ans2});
S[t].insert({X2, -1});
ans += ans2;
} else {
auto it = S[t].lower_bound({X1, 1e18});
--it;
ans -= it->second;
vector< pair<int, int> > ve;
if (it->first != X1) {
ve.push_back({it->first, X1});
}
++it;
if (it->first != X2) {
ve.push_back({X2, it->first});
}
auto it2 = prev(it);
S[t].erase(it);
S[t].erase(it2);
for (auto v : ve) {
int ans2 = calc(v.first, t - v.first, v.second, t - v.second);
S[t].insert({v.first, ans2});
S[t].insert({v.second, -1});
ans += ans2;
}
}
cout << ans << endl;
}
}