首页 > 其他分享 >CSP-S模拟14 ~ CSP-S模拟17 杂题选讲

CSP-S模拟14 ~ CSP-S模拟17 杂题选讲

时间:2022-10-11 22:01:25浏览次数:53  
标签:GCC 14 int CSP pragma inline optimize 模拟 define

\(\text{Preface}\)

image

我觉得殷教说的很对。如果说强行让我写之前咕掉的总结我觉得意义不大,而且我大多数题也都忘了再写思路不一定对而且有亿点敷衍,所以我就写其中一部分吧


CSP-S模拟15

\(网格图\)

这个题我咕了一周多,后来花了整整一个上午调试+下午的一小会时间终于 \(\text{A}\) 掉

首先考虑暴力怎么打,显然是枚举这个 K*K 矩阵的右下角,然后扫一遍他的四周和内部统计答案,最后取最大值。时间复杂度大概是 \(O(N^4)\),显然超时。有 \(\text{50pts}\) 的好成绩。但是sandom就这样A了,我不理解

当然了你得先预处理一下联通块的大小和每个点的 belong

考虑如何优化这个过程。首先明确答案的组成:对于一个 K*K 矩形,他的答案可以分为 [①]『\(K \times K\) 块内 x 的个数』\(+\) [②]『完全被包含在 \(K \times K\) 块内的联通块大小』\(+\) [③]『\(K \times K\) 块四周的联通块大小』。

画个图理解:

image

那个黑框框框住的就是 K*K 矩形。

在这个图里边我按照刚才的标号给他重新标一下,就是这个样子:

image

在同一行中,发现当矩阵右移时并不需要整个的扫一遍,相反可以继承上一个状态的一些答案。因为③的答案可以直接两个 \(O(n)\) 的 \(\text{for}\)循环扫出来,所以考虑维护①和②的答案。

整三个答案变量分别算三个部分的答案。我给他们命名的分别是 xans isdans outans

isdans 指的就是 inside ans

具体地,先弄一个 used 数组标识一下当前这个联通块是否已经贡献过答案了,避免重复贡献。再整一个 isd 数组(inside)标识这个联通块是否被完全包含在 K*K 矩形中。

对于每一行,先爆扫第一个矩形,记录一下答案,然后开始移动。

在移动之前首先把左边那一列对块内部的贡献删去,显然的当左边这一列被删的时候出现了 . 就说明这个 . 所属的联通块不完全被包含在块内了。更新相关信息。

然后移动矩形,将 used 清空(因为重新统计即可,不用费劲继承上一个),先扫矩形的四周,记录谁已经贡献过了。

最后再扫新加进来的这一列。因为刚才已经记录了谁被用过了,如果说当前新加进来的这一列中有个 .usedfalse,这说明他是完全被包含在块内的了。记得更新他的 isd 数组。

如此移动即可。细节还行,不是特别多,调试时间这么长的原因是之前的思路太麻烦了,而且后来听新的思路又理解错了一次。。

不过代码还是自己写的,没有贺。

