2022 NOI Online 游记 + 赛后复盘

人生中 第一场OI全国大赛

感觉还不错 虽然结果一般(还没出成绩 估分很低

但也毕竟是个“练兵”

是为了日后在比赛中取得更好的成绩

打好基础 埋下伏笔

8:30开始

8:27左右就能进了

 

 

T1

丹钓战

谐音梗是要扣钱的bushi

正巧了 25号老师刚带我们练了一套题 第一题就是用单调栈去写的矩阵题 一眼就看出来了谐音

(然鹅某条金鱼并没有get到谐音的点并以丹钓战为题脑嗨了一万三千字的唯美故事

看了一会 N能达到5e5 感觉要写线段树

线段树打了1个多小时 打不出来 没办法 直接转暴力 15pts

 

复盘:

受到启发:区间 ——> 莫队

写了个莫队  

复制代码
  1 #include <iostream>
  2 #include <algorithm>
  3 #include <cstdio>
  4 #include <cmath>
  5 using namespace std;
  6 #define int long long
  7 const int N = 5e5 + 5;
  8 inline int read(){
  9     int s = 0,w = 1;
 10     char ch = getchar();
 11     while(ch<'0'||ch>'9'){
 12         if(ch=='-')w = -1;
 13         ch = getchar();
 14     }
 15     while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0',ch = getchar();
 16     return s * w;
 17 }
 18 inline void write(int x){
 19     static char buf[16];
 20     static int len = -1;
 21     if(x < 0) putchar('-'),x = -x;
 22     do buf[++ len] = x % 10,x /= 10;
 23     while(x);
 24     while(len >= 0) putchar(buf[len --] + '0');
 25     putchar('\n');
 26 }
 27 struct A{
 28     int l;
 29     int r;
 30     int num;
 31 }q[N];
 32 int n,Q;
 33 int a[N];
 34 int b[N];
 35 int rcd[N] = {0,0};
 36 int ans[N];
 37 int pos[N];
 38 int nw = 1;
 39 class CHK{
 40 public:
 41     inline void Add_L(){
 42         l--;
 43         vis[rcd[l]]++;
 44         sum++;
 45         sum -= vis[l];
 46     }
 47     inline void Sub_L(){
 48         vis[rcd[l]]--;
 49         sum--;
 50         sum += vis[l];
 51         l++;
 52     }
 53     inline void Add_R(){
 54         r++;
 55         vis[rcd[r]]++;
 56         sum += (rcd[r] < l);
 57     }
 58     inline void Sub_R(){
 59         vis[rcd[r]]--;
 60         sum -= (rcd[r] < l);
 61         r--;
 62     }
 63     inline int L(){
 64         return l;
 65     }
 66     inline int R(){
 67         return r;
 68     }
 69     inline int Sum_(){
 70         return sum;
 71     }
 72 private:
 73     int vis[N];
 74     int l = 1,r = 0;
 75     int sum = 0;
 76 };
 77 CHK Stack_;//最近迷上了class
 78 signed main(){
 79     n = read();
 80     Q = read();
 81     int siz = sqrt(n);
 82     for(register int i = 1;i <= n;i++) a[i] = read();
 83     for(register int i = 1;i <= n;i++) b[i] = read();
 84     for (register int i = 2;i <= n;i ++,nw = i - 1){
 85         while(nw){
 86             if (a[i] == a[nw] || b[i] >= b[nw]) nw = rcd[nw];
 87             else break;
 88         }
 89         rcd[i] = nw;
 90     }
 91     for(register int i = 1;i<=Q;i ++){
 92         q[i].l = read();
 93         q[i].r = read();
 94         q[i].num = i;
 95         pos[i] = (q[i].l - 1)/(siz + 1);//收到大佬启发;i/siz只能过60pts
 96     }
 97     sort(q + 1,q + 1 + Q,[](A a,A b){return pos[a.num] != pos[b.num]?a.l < b.l : ((pos[a.num] & 1) ? a.r < b.r : a.r > b.r);});//在B站学到的高级cmp写法
 98     for(register int i = 1;i<=Q;i ++){
 99         while(q[i].l < Stack_.L()) Stack_.Add_L();
100         while(q[i].r > Stack_.R()) Stack_.Add_R();
101         while(q[i].l > Stack_.L()) Stack_.Sub_L();
102         while(q[i].r < Stack_.R()) Stack_.Sub_R();
103         ans[q[i].num] = Stack_.Sum_();
104     }
105     for(register int i = 1;i<=Q;i ++) write(ans[i]);
106     return 0;
107 }
复制代码

 

T2

讨论

乍一看以为是图论

若该点入度大于2 则被两个以上的人会 即被两个以上的点所指向 那就查这些点的出度 出度>2则可能有不会的题 为了控复杂度 用string去存每个人会的题 s.find()找包含(事实证明这是一种很扯淡的做法)

这样存图 每个人的编号既是一个人 也是一道题 一举两得

但是肯定是什么地方写的不对 访问溢出 RE 

 

复盘:

从头开始推

如果一个人参与了讨论,则它必然会与另一个人有同样会的题目;

按会的题量降序排序,若上下两人有共同的题目,上面的人会的题数>下面的人,所以一定会有下面不会的题;

则只需判断下面的人是否有上面的人不会的题即可;

与记入度同理 记录每道题“被会”的数量 若数量 == 1,则它是只被当前的人会,属于新题;

若数量 >= 2,则对于当前的人来说,他与下面的那个人就不会这道题,则他俩是可以开始讨论的 

 

复制代码
 1 #include <iostream>
 2 #include <algorithm>
 3 #include <cstdio>
 4 #include <cstring>
 5 #include <limits.h>
 6 using namespace std;
 7 const int N = 5e5 +5;
 8 inline int read(){
 9     int dp = 0,w = 1;
10     char ch = getchar();
11     while(ch<'0'||ch>'9'){
12         if(ch=='-')w = -1;
13         ch = getchar();
14     }
15     while(ch >= '0' && ch <= '9') dp = dp * 10 + ch - '0',ch = getchar();
16     return dp * w;
17 }
18 inline void write(int x){
19     static char buf[16];
20     static int len = -1;
21     if(x < 0) putchar('-'),x = -x;
22     do buf[++ len] = x % 10,x /= 10;
23     while(x);
24     while(len >= 0) putchar(buf[len --] + '0');
25     putchar('\n');
26 }
27 int T;
28 int n,p;
29 int x = -1,y = -1;
30 int t,v;
31 struct A{
32     int pos;
33     int num;
34 }ans[N];
35 int a[N];
36 int f[N];
37 int dp[N] = {0};
38 bool chk = 0;
39 int main() {
40     T = read();
41     while (T--) {
42         chk = 0;
43         memset(f,0,sizeof(f));
44         n = read();
45         for (register int i = 1; i <= n; i++) {
46             ans[i].pos = i;
47             ans[i].num = read();
48             dp[i] = dp[i - 1] + ans[i].num;
49             for (register int j = 1;j<=ans[i];j++) a[dp[i-1]+j] = read();
50         }
51         sort(ans + 1,ans + n + 1,[](A a,A b){return a.num > b.num;});
52         for(register int i = 1;i<=n;i++,x = -1,y = -1){
53             p = ans[i].pos;
54             for(register int j = 1;j<=ans[p].num;j++){
55                 v = f[a[dp[p-1]+j]];
56                 if(!v) v = p;
57                 if(!x) x = v;
58                 else if(x != v) y = v;
59                 if(x != -1 && y != -1){
60                     chk = 1;
61                     ans[p].num = -INT_MAX;
62                 }
63                 f[a[dp[p-1]+j]] = p;
64             }
65         }
66         if(chk) puts("NO"),cout<<endl;
67         else{
68             if(ans[x].num > ans[y].num) x = p;
69             else y = p;
70             puts("YES");
71             cout<<endl;
72             printf("%d %d\n",x,y);
73         }
74     }
75     return 0;
76 }
复制代码

 

 

T3

如何正确的去排序

考试的时候没有时间看了 暴力都没时间写 直接打表上样例 估计是保龄

 

复盘:

第一反应还是线段树

但是感觉线段树好麻烦

于是决定用树状数组

上了趟博客园 看了hbc大佬的博客 提醒我了

CDQ分治

老师之前在群里发过一个讲陈丹琦的文章 里面就提到了她的cdq分治

于是就去学了cdq分治

并引申到二维偏序三维偏序

才发现 原来丹钓战也是二维偏序

后来一直在推公式 直到发现m = 4时max(S4)可以被压掉 压回三维

去查了知道 这个东西叫反演

所以整个就是二维偏序+三维偏序(著名的陌上开花)+树状数组+cdq分治+反演(这个实在想不到)

(看到好多人还有写 离散化 容斥)

然后就 疯狂的  TLE   WA 

好在看到了一位大佬的博客

才知道位运算这么好用

还有处理双倍贡献

于是

终于

复制代码
  1 #include <iostream>
  2 #include <cstdio>
  3 #include <algorithm>
  4 #include <limits.h>
  5 #include <cstring>
  6 #include <cmath>
  7 //整个代码是在第一次系的树状数组 + cqd + 三维偏序的基础上
  8 //从大佬的代码中学习了好多降复杂度的写法 包括区域定义、快速位运算等等
  9 //整个复杂度在O(nlog2n)左右
 10 #define MOR 996655884
 11 #define lowbit(x) x & -x
 12 #define int long long
 13 using namespace std;
 14 const int N = 5e5 + 5;
 15 int n,m;
 16 int a[5][N];
 17 inline int read(){
 18     int s = 0,w = 1;
 19     char ch = getchar();
 20     while(ch<'0'||ch>'9'){
 21         if(ch=='-')w = -1;
 22         ch = getchar();
 23     }
 24     while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0',ch = getchar();
 25     return s * w;
 26 }
 27 inline void write(int x) {
 28     static char buf[16];
 29     static int len = -1;
 30     if(x < 0) putchar('-'),x = -x;
 31     do buf[++len] = x % 10,x /= 10;
 32     while(x);
 33     while(len >= 0) putchar(buf[len--] + '0');
 34     putchar('\n');
 35 }
 36 const int INF_ = 2e5;
 37 struct A{
 38     int rcd[4];
 39     int p;
 40     int res;
 41     friend bool operator < (const A &a,const A &b);
 42 };
 43 inline bool operator < (const A a, const A b){
 44     for(register int i = 0;i < 3;i++) if(a.rcd[i] ^ b.rcd[i]) return a.rcd[i] < b.rcd[i];
 45     return a.p < b.p;
 46 }//看了大佬的处理方式 重载运算符
 47 //原先只从一维重定义rcd,m == 4的时候就全失效了
 48 class C{
 49 private:
 50     int s[N*2];
 51     A t[N*2];
 52     int res = 0;
 53 public:
 54     inline void modify(int x,int y){
 55         for(x += INF_; x < INF_ << 1; x += lowbit(x)) s[x] += y;
 56     }
 57     inline int query(int x){
 58         res = 0;
 59         for(x += INF_; x; x -= lowbit(x)) res += s[x];
 60         return res;
 61     }
 62     void cdq(int l,int r,A *f){
 63         if(l == r) return;
 64         /*if(l < r) return;*/
 65         int mid = l + r >> 1;
 66         cdq(l,mid,f);
 67         cdq(mid + 1,r,f);//二分
 68         static A t[N*2];//这步真的好重要
 69         int h = 0;
 70         for(register int i = l,j = mid + 1;(i <= mid) || (j <= r);)
 71             if(i <= mid && (j > r || f[i].rcd[1] <= f[j].rcd[1])){
 72                 f[i].p == -1 && (modify(f[i].rcd[2],1),0);//位运算压时间真的超好用
 73                 t[h] = f[i];
 74                 h++,i++;
 75             }
 76             else{
 77                 if (j <= r && (i > mid || f[i].rcd[1] > f[j].rcd[1])) {
 78                     ~f[j].p && (f[j].res += query(f[j].rcd[2]));
 79                     t[h] = f[j];
 80                     h++,j++;
 81                 }
 82             }
 83         for(register int i = l;i <= mid;i++) f[i].p == -1 && (modify(f[i].rcd[2],-1),0);
 84         int k = 0;
 85         for(register int i = l;i <= r;i++){
 86             f[i] = t[k];
 87             k++;
 88         }
 89     }
 90     inline int work(int n,int k,A *f){
 91         sort(f + 0,f + n,[](A a,A b){for(register int i = 0;i < 3;i++)if(a.rcd[i] ^ b.rcd[i])return a.rcd[i] < b.rcd[i];return a.p < b.p;});
 92         cdq(0,n - 1,f);//从头开始 从第一位开始 向下分治
 93         res = 0;
 94         for(register int i = 0;i < n;i++){
 95             ~f[i].p && (res += a[k][f[i].p] * f[i].res);//'~'第一次用 反演
 96             f[i].res = 0;
 97         }
 98         return res;
 99     }
100 };
101 C cdq;
102 inline int solve(){
103     int res = 0,h = 0;
104     static A f[N];//这步真的好重要
105     for(register int i = 0;i < m;i++,h = 0){
106         for(register int k = 0; k < n; k++){
107             f[h].p = -1;
108             h++;
109             for(register int j = 0,p = 0;j < m;j++){
110                 if(j == i) continue;
111                 f[h].rcd[p] = a[i][k] - a[j][k];
112                 f[h ^ 1].rcd[p] = a[j][k] - a[i][k] + (j < i);//这步第一次写的的时候用了两个if - else 有好多重复贡献 会耽误掉很多时间
113                 p++;
114             }
115             f[h].p = k;
116             h++;
117         }
118         res += cdq.work(h,i,f);
119     }
120     return res;
121 }
122 signed main(){
123     m = read();
124     n = read();
125     for(register int i = 0;i < m;i++) for(register int j = 0;j < n;j++) a[i][j] = read();
126     int res = solve();
127     for(register int i = 0;i < m;i++) for(register int j = 0;j < n;j++) a[i][j] *= -1;
128     int ans = res - solve() << 1;
129     write(ans);
130     return 0;
131 }
复制代码

 

Generally:

我就是那个考试连暴力都没时间写的蒟蒻

加油吧 小伙子

24OI Fighting!

 

posted @   November&&Rain  阅读(159)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示