2025-11-12 ZYZ28-NOIP-aoao round 2 hetao1733837的record
比赛链接:比赛详情 - ZYZ28-NOIP-aoao round 2 - ZYZOJ
比赛背景
昨天双十一,ZYZ著名NOI Cu选手@TaoRan爆出了惊天大瓜——ZYZ著名NOISC Ag选手@ydtz初中时的恋爱博客!一下为节选片段……
${\color{Grey}她的手好软,糯糯地,比我之前握过的一切东西都要软;}$
${\color{Grey}她的手好凉,让我原本多多少少因为中考而有些燥热的心情瞬间冷静下来;}$
${\color{Grey}她的手好小,小到我的手可以将它完全包住,不留一丝空隙。}$
${\color{Grey}那一刻,我的脑中只剩下了一个念头一一这,是我的女人。}$
A.empty
提交链接:题目详情 - 空 - ZYZOJ
分析
虽然场切了吧,但是显然做过原题,所以还是复盘一下思路。就是说中位数实际就是$b$在一个序列中,比$b$大的和比$b$小的数量相同,那么又由于题目是在排列的背景下,要求子序列,所以,找到$b$的位置,扩展即可。这里还是以前提到的Trick,就是大于某个数的看成1,小于的
正解
#include <bits/stdc++.h>
using namespace std;
const int N = 100005;
int n, b, a[N];
map<int, int> mp;
int main(){
freopen("empty.in", "r", stdin);
freopen("empty.out", "w", stdout);
cin >> n >> b;
int pos;
for (int i = 1; i <= n; i++){
cin >> a[i];
if (a[i] == b){
pos = i;
}
}
int cnt = 0;
for (int i = pos; i <= n; i++){
if (a[i] > b)
cnt++;
if (a[i] < b)
cnt--;
mp[cnt]++;
}
int ans = 0;
cnt = 0;
for (int i = pos; i >= 1; i--){
if (a[i] > b)
cnt++;
if (a[i] < b)
cnt--;
ans += mp[0 - cnt];
}
cout << ans;
}
B.purple
提交链接:题目详情 - 紫 - ZYZOJ
原题链接:[P9178 COCI 2022/2023#5] Diskurs - 洛谷
方法一
分析
其实就是一个性质,对于$hamming(x,y)$,若$z$是$x$按位取反的数,那么$hamming(x,y)+hamming(z,y)=m$。那么问题得到了转化,
求$\max\limits_{1\leq j \leq n}hamming(a_i,a_j)$即求$\min\limits_{1\leq j \leq n}hamming(\neg a_i,a_j)$!
那么,考虑建图,把$hamming$函数为1的两个数之间连边,相当于一个数到$a$中所有数的最短距离。直接$bfs$即可。甚至不需要建图 ,只需要位运算即可。
正解
#include <bits/stdc++.h>
using namespace std;
const int M = 20;
int a[(1 << M) + 5], cnt[(1 << M) + 5];
int n, m;
int main(){
freopen("purple.in", "r", stdin);
freopen("purple.out", "w", stdout);
cin >> n >> m;
memset(cnt, 0xc0, sizeof(cnt));
queue<pair<int, int>> q;
for (int i = 1; i <= n; i++){
cin >> a[i];
cnt[a[i]] = 0;
q.push({a[i], cnt[a[i]]});
}
while (!q.empty()){
auto tmp = q.front();
q.pop();
int p = tmp.first, w = tmp.second;
for (int i = 0; i < m; i++){
int k = p ^ (1 << i);
if (cnt[k] == 0xc0c0c0c0){
cnt[k] = w + 1;
q.push({k, cnt[k]});
}
}
}
for (int i = 1; i <= n; i++){
cout << m - cnt[(1 << m) - 1 ^ a[i]] << " ";
}
}
方法二
来自@mahaihang1
分析
我们发现,$O(n2)$是好做的,因为*__builtin_popcount*复杂度是$O(1)$的,那我暴力匹配即可。这样,我们对序列去重,设此时长度为*K*,可以跑*2e4*左右。那么对于K大于*2e4*的情况,答案一定不会很小,所以,倒序枚举*m*,可以做到$O(2mK)$。但是,实际复杂度更优,其中$2^m$大概最多跑1e3左右,那么复杂度就来到了1e7左右,甚至更优 /jy
歪解
#include <bits/stdc++.h>
using namespace std;
const int N = 2000005;
int n, m, a[N], ans[N], res[N], len, mk[N];
vector<int> v[30];
signed main(){
freopen("purple.in", "r", stdin);
freopen("purple.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> m;
for (int i = 0; i < (1 << m); i++)
v[__builtin_popcount(i)].push_back(i);
for (int i = 1; i <= n; i++){
cin >> a[i];
ans[i] = a[i];
mk[a[i]] = 1;
}
sort(a + 1, a + 1 + n);
len = unique(a + 1, a + 1 + n) - a - 1;
if (len <= 10000){
for (int i = 1; i <= len; i++){
for (int j = i + 1; j <= len; j++){
res[a[i]] = max(res[a[i]], __builtin_popcount(a[i] ^ a[j]));
res[a[j]] = max(res[a[j]], __builtin_popcount(a[i] ^ a[j]));
}
}
}
else{
for (int i = 1; i <= len; i++){
for (res[a[i]] = m; res[a[i]] > 0; res[a[i]]--){
int f = 0;
for (int x : v[res[a[i]]]){
if (mk[a[i] ^ x]){
f = 1;
break;
}
}
if (f)
break;
}
}
}
for (int i = 1; i <= n; i++)
cout << res[ans[i]] << " ";
return 0;
}
C.flower
提交链接:题目详情 - 花 - ZYZOJ
原题链接1:[AT_agc001_e [AGC001E] BBQ Hard]([AT_agc001_e AGC001E] BBQ Hard - 洛谷)
原题链接2:[E - BBQ Hard](E - BBQ Hard)
分析
看来aoao还是比较人性的,居然给出了形式化的题面,直接要求程序快速求解$$\sum\limits_{i=1}{n}\sum\limits_{j=1}\binom{a_i+a_j+b_i+b_j}{a_i+a_j}$$即可。
那么,原式计算的时间复杂度是$O(n^2)$的,接受不了,考虑转化为组合意义(呃,原题的组合意义似乎不太能用)。
我们设$n=a_i+a_j,m=b_i+b_j$将其转化为$\binom{m+n}{n}$,这种东西我们是有套路的,可以直接转化为从$(0,0)$到$(m,n)$只能向上或向右走的方案数。但是,式子还是与$i$与$j$同时相关,那么,我们考虑将其转化为从$(-a_i,-b_i)$到$(a_j,b_j)$的方案数。那么讲所有$(-a_i,-b_i)$位置$+1$,然后只需知道所有$(a_j,b_j)$位置的和即可。那么直接$O(V^2)$DP求解即可(V是值域)。还需要减掉$(-a_i,-b_i)$到$(a_j,b_j)$的贡献(组合数),最后答案乘$\frac{1}{2}$即可。
我会了……吗?似乎并非,还是看代码吧……
正解
#include <bits/stdc++.h>
#define int long long
#define mod 998244853 //注意模数
using namespace std;
const int N = 200005, V = 5000, K = 2500;
int inv = (mod + 1) / 2;
int n, a[N], b[N], fac[N], dp[V][V], ans;
int qpow(int a, int b){
int res = 1;
while (b){
if (b & 1){
res = res * a % mod;
}
a = a * a % mod;
b >>= 1;
}
return res;
}
int calc(int x, int y){
return fac[x] * qpow(fac[y] * fac[x - y] % mod, mod - 2) % mod;
}
signed main(){
freopen("flower.in", "r", stdin);
freopen("flower.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n;
for (int i = 1; i <= n; i++){
cin >> a[i] >> b[i];
dp[K - a[i]][K - b[i]]++;
}
fac[0] = 1;
for (int i = 1; i < N; i++){
fac[i] = fac[i - 1] * i % mod;
}
for (int i = 1; i < V; i++){
for (int j = 1; j < V; j++){
dp[i][j] = (dp[i][j] + dp[i - 1][j] + dp[i][j - 1]) % mod;
}
}
for (int i = 1; i <= n; i++){
ans = (ans + dp[K + a[i]][K + b[i]] - calc(2 * (a[i] + b[i]), 2 * a[i]) + mod) % mod;
}
cout << ans * inv % mod;
}
但是,如果你直接在AtCoder上提交,会发现WA掉了,为什么呢?因为😈aoao改了模数,一方面为了卡掉题解,另一方面998244353,998244853是容易搞混的,曾经有人因此差点G掉,因此,放在此处以警示后人。
AtCoder正解
#include <bits/stdc++.h>
#define int long long
#define mod 1000000007 //注意模数
using namespace std;
const int N = 200005, V = 5000, K = 2500;
int inv = (mod + 1) / 2;
int n, a[N], b[N], fac[N], dp[V][V], ans;
int qpow(int a, int b){
int res = 1;
while (b){
if (b & 1){
res = res * a % mod;
}
a = a * a % mod;
b >>= 1;
}
return res;
}
int calc(int x, int y){
return fac[x] * qpow(fac[y] * fac[x - y] % mod, mod - 2) % mod;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n;
for (int i = 1; i <= n; i++){
cin >> a[i] >> b[i];
dp[K - a[i]][K - b[i]]++;
}
fac[0] = 1;
for (int i = 1; i < N; i++){
fac[i] = fac[i - 1] * i % mod;
}
for (int i = 1; i < V; i++){
for (int j = 1; j < V; j++){
dp[i][j] = (dp[i][j] + dp[i - 1][j] + dp[i][j - 1]) % mod;
}
}
for (int i = 1; i <= n; i++){
ans = (ans + dp[K + a[i]][K + b[i]] - calc(2 * (a[i] + b[i]), 2 * a[i]) + mod) % mod;
}
cout << ans * inv % mod;
}
D.float
提交链接:题目详情 - 浮 - ZYZOJ
原题链接1:[CF2162H Beautiful Problem](CF2162H Beautiful Problem - 洛谷)
原题链接2:H. Beautiful Problem
分析
原式想取$1$,则$x$要么$\ge \max\limits_{j=l}{r}a_j$要么$\le\min\limits_{j=l}a_j$,那么,每个位置会属于四种情况——①只被$\ge$覆盖;②只被$\le$覆盖;③被两种同时覆盖;④不被覆盖。呃剩下的我就不太会了,粘一下STD……
STD
#include <bits/stdc++.h>
using namespace std;
const int N = 2010, inf = 0x3f3f3f3f;
int t, n, m, a[N], dp[N][N][2], c[N], idx;
vector<int> v[N];
struct node{
int l, r;
}q[N];
signed main(){
freopen("float.in", "r", stdin);
freopen("float.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
idx = 0;
cin >> n >> m;
for (int i = 1; i <= n; i++){
c[i] = 0;
v[i].clear();
}
for (int i = 1; i <= n; i++)
cin >> a[i];
for (int i = 1; i <= m; i++){
int l, r;
cin >> l >> r;
v[l].push_back(r);
}
int maxr = 0;
for (int i = 1; i <= n; i++){
if (v[i].empty())
continue;
sort(v[i].begin(), v[i].end());
if (v[i].back() <= maxr)
continue;
maxr = v[i].back();
q[++idx] = (node){i, maxr};
c[i]++;
c[maxr + 1]--;
}
for (int i = 0; i <= idx; i++){
for (int j = 0; j <= n; j++){
dp[i][j][0] = dp[i][j][1] = -inf;
}
}
dp[1][0][1] = q[1].r - q[1].l + 1;
dp[1][q[1].r - q[1].l + 1][0] = 0;
for (int i = 2; i <= idx; i++){
int len = q[i].r - q[i].l + 1, x = max(0, q[i - 1].r - q[i].l + 1);
for (int j = 0; j <= n; j++){
if (j - len + x >= 0)
dp[i][j][0] = max(dp[i][j][0], dp[i - 1][j - len + x][0]);
if (j-len+x>=0)
dp[i][j][0] = max(dp[i][j][0], dp[i - 1][j - len + x][1] - x);
dp[i][j][1] = max(dp[i][j][1], dp[i - 1][j][1] + len - x);
if (j + x <= n)
dp[i][j][1] = max(dp[i][j][1], dp[i - 1][j + x][0] + len - x);
}
}
int cnt = 0;
for (int i = 1; i <= n; i++){
c[i] += c[i - 1];
cnt += (!c[i]);
}
for (int i = 1; i <= n; i++){
int sum[2] = {0, 0}, ans = 0;
for (int j = 1; j <= n; j++){
if (a[j] == i)
continue;
sum[a[j] > i]++;
}
for (int j = 0; j <= n; j++){
int k = max(dp[idx][j][0], dp[idx][j][1]);
ans |= ((sum[0] <= j && sum[1] <= k) || (max(0, sum[0] - j) + max(0, sum[1] - k) <= cnt));
}
cout << ans;
}
return 0;
}
原题是多测,需要改一下。
Codeforces正解
#include <bits/stdc++.h>
using namespace std;
const int N = 2010, inf = 0x3f3f3f3f;
int t, n, m, a[N], dp[N][N][2], c[N], idx;
vector<int> v[N];
struct node{
int l, r;
}q[N];
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int T;
cin >> T;
while (T--){
idx = 0;
cin >> n >> m;
for (int i = 1; i <= n; i++){
c[i] = 0;
v[i].clear();
}
for (int i = 1; i <= n; i++)
cin >> a[i];
for (int i = 1; i <= m; i++){
int l, r;
cin >> l >> r;
v[l].push_back(r);
}
int maxr = 0;
for (int i = 1; i <= n; i++){
if (v[i].empty())
continue;
sort(v[i].begin(), v[i].end());
if (v[i].back() <= maxr)
continue;
maxr = v[i].back();
q[++idx] = (node){i, maxr};
c[i]++;
c[maxr + 1]--;
}
for (int i = 0; i <= idx; i++){
for (int j = 0; j <= n; j++){
dp[i][j][0] = dp[i][j][1] = -inf;
}
}
dp[1][0][1] = q[1].r - q[1].l + 1;
dp[1][q[1].r - q[1].l + 1][0] = 0;
for (int i = 2; i <= idx; i++){
int len = q[i].r - q[i].l + 1, x = max(0, q[i - 1].r - q[i].l + 1);
for (int j = 0; j <= n; j++){
if (j - len + x >= 0)
dp[i][j][0] = max(dp[i][j][0], dp[i - 1][j - len + x][0]);
if (j-len+x>=0)
dp[i][j][0] = max(dp[i][j][0], dp[i - 1][j - len + x][1] - x);
dp[i][j][1] = max(dp[i][j][1], dp[i - 1][j][1] + len - x);
if (j + x <= n)
dp[i][j][1] = max(dp[i][j][1], dp[i - 1][j + x][0] + len - x);
}
}
int cnt = 0;
for (int i = 1; i <= n; i++){
c[i] += c[i - 1];
cnt += (!c[i]);
}
for (int i = 1; i <= n; i++){
int sum[2] = {0, 0}, ans = 0;
for (int j = 1; j <= n; j++){
if (a[j] == i)
continue;
sum[a[j] > i]++;
}
for (int j = 0; j <= n; j++){
int k = max(dp[idx][j][0], dp[idx][j][1]);
ans |= ((sum[0] <= j && sum[1] <= k) || (max(0, sum[0] - j) + max(0, sum[1] - k) <= cnt));
}
cout << ans;
}
cout << '\n';
}
return 0;
}
OK,完结,撒花★,°:.☆( ̄▽ ̄)/$:.°★ 。
posted on 2025-11-12 17:43 hetao1733837 阅读(2) 评论(0) 收藏 举报
浙公网安备 33010602011771号