DAY4-补题

消灭Coduck暴政,可达鸭属于人民!

我来可达鸭只有三件事:爆零,爆零,TMD还是爆零

本文部分梗来自于

我们可是尊贵的oier。

可达鸭机子跑真快(

虽然1e12,但莫名其妙的Juding。

很不好的体验,谴责.jpg

补题绝对是一天中最快乐的时候,这个时候敲游记往往是效率最高的。

宣传游记:https://www.luogu.com.cn/article/pjubwmlz

萌新妹子,刚学OI,请问可持久化非确定状态AC自动分块维护线段平衡仙人掌优化最小费用最大流预处理混合图上莫比乌斯反演莫队带花舞蹈链并查集树状数组套主席树预处理动态DP分治FFT求多项式逆元对数函数的指数函数用可持久化并查集合并最小费用循环流上插头DP怎么写?

说在前面

今天rp满格啦!

估分:80 + 80 + 0 + 20 = 180

实际:100 + 70 + 0 + 30 = 200

我饭有了。

%你赛糖丸了,手搓T1。T1是很简单的暴力,很神奇的是大家都是 O(1) 跑了个式子过掉了,我码了 O(n) 居然没有爆掉。很罕见的没有推式子的一天(其实想到 O(1) 但觉得不太真就没敲(

T2发现一开始敲的和正解差了几句话(其实就是写一半跑去暴力了),有大佬敲了正解然后没加 scanfprintf 挂了30pts,而我回去敲了个读优之后怎么只有10pts了(怒怒怒

T3一眼不会,果断舍弃去搞T4。 说实话后面看他们暴力的代码还挺简单的,就是4层循环,居然有20pts是没想到的。果然每日一屑。

T4立马敲了个逆序对出来,得益于前几天水题的时候随机到的逆序对的正解,敲了个30pts的归并就很好,比直接暴力可是多了10pts的呢。

总结:今日手感不错,但还是屑(指好像被单调队列了

NOIP斗十千,毒瘤题目值万钱。

停课刷题不能使,拔键四顾心茫然。

欲A T3心塞穿,将考Day2WA满山。

闲来A题Luogu上,忽复联赛梦机边。

省一难!省一难!多大佬,我何在?

AK 虐场未有时,手打暴力祭沧海。

T1-三个(three)

为了水点字数扔一下题目吧:

培养 A,B,C 三种微生物,这三种微生物每一秒都会繁殖出新的微生物,具体规则为:

A 类微生物每一秒会繁殖出 1A 类微生物,1B 类微生物,1C 类微生物。B 类微生物每一秒会繁殖出 2A 类微生物,2C 类微生物。C 类微生物每一秒会繁殖出 1A 类微生物,1B 类微生物。

假设所有的微生物都不会死亡,一开始培养皿中有 A,B,C 三种微生物各 1 个,现在问你 n 秒后 A,B,C 三种微生物分别有奇数个还是偶数个。

赛时A掉(我最棒)

瞪眼法即可获得思路。

模拟可以卡时限过掉(但很强的是正解也为模拟,不过没有我好看),唐完了。

赛后看大佬推了个数学式子,定睛一看是和我的模拟同根同源的思路。果然还是太菜了以至于没打出 O(1) 的解法。

今日收获:先打个表找找规律。

很好跟机房大佬学会放图片了(

扔一下赛时正解:

// 应该会A掉的吧……
// 应该会的吧…… 
// I Believe unsigned long long.
// I love unsigned long long 喵. 
#include <iostream>
using namespace std;
int main() {
    unsigned long long n;
	scanf("%lld", &n);
	unsigned long long x = 1, y = 1, z = 1; // 记录三种微生物的数量 
	unsigned long long xx = 1, yy = 1, zz = 1; // 增加的数量 
	for(int i = 1; i <= n; i ++) {
		xx = x + 2 * y + z;
		yy = x + z;
		zz = x + 2 * y; 
		x += xx;
		y += yy;
		z += zz;
	}
//	cout << x << " " << y << " " << z;
	if(x % 2 != 0) cout << "odd\n";
	else cout << "even\n"; 
	if(y % 2 != 0) cout << "odd\n";
	else cout << "even\n"; 
	if(z % 2 != 0) cout << "odd\n";
	else cout << "even\n"; 
	return 0;
}

T2-合体(fit)

很好想的一个暴力。

但被题目带歪。

这就不得不放出一个梗图了。

S20外环隧道方向

考虑到数字大小不大。我们可以记录一个答案数组 ansi 表示为查询 i 的答案。也就是 ansi 表示最多可以产生多少个 i。直接统计每个数字的数量cnti,进行递推。

ansi=ansi12+cnti

其中,cnti 表示数字 i 出现的次数,是桶的思想(最近桶的题目还挺多的。

赛时暴力70pts:

#include <bits/stdc++.h>
#include <queue>
using namespace std;
const int MAXN = 1e7;
int a[MAXN], b[MAXN];
int n, m;
int c[MAXN], d[MAXN];
// priority_queue<long long,vector<long long>,greater<long long> >q;
int main() {
//	freopen("fit.in","r",stdin);
//	freopen("fit.out","w",stdout);
	cin >> n >> m;
	for(int i = 1; i <= n; i ++) {
		cin >> a[i];
		d[a[i]] ++;
		c[a[i]] ++;
 	} 
 	int sum = 0, cnt = 0;
 	// cout << endl;
	for(int i = 1; i <= m; i ++) {
	//	b[i] += d[i];
		for(int j = 1; j <= i; j ++) {
			sum = d[j] / 2;
			d[j + 1] += sum;
			// cout << i << ":" << sum << endl;
		}
	//	cout << endl;
		b[i] += d[i];
		for(int i = 1; i <= m; i ++) d[i] = c[i];
	}
//	cout << endl;
	int q;
	cin >> q;
	while(q --) {
		int x;
		cin >> x;
		cout << b[x] << endl;
	}
//	fclose(stdin);
//	fclose(stdout);
	return 0;
}

正解:

#include <bits/stdc++.h>
#include <queue>
using namespace std;
const int MAXN = 1e7;
int n, m;
int b[MAXN], d[MAXN];
int main() {
    scanf("%d%d", &n, &m);
	for(int i = 1; i <= n; i ++) {
		int x;
		scanf("%d", &x);
		b[x] ++;
 	} 
 	int sum = 0, cnt = 0;
	for(int i = 1; i <= m; i ++) {
	    d[i] = d[i - 1] / 2 + b[i];
	}
	int q;
	scanf("%d",&q);
	while(q --) {
		int x;
		scanf("%d",&x);
		printf("%d\n",d[x]);
	}
	return 0;
}

T3-矩阵(matrix)

骗分过样例,暴力出奇迹

非常好的一道题。只要我不会就是好题。

加边!加边!加边!

我们来理一下这道题。

现在给你一个 nm 列的矩阵,矩阵上每个格子有一个整数,其中第 i 行第 j 列对应的格子上的整数为 gi,j。定义该矩阵的一个子矩阵的快乐值为该子矩阵上的所有数字的异或和。一组数字 a1,a2,...,an 的异或和为 a1 xor a2 xor ... xor ana (其中 xor 表示按位异或运算)

现在问你,该矩阵的所有子矩阵的快乐值之和为多少?

如果像我一样蒟蒻请看下面的异或解释:

这道题一看就没有思路能发现是一道前缀和的好题。

为什么呢?因为他善啊。

好的大家的补题报告都这么抽象我就放心了。

因为这个数组肯定有很多数据是被重复计算的,如果我们能记录下这些重复计算的值是不是就能非常迅速的计算出答案了?

当然这是我这个屑的思路(考后1min想出的就很唐

哎不是大家怎么都20就我保龄。

正解肯定不会这么简单。我们来思考一下。

代码的优劣一般分为两种,一种是时间复杂度,一种是空间复杂度。

很明显我们的时间复杂度很优,空间可是到了惊人的 1062

既然是空间的问题,那何不从空间下手呢?

在dp中我们学过滚动数组优化,那前缀和为什么不可以呢?于是这道题的正解便出来了——

不好意思,出不来。

空间是处理完了,但回头一想,时间好像到了 O(n4)

也就是可达鸭机子跑得快能搞。

那我们继续优化时间。

二进制非常快,所以考虑二进制。

对于某个区间,按位异或之后的值的某一位,是否为1。只需要考虑这个区间内的这一位的1的个数是奇数个还是偶数个。

也可以考虑 xo 数组 ans += (xo[r] ^ xo[l - 1]); ,按位考虑,某一位如果想被累计到答案里,那么xo[r]的这一位和 xo[l-1] 的这一位不相同。

所以可以考虑把 xo 拆位,如果当前这一位是 1,那么就考虑它前面这一位是 0 的情况有多少个,反之同理。

所以正解便出来了……

锐评:不如瞎搞,不如珂朵莉。

//子矩阵异或,二维前缀和做法求二维异或和 O(n^4)
//二维压一维
/*
枚举起始行i和终止行j
1 2 3 4 5 6
1 2 3 4 5 6
1 2 3 4 5 6
1 2 3 4 5 6
x[k]=a[i][k]~a[j][k] -->一维前缀异或和


0 按位异或一个数的结果是1,代表这个数里1的个数为奇数;结果为0,为偶数
*/
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 305;
int n, m, a[N][N], x[N], xo[N];
int main(){
    cin >> n >> m;
    for(int i = 1; i <= n; i ++){
        for(int j = 1; j <= m; j ++){
            cin >> a[i][j];
        }
    }
    ll ans = 0;
    for(int i = 1; i <= n; i ++){ //  枚举起始行
        memset(x, 0, sizeof x);
        for(int j = i; j <= n; j ++){ // 枚举终止行
            for(int k = 1; k <= m; k ++){
                x[k] ^= a[j][k];
                xo[k] = xo[k - 1] ^ x[k];
            }
            for(int p = 0; p < 10; p ++){
                // 按位枚举,拆位,如10101=10000+00100+00001,选带1的,结果最大
                int cnt0 = 1; // 记录第p位为0的个数
                int cnt1 = 0; // 记录第p位为1的个数
                for(int k = 1; k <= m; k ++){
                    if(xo[k] & (1 << p)){ // 如果当前是1
                        ans += 1ll * (1 << p) * cnt0;
                        // 异或取前面“有意义的”的0的个数,1^0=1
                        cnt1 ++;
                    }
                    else{
                        ans += 1ll * (1 << p) * cnt1;
                        // 异或取前面“有意义的”的1的个数,1^0=1
                        cnt0 ++;
                    }
                }
            }
        }
    }
    cout << ans;
    return 0;
}

T4-数对(pair)

如果你不会逆序对

关于归并,ta死了。

锐评:不如暴力

模拟只会猜题意,贪心只能过样例。
数学上来先打表,动规一般看规律。
组合数学靠运气,计算几何瞎暴力。
图论一顿套模板, 数论只会GCD。
递归递推伤不起,搜索茫然TLE。
分治做得像枚举,暴力枚举数第一。
数据结构干瞪眼,怒刷水题找信心。

分析一下吧(拉我起来

首先很明显的不考虑 c 数组的情况可以打一个暴力,双层循环枚举逆序对,可以水灵灵的拿到20pts,而动一下脑子打一个美丽的归并可以多拿10pts。

归并!爆!

放一下考试代码:

// 简单敲一个归并排序求逆序对
// 时间复杂度应该比直接暴力快不少
// 感谢之前我闲的没事干学的归并求逆序对
// T4!爆! 
#include <iostream>
using namespace std;
const int MAXN = 1e7 + 10;
int a[MAXN], b[MAXN], c[MAXN];
int f[MAXN];
long long ans = 0;
void msort(int x,int y) {
    if(x == y) return;
    int mid = (x + y) / 2;
	int i = x, j = mid + 1, k = x;
    msort(x, mid);
	msort(mid + 1, y);
    while(i <= mid && j <= y)
    	if(c[i] <= c[j])
    		f[k ++] = c[i ++];
    	else {
    		f[k ++] = c[j ++];
			ans += mid - i + 1;
		}
    while(i <= mid) f[k ++] = c[i ++];
    while(j <= y) f[k ++] = c[j ++];
    for(int l = x; l <= y; l ++) c[l] = f[l];
} 
int main() {
//	freopen("pair.in","r",stdin);
//	freopen("pair.out","w",stdout);
	int n, m, p;
	cin >> n >> m >> p;
	int nn = -0x3f3f3f3f;
	for(int i = 1; i <= n; i ++) cin >> a[i];
	for(int i = 1; i <= m; i ++) cin >> b[i];
	for(int i = 1; i <= n; i ++) {
		for(int j = 1; j <= m; j ++) {
			nn = max(nn, (i - 1) * m + j);
			c[(i - 1) * m + j] = (a[i] + b[j]) % p;
		}
	}
	msort(1, nn);
	cout << ans;
//	fclose(stdin);
//	fclose(stdout);
	return 0;
}

当然这道题拿30已经很好了。

但正解也是不得不学的。

很明显这道题数据非常大,要高精度。而平凡的高精度一位int才能存一个数。

那我们何不压榨int让他存6个数呢?

——资本家压位高精的精髓

我们瞪眼法观察到 c 数组是可以分成 n 块,每一块有 m 个数字。 然后我们求逆序对可以块内求,然后再块与块之间求。

对于块内

观察到值域非常小,我们可以对 b 数组记录数字出现次数 num

枚举到当前数字 x,计算逆序数时,可以枚举比x大的数字的出现次数即可。也就是记录 num[b[i]+1]~num[p1] 的和。

考虑 b 后续需要变化 (+a[j]) 。所有我们记录一个数组 nixu[k]。表示为对于 b 数组所有数组都加 k之后,逆序数为多少。

对于块与块

我们可以记录之前所有数字的出现次数,当前块一定是在之前块的后面,所以直接枚举值域,统计逆序数即可。

扔一下正解:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e6 + 5, M = 1e6 + 5;
bool flag = 0; // 特殊样例全0
int n, m, p, a[M], b[M];
ll num[10], numb[M], nixu[10]; // nixu[i]表示b数组+i之后的逆序对个数
ll ans[200], cnt = 0;
void jia(ll k){
    ans[0] += k;
    int pos = 0;
    while(1){
        if(ans[pos] >= 1000000){ //压位高精,一个变量存6位,不再存1位
            ans[pos + 1] += ans[pos] / 1000000;
            ans[pos]%=1000000;
            if(++ pos > cnt){
                cnt ++;
            }
        }
        else break;
    }
}
int main(){
    cin >> n >> m >> p;
    for(int i=1; i<=n; i++) {
        cin >> a[i];
        if(a[i]!=0) flag=1;
    }
    for(int i=1; i<=m; i++){
        cin >> b[i];
        if(b[i]!=0) flag=1;
        numb[b[i]]++;
    }
    //预处理,nixu[i]存储a[i]加到b[1]~b[m]各个数上的逆序对数
    for(int j=0; j<p; j++){//a、b数组范围在0~9,打表预处理,
        memset(num, 0, sizeof num);
        for(int i=1; i<=m; i++){//枚举b
            for(int k=b[i]+1; k<p; k++){//k=b[(i+j)%p]+1;
                //保证数字比b[i]大
                nixu[j] += num[k];
            }
            num[b[i]]++;//num[(i+j)%p]++;
            //记录b[i]次数,为下次循环准备
            b[i]=(b[i]+1)%p;//注释掉
            //保证自增,代替掉模拟 a[i] + b[j] 的枚举,因为 a、b 数组元素皆 < p
        }
    }
    memset(num, 0, sizeof num);
    //ll ans = 0;
    for(int i = 1; i <= n; i ++){
        // ans += nixu[a[i]];
		//块内的逆序对数量,c[(i - 1) % m + 1]  ~ c[(i - 1) * m + m]
        jia(nixu[a[i]]);
        for(int j = 0; j < p; j ++){
            int x = (j + a[i]) % p;//某一个c的值
            for(int k = x + 1; k < p; k ++){
                // ans += 1ll * numb[j] * num[k];
                // 块与块之间的个数(nm太大只能求块与块加、间的),不做优化30
                ll x = 1ll * numb[j] * num[k];
                jia(x);
            }
        }
        for(int j = 0; j < p; j ++){
            num[(j + a[i]) % p] += numb[j];
        }
    }
    if(!flag) cout << 0;
    else{
        cout << ans[cnt];
        for(int i = cnt - 1; i >= 0; i --){
            printf("%06lld", ans[i]);
        }
    }
    return 0;
}

很好的一道题目,让我的大脑旋转。

写在最后

这几天的分数都很不错。

之前补题说的明天会更好也是实现了。

拜谢所有做题以及讲题时给我过思路的老师和同学。

我真的很屑啊QAQ。

马上最后一天集训了,我的国庆马上也要结束了。

明天会更好。

posted @   卡布叻-空白  阅读(16)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
  1. 1 恋愛サーキュレーション 花澤香菜
  2. 2 神のまにまに 初音ミク,鏡音リン,GUMI,れるりり
  3. 3 可愛くてごめん kazami_咔嚓
  4. 4 secret base ~君がくれたもの~ (10 years after Ver.) 茅野愛衣,戸松遥,早見沙織
  5. 5 妄梦 第五人格
  6. 6 青花瓷 周杰伦
妄梦 - 第五人格
00:00 / 00:00
An audio error has occurred, player will skip forward in 2 seconds.

词:第五人格/申名利/印子月

曲:第五人格/印子月

编曲:第五人格/李冠奥

和声:白荼

混音:李冠奥

制作人:印子月

月光落入水面碎成淡银色

涟漪啊吟颂悲歌

深蓝雾气飘入瞳孔游荡着

睁开眼鳞光闪烁

赐我神意再刺痛我心底

满眼哀恸溅湿夜空

无法开口辩驳先报以你炽热

何来罪过自有因果

善良被欲望捆绑成献祭者

他们为何会听信教唆

深渊卷起无边怒火吞噬我

为什么我只能在沉默中沉没

赐我神意再刺痛我心底

那消散的幻梦被葬身于漩涡

若逃不过我将溶为泡沫

无法开口辩驳先报以你炽热

何为灾祸自食因果

飘拂的水藻包裹我似琥珀

睁开眼鳞光闪烁

裙摆曼舞生于水中洄游着

涟漪啊漾灿烂水波

点击右上角即可分享
微信分享提示