建议也别贺我的,我估计看了我写的代码得吐,不删注释有将近五百行(

Code
#include <iostream>
#include <bitset>
#define GMY (520&1314)
#define FBI_OPENTHEDOOR(x, y) freopen(#x ".in", "r", stdin), freopen(#y ".out", "w", stdout);
#define re register int
#define char_phi signed
#define DMARK cerr << "###"
#define _ ' '
#define Endl cout << '\n'
#define Dl cerr << '\n'
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
#define N 505
using namespace std;
inline void Fastio_setup(){ios::sync_with_stdio(false); cin.tie(NULL), cout.tie(NULL);}

/*
	找到问题了,是used[]的锅,检查在往右扫的时候used数组的变化是否正确,应该是这里挂了
	锐评今日多校:一个题都不会改(
	我又来改这个题了(
	两百多行烂码
	最终A掉已经接近五百行了。。。
*/

int n, K, L, R, kuainum, final_ans, isdans, outans, xans;
int belong[N*N], sz[N*N];
bitset<N*N> used, isd;
char s[N][N];

struct Poi{int x, y;};

struct Poi q[N*N*2];

inline void bfs(int x, int y, int ID){
	L = 0, R = -1;
	q[++ R] = (Poi){x, y}; belong[(x-1)*n + y] = ID; sz[ID] = 1; 
	struct Poi fw;
	while (L <= R){
		fw = q[L ++], x = fw.x, y = fw.y;
		// 往左拓展
		if (s[x][y-1] == '.' and belong[(x-1)*n + (y-1)] == 0 and y > 1)
			q[++ R] = (Poi){x, y-1}, belong[(x-1)*n + (y-1)] = ID, sz[ID] ++;;
		// 往右
		if (s[x][y+1] == '.' and belong[(x-1)*n + (y+1)] == 0 and y < n)
			q[++ R] = (Poi){x, y+1}, belong[(x-1)*n + (y+1)] = ID, sz[ID] ++;
		// 往上
		if (s[x-1][y] == '.' and belong[(x-2)*n + y] == 0 and x > 1)
			q[++ R] = (Poi){x-1, y}, belong[(x-2)*n + y] = ID, sz[ID] ++;
		// 往下
		if (s[x+1][y] == '.' and belong[x*n + y] == 0 and x < n)
			q[++ R] = (Poi){x+1, y}, belong[x*n + y] = ID, sz[ID] ++;
	}
}

/*
5 2
..xxx
xx.xx
x.xxx
x...x
xxxx.

md,继续hack
嗯?!还是对的?!
我找正确代码对拍灞
我囸,大样例都过了
好吧我自己造数据
5 2
..xxx
x..xx
.xx..
..x.x
xxxx.

5 2
xxxx.
x.x..
.....
xxx.x
x..xx

5 2
.x..x
xx.xx
.xxxx
xxxxx
xxx.x

5 2
xx.xx
.xxxx
.xx.x
.x.xx
.xx..

好了这一组有锅:
5 2
..x..
xxxxx
xxxxx
xx..x
x.x.x

答案是方格右下角处于(3, 4)时 (3, 3)

还过不了
又来一组数据
5 2
x..x.
.xxxx
.x.xx
x.x..
.x.xx
答案在(3, 3)
*/

/*inline void Debug(){
	cerr << "used: ";
	for (re i = 1 ; i <= 6 ; ++ i)
		cerr << used[i] << _;
	Dl;
	cerr << "still: ";
	for (re i = 1 ; i <= 6 ; ++ i)
		cerr << still[i] << _;
	Dl;
	cerr << "dld: ";
	for (re i = 1 ; i <= 6 ; ++ i)
		cerr << dld[i] << _;
	Dl;
}*/
inline int id(int x, int y){return (x-1)*n+y;}

inline void work(){
	cin >> n >> K;
	for (re i = 1 ; i <= n ; ++ i)
		cin >> s[i]+1;
	
	/*cerr << n << _ << K << '\n';// 实在没办法了,这题拖了一周多了(套WA的数据点)
	for (re i = 1 ; i <= n ; ++ i)
		cerr << s[i]+1 << '\n';*/
	
	for (re i = 1 ; i <= n ; ++ i){
		for (re j = 1 ; j <= n ; ++ j){
			if (s[i][j] == '.' and belong[(i-1)*n+j] == 0)
				kuainum ++, bfs(i, j, kuainum);
		}
	}
	
	/*for (re i = 1 ; i <= n ; ++ i){// 无锅
		for (re j = 1 ; j <= n ; ++ j)
			cerr << belong[(i-1)*n+j] << _;
		Dl;
	}
	cerr << kuainum << '\n';
	for (re i = 1 ; i <= kuainum ; ++ i)
		cerr << sz[i] << _;
	Dl; Dl;*/
	
	// 核心思想又出错了!我囸!
	// 完全在块内 + 四周 + 块内x的个数
	// 好家伙样例WA了
	
	for (re x = K, i, ii, j, jj ; x <= n ; ++ x){
		// 先爆扫第一个
		outans = isdans = xans = 0;
		used = 0; isd = 0;
		// 先扫边界并打上标记
		// 右边
		j = K+1;
		for (i = x-K+1 ; i <= x ; ++ i)
			if (s[i][j] == '.' and used[belong[id(i, j)]] == false)
				outans += sz[belong[id(i, j)]], used[belong[id(i, j)]] = true;
		// 上边和下边
		i = x-K, ii = x+1;
		for (j = 1 ; j <= K ; ++ j){
			if (s[i][j] == '.' and used[belong[id(i, j)]] == false)
				outans += sz[belong[id(i, j)]], used[belong[id(i, j)]] = true;
			if (s[ii][j] == '.' and used[belong[id(ii, j)]] == false)
				outans += sz[belong[id(ii, j)]], used[belong[id(ii, j)]] = true;
		}
		// 扫块内
		for (i = x-K+1 ; i <= x ; ++ i){
			for (j = 1 ; j <= K ; ++ j){
				if (s[i][j] == '.' and used[belong[id(i, j)]] == false and isd[belong[id(i, j)]] == false)// 完全被包含在块内
					isdans += sz[belong[id(i, j)]], isd[belong[id(i, j)]] = true;
				else if (s[i][j] != '.')
					xans ++;
			}
		}
		
		/*if (x == 3){
			cerr << isdans << _ << outans << _ << xans << '\n';
			cerr << "used: ";
			for (re i = 1 ; i <= 5 ; ++ i)
				cerr << used[i] << _;
			Dl;
			cerr << "isd: ";
			for (re i = 1 ; i <= 5 ; ++ i)
				cerr << isd[i] << _;
			Dl;
		}*/
		
		final_ans = MAX(final_ans, isdans+outans+xans);
		
		// 然后先把左边一列的isdans和xans删去
		j = 1;
		for (i = x-K+1 ; i <= x ; ++ i){
			if (s[i][j] == '.' and used[belong[id(i, j)]] == false and isd[belong[id(i, j)]] == true)
				isdans -= sz[belong[id(i, j)]], isd[belong[id(i, j)]] = false;
			else if (s[i][j] != '.')
				xans --;
		}
		
		// 往右移
		for (re y = K+1 ; y <= n ; ++ y){
			used = 0; outans = 0;
			// 先把外边一层标记上
			// 左边和右边
			j = y-K, jj = y+1;
			for (i = x-K+1 ; i <= x ; ++ i){
				if (s[i][j] == '.' and used[belong[id(i, j)]] == false)
					outans += sz[belong[id(i, j)]], used[belong[id(i, j)]] = true;
				if (s[i][jj] == '.' and used[belong[id(i, jj)]] == false)
					outans += sz[belong[id(i, jj)]], used[belong[id(i, jj)]] = true;
			}
			// 上边和下边
			i = x-K, ii = x+1;
			for (j = y-K+1 ; j <= y ; ++ j){
				if (s[i][j] == '.' and used[belong[id(i, j)]] == false)
					outans += sz[belong[id(i, j)]], used[belong[id(i, j)]] = true;
				if (s[ii][j] == '.' and used[belong[id(ii, j)]] == false)
					outans += sz[belong[id(ii, j)]], used[belong[id(ii, j)]] = true;
			}
			// 搜新加入的列
			j = y;
			for (i = x-K+1 ; i <= x ; ++ i){
				if (s[i][j] == '.' and used[belong[id(i, j)]] == false and isd[belong[id(i, j)]] == false)
					isdans += sz[belong[id(i, j)]], isd[belong[id(i, j)]] = true;
				else if (s[i][j] != '.')
					xans ++;
			}
			
			final_ans = MAX(final_ans, isdans+outans+xans);
			
			// 把左边那一列的贡献给删掉
			j = y-K+1;
			for (i = x-K+1 ; i <= x ; ++ i){
				if (s[i][j] == '.' and used[belong[id(i, j)]] == false and isd[belong[id(i, j)]] == true)
					isdans -= sz[belong[id(i, j)]], isd[belong[id(i, j)]] = false;
				else if (s[i][j] != '.')
					xans --;
			}
		}
	}
	
	cout << final_ans << '\n';
	
	// 仅仅需要把移动一格之后被删的边的点给加上,新加的边的点给减去,然后扫一遍四个框统计外部答案即可。我。。。
	/*for (re x = K, i, j ; x <= n ; ++ x){// 右下角在第几行
		// 直接暴扫第一个
		isdans = 0; tmpans = 0; used = 0;
		for (i = x-K+1 ; i <= x ; ++ i){
			for (j = 1 ; j <= K ; ++ j){// 当前块内的
				if (s[i][j] == '.')
					isdans --;// insideans记录里边有几个是'.',之后减去这个就好了
				if (s[i][j] == '.' and used[belong[id(i, j)]] == false)
					tmpans += sz[belong[id(i, j)]], used[belong[id(i, j)]] = true;
			}
		}
		// 扫右边
		j = K + 1;
		for (i = x-K+1 ; i <= x ; ++ i)
			if (s[i][j] == '.' and used[belong[id(i, j)]] == false)
				tmpans += sz[belong[id(i, j)]], used[belong[id(i, j)]] = true;
		
		// 扫上边
		i = x - K;
		for (j = 1 ; j <= K ; ++ j)
			if (s[i][j] == '.' and used[belong[id(i, j)]] == false)
				tmpans += sz[belong[id(i, j)]], used[belong[id(i, j)]] = true;
		
		// 扫下边
		i = x + 1;
		for (j = 1 ; j <= K ; ++ j)
			if (s[i][j] == '.' and used[belong[id(i, j)]] == false)
				tmpans += sz[belong[id(i, j)]], used[belong[id(i, j)]] = true;
		
		final_ans = MAX(final_ans, tmpans+isdans+K*K);
		
		if (x == 3)
			cerr << "begin: " << isdans << _ << tmpans << '\n';
		
		// 块向右移动
		for (re y = K+1 ; y <= n ; ++ y){
			// 扫左边变为相邻的边
			used = 0; tmpans = 0;
			j = y - K;
			for (i = x-K+1 ; i <= x ; ++ i)
				if (s[i][j] == '.')
					isdans ++;
			
			// 扫右边新加入的边
			j = y;
			for (i = x-K+1 ; i <= x ; ++ i)
				if (s[i][j] == '.')
					isdans --;
			
			// 暴力扫块的四个边
			
			
			// 左边
			j = y - K;
			for (i = x-K+1 ; i <= x ; ++ i)
				if (s[i][j] == '.' and used[belong[id(i, j)]] == false)
					tmpans += sz[belong[id(i, j)]], used[belong[id(i, j)]] = true;
			
			// 右边
			j = y + 1;
			for (i = x-K+1 ; i <= x ; ++ i)
				if (s[i][j] == '.' and used[belong[id(i, j)]] == false)
					tmpans += sz[belong[id(i, j)]], used[belong[id(i, j)]] = true;
			
			// 上边
			i = x - K;
			for (j = y-K+1 ; j <= y ; ++ j)
				if (s[i][j] == '.' and used[belong[id(i, j)]] == false)
					tmpans += sz[belong[id(i, j)]], used[belong[id(i, j)]] = true;
			
			// 下边
			i = x + 1;
			for (j = y-K+1 ; j <= y ; ++ j)
				if (s[i][j] == '.' and used[belong[id(i, j)]] == false)
					tmpans += sz[belong[id(i, j)]], used[belong[id(i, j)]] = true;
			
			if (x == 3)
				cerr << "y = " << y << " 時,ループ内: " << isdans << _ << tmpans << '\n';
			
			final_ans = MAX(final_ans, tmpans+isdans+K*K);
		}
		cerr << x << _ << "真ん中のfinal_ans: " << final_ans << '\n';
	}*/
	
	/*for (re x = K ; x <= n ; ++ x){
		used = 0;
		tmpans = K*K;// 先暴力扫第一个
		for (re i = x-K ; i <= x+1 ; ++ i){
			for (re j = 0 ; j <= K+1 ; ++ j){
				if ((i == x-K and j == 0) or (i == x-K and j == K+1) or (i == x+1 and j == 0) or (i == x+1 and j == K+1)) 
					continue;
				ider = (i-1)*n + j;
				/*if (x == 3)
					cerr << "遍历到了点(" << i << ", " << j << "):" << '\n' << "before-tmpans: " << tmpans << '\n';*//*
				if (s[i][j] == '.' and used[belong[ider]] == false)
					tmpans += sz[belong[ider]], used[belong[ider]] = true;
				/*if (x == 3)
					cerr << "Mid-tmpans: " << tmpans << '\n';*//*
				if (s[i][j] == '.' and i <= x and j <= K and i >= x-K+1 and j >= 1)
					tmpans --;
				/*if (x == 3)
					cerr << "after-tmpans: " << tmpans << '\n';*//*
				// Dl; Dl;
			}
		}
		
		/*if (x == 3)
			Debug();*/
		
		/*if (x == 3)
			cerr << tmpans << '\n';*/// 没问题
		/*
		final_ans = MAX(final_ans, tmpans);
		
		// cerr << "x: " << x << '\n';
		// cerr << "大before-ans: " << tmpans << _ << final_ans << '\n';
		
		for (re y = K+1, i, j ; y <= n ; ++ y){// 类似莫队地向右移
			// 我觉得我重构一遍比较好,细节太多了
			// 我听sandom说了一嘴,我人傻了
			// 对阿,我直接扫一遍四个边不就行了,复杂度是对的
			// 我囸!
			
			// 仅仅需要把移动一格之后被删的边的点给加上,新加的边的点给减去,然后扫一遍四个框统计外部答案即可
			
			/*dld = 0;
			
			// 最左边被删除的边
			
			j = y - K - 1;
			for (i = x-K+1 ; i <= x ; ++ i)
				if (s[i][j] == '.' and dld[belong[id(i, j)]] == false and used[belong[id(i, j)]] == false)
					dld[belong[id(i, j)]] = true, tmpans -= sz[belong[id(i, j)]];
			
			// 稍微靠左的边
			// 首先他变成了自由边之后原来扣掉的要加上
			j = y-K;
			for (i = x-K+1 ; i <= x ; ++ i)
				if (s[i][j] == '.')
					tmpans ++;
			*/
			
			/*still = 0; dld = 0;
			
			j = y - K;// 稍微靠左的边
			for (re i = x-K+1 ; i <= x ; ++ i){
				if (s[i][j] == '.')
					tmpans ++, still[belong[(i-1)*n + j]] = true;
			}
			
			if (x == 3)
				cerr << "稍微靠左的边 " << tmpans << '\n', Debug();
			
			// 新的左上角
			if (s[x-K][y-K+1] == '.' and 
			
			j = y - K - 1;// 被淘汰的左边
			for (re i = x-K+1 ; i <= x ; ++ i)
				if (s[i][j] == '.' and still[belong[(i-1)*n + j]] == false)
					tmpans -= sz[belong[(i-1)*n + j]], used[belong[(i-1)*n + j]] = false, dld[belong[(i-1)*n + j]] = true;
			
			if (x == 3)
				cerr << "被淘汰的左边 " << tmpans << '\n', Debug();
			
			// 左上角
			if (s[x-K][y-K] == '.' and still[belong[(x-K-1)*n + y-K]] == false and dld[belong[(x-K-1)*n + y-K]] == false)
				tmpans -= sz[belong[(x-K-1)*n + y-K]], used[belong[(x-K-1)*n + y-K]] = false, dld[belong[(x-K-1)*n + y-K]] = true;
			
			if (x == 3)
				cerr << "左上角 " << tmpans << '\n', Debug();
			
			// 左下角
			if (s[x+1][y-K] == '.' and still[belong[x*n + y-K]] == false and dld[belong[x*n + y-K]] == false)
				tmpans -= sz[belong[x*n + y-K]], used[belong[x*n + y-K]] = false, dld[belong[x*n + y-K]] = true;
			
			if (x == 3)
				cerr << "左下角 " << tmpans << '\n', Debug();
			
			// 处理好左边了
			// 稍微靠右的边
			j = y;
			for (re i = x-K+1 ; i <= x ; ++ i)
				if (s[i][j] == '.')
					tmpans --, used[belong[(i-1)*n+j]] = true;
			
			if (x == 3)
				cerr << "稍微靠右的边 " << tmpans << '\n', Debug();
			
			// 右上角
			if (s[x-K][y] == '.' and used[belong[(x-K-1)*n + y]] == false)
				tmpans += sz[belong[(x-K-1)*n + y]], used[belong[(x-K-1)*n + y]] = true;
			
			if (x == 3)
				cerr << "右上角 " << tmpans << '\n', Debug();
			
			// 右下角
			if (s[x+1][y] == '.' and used[belong[x*n + y]] == false)
				tmpans += sz[belong[x*n + y]], used[belong[x*n + y]] = true;
			
			if (x == 3)
				cerr << "右下角 " << tmpans << '\n', Debug();
			
			// 最靠右的边
			j = y+1;
			for (re i = x-K+1 ; i <= x ; ++ i)
				if (s[i][j] == '.' and used[belong[(i-1)*n + j]] == false)
					tmpans += sz[belong[(i-1)*n + j]], used[belong[(i-1)*n + j]] = true;
			
			if (x == 3)
				cerr << "最靠右的边 " << tmpans << '\n', Debug();
			
			// 终于都处理完了
			
			final_ans = MAX(final_ans, tmpans);
			Dl; Dl; Dl; Dl; Dl;*//*
		}
		
		// cerr << "大after-ans: " << tmpans << _ << final_ans << '\n';
		// Dl; Dl; Dl; Dl; Dl;
	}*/
	// cerr << '\n' << "Final: " << final_ans << '\n';
}
// #define IXINGMY
char_phi main(){
	#ifdef IXINGMY
		FBI_OPENTHEDOOR(a, a);
	#endif
	Fastio_setup();
	work();
	return GMY;
}

国庆のsurprise

\(\text{Su_Zipei is always here}\)

大力分块差分即可。没啥好讲的,但是在出现次数上开数组,挺妙的。

然后查询出现次数的时候直接边更新边查,也比较妙(也可能是因为我比较菜所以觉得妙(

有点卡常,加个火车头

Code
%:pragma GCC target("avx")
%:pragma GCC optimize(3)
%:pragma GCC optimize("Ofast")
%:pragma GCC optimize("inline")
%:pragma GCC optimize("-fgcse")
%:pragma GCC optimize("-fgcse-lm")
%:pragma GCC optimize("-fipa-sra")
%:pragma GCC optimize("-ftree-pre")
%:pragma GCC optimize("-ftree-vrp")
%:pragma GCC optimize("-fpeephole2")
%:pragma GCC optimize("-ffast-math")
%:pragma GCC optimize("-fsched-spec")
%:pragma GCC optimize("unroll-loops")
%:pragma GCC optimize("-falign-jumps")
%:pragma GCC optimize("-falign-loops")
%:pragma GCC optimize("-falign-labels")
%:pragma GCC optimize("-fdevirtualize")
%:pragma GCC optimize("-fcaller-saves")
%:pragma GCC optimize("-fcrossjumping")
%:pragma GCC optimize("-fthread-jumps")
%:pragma GCC optimize("-funroll-loops")
%:pragma GCC optimize("-fwhole-program")
%:pragma GCC optimize("-freorder-blocks")
%:pragma GCC optimize("-fschedule-insns")
%:pragma GCC optimize("inline-functions")
%:pragma GCC optimize("-ftree-tail-merge")
%:pragma GCC optimize("-fschedule-insns2")
%:pragma GCC optimize("-fstrict-aliasing")
%:pragma GCC optimize("-fstrict-overflow")
%:pragma GCC optimize("-falign-functions")
%:pragma GCC optimize("-fcse-skip-blocks")
%:pragma GCC optimize("-fcse-follow-jumps")
%:pragma GCC optimize("-fsched-interblock")
%:pragma GCC optimize("-fpartial-inlining")
%:pragma GCC optimize("no-stack-protector")
%:pragma GCC optimize("-freorder-functions")
%:pragma GCC optimize("-findirect-inlining")
%:pragma GCC optimize("-fhoist-adjacent-loads")
%:pragma GCC optimize("-frerun-cse-after-loop")
%:pragma GCC optimize("inline-small-functions")
%:pragma GCC optimize("-finline-small-functions")
%:pragma GCC optimize("-ftree-switch-conversion")
%:pragma GCC optimize("-foptimize-sibling-calls")
%:pragma GCC optimize("-fexpensive-optimizations")
%:pragma GCC optimize("-funsafe-loop-optimizations")
%:pragma GCC optimize("inline-functions-called-once")
%:pragma GCC optimize("-fdelete-null-pointer-checks")
#include <iostream>
#include <cmath>
#define GMY (520&1314)
#define FBI_OPENTHEDOOR(x, y) freopen(#x ".in", "r", stdin), freopen(#y ".out", "w", stdout);
#define re register int
#define char_phi signed
#define DMARK cerr << "###"
#define _ ' '
#define Endl cout << '\n'
#define Dl cerr << '\n'
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
#define N 100005
using namespace std;
inline void Fastio_setup(){ios::sync_with_stdio(false); cin.tie(NULL), cout.tie(NULL);}

/*
	马八∑
	我囸,被卡常了
	大样例跑了三秒多
*/

int n, Q, Opt, blocklen=3300, blocknum, final_ans;
int w[N], t[N];
int cnt[33][N];
int a[33][33][N];

inline void Block_Divide(){
	blocknum = (n+blocklen-1) / blocklen;
	// sleep(10);
	// cerr << blocklen << _ << blocknum << '\n';
	for (re i = 1 ; i <= blocknum ; ++ i){
		int Lf = (i-1) * blocklen + 1, Rt = MIN(n, blocklen*i);
		cerr << Lf << _ << Rt << '\n';
		for (re j = 1 ; j <= n ; ++ j)
			cnt[i][j] = cnt[i-1][j];
		for (re j = Lf ; j <= Rt ; ++ j)// 差分
			cnt[i][w[j]] ++;
	}
	
	for (re l = 1 ; l <= blocknum ; ++ l){
		for (re r = l ; r <= blocknum ; ++ r){
			for (re col = 1 ; col <= n ; ++ col)
				a[l][r][cnt[r][col]-cnt[l-1][col]] ++;
			for (re col = 1 ; col <= n ; ++ col)
				a[l][r][col] += a[l][r][col-1];
		}
	}
}
inline int Query(int l, int r, int K){
	int res = 0, bl = (l+blocklen-1) / blocklen, br = (r+blocklen-1) / blocklen;
	if (br - bl <= 1){
		for (re i = l ; i <= r ; ++ i)
			t[w[i]] ++, res += (t[w[i]] == K);// 好方法,直接在加入桶的时候查询,不用再扫整个值域了
		for (re i = l ; i <= r ; ++ i)
			t[w[i]] = 0;
	}
	else {
		res = a[bl+1][br-1][n] - a[bl+1][br-1][K-1];// 差分
		int Rtl = bl * blocklen, Lfr = (br-1) * blocklen + 1;
		for (re i = l ; i <= Rtl ; ++ i)
			t[w[i]] ++, res += ((t[w[i]] + cnt[br-1][w[i]] - cnt[bl][w[i]]) == K);
		for (re i = Lfr ; i <= r ; ++ i)
			t[w[i]] ++, res += ((t[w[i]] + cnt[br-1][w[i]] - cnt[bl][w[i]]) == K);
		for (re i = l ; i <= Rtl ; ++ i)
			t[w[i]] = 0;
		for (re i = Lfr ; i <= r ; ++ i)
			t[w[i]] = 0;
	}
	return res;
}

inline void work(){
	cin >> n >> Q >> Opt;
	for (re i = 1 ; i <= n ; ++ i)
		cin >> w[i];
	
	Block_Divide();
	
	int l1, r1, K1, l, r, K;
	while (Q --){
		cin >> l1 >> r1 >> K1;
		l = MIN((l1 + final_ans*Opt - 1) % n + 1, (r1 + final_ans*Opt - 1) % n + 1);
		r = MAX((l1 + final_ans*Opt - 1) % n + 1, (r1 + final_ans*Opt - 1) % n + 1);
		K = (K1 + final_ans*Opt - 1) % n + 1;
		
		if (l > r)
			swap(l, r);
		
		// cerr << l << _ << r << _ << K << '\n'; sleep(4);
		
		final_ans = Query(l, r, K);
		cout << final_ans << '\n';
	}
}
// #define IXINGMY
char_phi main(){
	#ifdef IXINGMY
		FBI_OPENTHEDOOR(suzipei, a);
	#endif
	Fastio_setup();
	work();
	return GMY;
}

CSP-S模拟16

猜道路

这个题大佬们都基本场切了,但是有一个部分分我想说一下。

考虑到他给出的这个矩阵中一部分肯定是作为原边权出现的,那么可以对于上三角矩阵枚举哪些边是作为原边权出现的,选出来跑个 \(\text{Floyd}\),跑出来再和整个矩阵比较判断是否最短路都一致,相应选择是否更新答案。

考虑到时限有 \(\text{2000ms}\),可以跑一个随机选边,就不爆搜了。数据小的时候随便跑就行。配合输出 \(-1\) 可以拿到 \(\text{56pts}\) 的好成绩。

正解就不说了。

随机化·$\text{56pts}$
#include <iostream>
#include <ctime>
#include <cstring>
#define GMY (520&1314)
#define FBI_OPENTHEDOOR(x, y) freopen(#x ".in", "r", stdin), freopen(#y ".out", "w", stdout);
#define re register int
#define char_phi signed
#define DMARK cerr << "###"
#define _ ' '
#define Endl cout << '\n'
#define Dl cerr << '\n'
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
#define N 305
using namespace std;
inline void Fastio_setup(){ios::sync_with_stdio(false); cin.tie(NULL), cout.tie(NULL);}

/*
	不会找环的屑ch_p来写T1
	差分约束desu
	in fact
	这最短路,肯定有一些边是本来就被选上了的
	那么就随机选边,跑Floyd验证
	但是极限数据下似乎只能随机十次
	不对,别说是极限数据了,29%的数据也是n<=300吧
	我囸
	打这么随机的玩意...真的能行吗...
	样例能过,笑死我了((
	震惊!大样例跑了甚至80次+!
	能拿多少分捏?大约有零分灞
*/

int n;
long long final_ans(1145141145141919810), tmpans;
long long tim;
long long a[N][N], dis[N][N];

inline void Floyd(){
	for (re i = 1 ; i <= n ; ++ i){
		for (re j = 1 ; j <= n ; ++ j){
			if (i == j)
				continue;
			for (re k = 1 ; k <= n ; ++ k){
				if (i == k or j == k)
					continue;
				if (dis[i][j] > dis[i][k]+dis[k][j])
					dis[i][j] = dis[i][k]+dis[k][j];
			}
		}
	}
}

/*
3
0 1 3
1 0 2
3 2 0
*/

inline void work(){
	cin >> n;
	for (re i = 1 ; i <= n ; ++ i)
		for (re j = 1 ; j <= n ; ++ j)
			cin >> a[i][j];
	
	while (true){
		if ((clock()-tim) * 1000 >= 1900 * CLOCKS_PER_SEC)
			break;
		tmpans = 0; memset(dis, 0x3f, sizeof(dis));
		for (re i = 1 ; i <= n ; ++ i)
			for (re j = i+1 ; j <= n ; ++ j)
				if ((rand() & 3) == 0)
					dis[i][j] = dis[j][i] = a[i][j], tmpans += a[i][j];//  cerr << i << _ << j << '\n';
		
		Floyd();
		
		/*for (re i = 1 ; i <= n ; ++ i){
			for (re j = 1 ; j <= n ; ++ j)
				cerr << dis[i][j] << _;
			Dl;
		}*/
		
		for (re i = 1 ; i <= n ; ++ i)
			for (re j = i+1 ; j <= n ; ++ j)
				if (dis[i][j] != a[i][j])
					{tmpans = 1145141145141919810; break;}
		
		final_ans = MIN(final_ans, tmpans);
		// Dl; Dl; Dl;
		// cerr << "胡萝贝" << '\n';
	}
	if (final_ans == 1145141145141919810)
		cout << -1 << '\n';
	else 
		cout << final_ans << '\n';
}
// #define IXINGMY
char_phi main(){
	tim = clock();
	srand(time(0));
	#ifdef IXINGMY
		FBI_OPENTHEDOOR(a, a);
	#endif
	Fastio_setup();
	work();
	return GMY;
}
正解
#include <iostream>
#define GMY (520&1314)
#define FBI_OPENTHEDOOR(x, y) freopen(#x ".in", "r", stdin), freopen(#y ".out", "w", stdout);
#define re register int
#define char_phi signed
#define DMARK cerr << "###"
#define _ ' '
#define Endl cout << '\n'
#define Dl cerr << '\n'
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
#define N 305
using namespace std;
inline void Fastio_setup(){ios::sync_with_stdio(false); cin.tie(NULL), cout.tie(NULL);}

/*
	
*/

int n;
long long final_ans;
char deleted[N][N];
long long a[N][N];

inline void Floyd(){
	for (re k = 1 ; k <= n ; ++ k){
		for (re i = 1 ; i <= n ; ++ i){
			if (i == k)
				continue;
			for (re j = 1 ; j <= n ; ++ j){
				if (j == i or j == k)
					continue;
				if (a[i][j] > a[i][k]+a[k][j])
					goto CANT;
				if (a[i][j] == a[i][k]+a[k][j])
					deleted[i][j] = deleted[j][i] = true;
			}
		}
	}
	return ;
	CANT:{cout << -1 << '\n'; exit(0);}
}

inline void work(){
	cin >> n;
	for (re i = 1 ; i <= n ; ++ i)
		for (re j = 1 ; j <= n ; ++ j)
			cin >> a[i][j];
	
	Floyd();
	
	for (re i = 1 ; i <= n ; ++ i)
		for (re j = i+1 ; j <= n ; ++ j)
			if (deleted[i][j] == false)
				final_ans += a[i][j];
	
	cout << final_ans << '\n';
	
}
// #define IXINGMY
char_phi main(){
	#ifdef IXINGMY
		FBI_OPENTHEDOOR(a, a);
	#endif
	Fastio_setup();
	work();
	return GMY;
}

2022NOIPA层联测3 10月5日

\(\text{C}\)

整个歪解,从低到高依次确定答案。

首先任何数异或 \(0\) 等于他本身,所以第一位答案就直接找到整个序列中字典序最小的字母,然后把这个字母所在的所有位置作为『候选的 \(k\)』扔到一个数组里。

然后确定第二位,把候选的 \(k\) 们依次异或出的位置中,选出该位置字符字典序最小的那一个,并将所有能得到这个字符的 \(k\) 们再次扔到数组里,类似的确定更高位直到『候选的 \(k\)』只剩一个或答案已经确定完毕。(应该等到『候选的 \(k\)』只剩一个了就可以 break 了?)

Code
#pragma GCC optimize(2)
#include <iostream>
#include <cmath>
#define GMY (520&1314)
#define FBI_OPENTHEDOOR(x, y) freopen(#x ".in", "r", stdin), freopen(#y ".out", "w", stdout);
#define re register int
#define char_phi signed
#define DMARK cerr << "###"
#define _ ' '
#define Endl cout << '\n'
#define Dl cerr << '\n'
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
#define NN 262155
using namespace std;
inline void Fastio_setup(){ios::sync_with_stdio(false); cin.tie(NULL), cout.tie(NULL);}

/*
	一个一个地选,选出“值得我枚举的k”,一直选直到只剩一个k。
*/

int n, mn;
char str[NN], ans[NN];
int a[NN], tmp[NN], s[NN];

/*
2
acba

3
bcbaaabb
*/

inline void work(){
	cin >> n; n = (1 << n) - 1;
	cin >> str;
	for (re i = 0 ; i <= n ; ++ i)
		s[i] = (int)str[i];
	// 第一次选是0,0^k = k,所以第一次只需要找出来整个序列里字典序最小的字母即可
	mn = 1145141919;
	for (re i = 0 ; i <= n ; ++ i)
		if (s[i] < mn)
			mn = s[i];
	for (re i = 0 ; i <= n ; ++ i)
		if (s[i] == mn)
			a[++ a[0]] = i;
	
	/*cerr << (char)mn << _ << a[0] << '\n';
	cerr << "a[i]: ";
	for (re i = 1 ; i <= a[0] ; ++ i)
		cerr << a[i] << _;
	Dl;*/
	
	for (re i = 1 ; i <= n ; ++ i){
		if (a[0] == 1)
			break;
		
		// cerr << i << '\n';
		
		mn = 1145141919; tmp[0] = 0;
		for (re j = 1 ; j <= a[0] ; ++ j)// cerr << "s[a[j]^i]: " << (char)s[a[j]^i] << '\n';
			if (s[a[j] ^ i] < mn)
				mn = s[a[j] ^ i];
		
		
		for (re j = 1 ; j <= a[0] ; ++ j)
			if (s[a[j] ^ i] == mn)
				tmp[++ tmp[0]] = a[j];
		
		// cerr << "repmn:|" << (char)mn << '\n';
		
		a[0] = tmp[0];
		for (re j = 1 ; j <= tmp[0] ; ++ j)
			a[j] = tmp[j];
		// Dl; Dl; Dl;
	}
	
	// cerr << a[0] << _ << a[1] << '\n';
	
	for (re i = 0 ; i <= n ; ++ i)
		ans[i] = str[i^a[1]];
	cout << ans << '\n';
}
// #define IXINGMY
char_phi main(){
	#ifdef IXINGMY
		FBI_OPENTHEDOOR(a, a);
	#endif
	Fastio_setup();
	work();
	return GMY;
}

标签:GCC,14,int,CSP,pragma,inline,optimize,模拟,define
From: https://www.cnblogs.com/charphi/p/16782198.html

相关文章

  • 一个有意思的分钱模拟问题
    大家好,我是吴师兄,今天来分享一个有意思的分钱模拟问题,为了帮助大家理解,采取了可视化的方式。这个问题描述是这样的:房间里有100个人,每人都有100元钱,他们在玩一个游戏。每......
  • LeetCode148. Sort List
    题意链表排序方法递归代码classSolution{public:ListNode*sortList(ListNode*head){returnsortList(head,nullptr);}ListNode*......
  • 14、Java——迷你图书管理器(对象+数组)
    ​ 目录​​⛳️项目需求​​​​⛳️覆盖知识​​​​⛳️开发思路 ​​​​⛳️开发步骤​​​​❤️1、数据初始化​​​​❤️2、BookMethod类中定义各种方法​​​​⛳️部......
  • Thread专题(14) - 自定义Annocation
    此文被笔者收录在系列文章​​​架构师必备(系列)​​中,这一章的注解是非官方的注解,属于一点扩展知识。自从java有了注解能力后,在有些时候可以考虑用注释来代替文档。@Guard......
  • AutoCAD2014 辅助设计从入门到精通
    【出版信息】书名:AutoCAD2014辅助设计从入门到精通书号:978-7-111-45385-7作者:钟日铭等开本:16开出版时间:2014.1出版社:机械工业出版社【内容简介】本书以最新的AutoCAD......
  • 514多表关系多对多关系实现和515多表关系一对一关系实现
    多对多关系实现1.如学生和课程,分析: 多对多关系实现需要借助第三张中间表。中间表至少包含两个字段,这两个字段作为第三张表的外键,分别指向两张表的主键  一对一关......
  • [2022.10.11 模拟赛] 联通块
    题意简述给定一颗树,每个点有点权\((a_i,b_i)\)。问满足\(\suma_i\lem\)的连通块的\(\sumb_i\)的最大值。\(n\le10^3,m\le10^4\)分析有一个显然的\(\mat......
  • CF1420D & gym102012 G
    CF1420D:注意到任意条线段的交集如果非空的话必定是一条线段,且这条线段的左端点一定是某条线段的左端点。很明显先将线段离散化,然后去枚举相交的线段的左端点。我们设\(......
  • “不得不知道“ 的14个获取数据的网站,解决工作中90%以上的问题!
    在日常学习、工作中,除了自家的数据库,免不了要找一些外部的数据来论证某些问题,这里给大家分享14个权威、常用的网站,以备不时之需。1.中华人民共和国统计局国家统计局2.中国......
  • 35-70K*14薪| 梅卡曼德2D/3D视觉、深度学习算法专家等岗位招聘
    公司介绍梅卡曼德机器人由清华海归团队于2016年创办,致力于推动智能机器人无所不在的存在,总部位于北京和上海,在深圳、长沙、青岛、慕尼黑、东京等地有布局。AI+3D+工业机器人......