20211014 noip76

考场

开考发现俩构造。。。

想了想 T1 发现有\(O(q\sqrt{mn}+m^{2}n^{2})\) 的 sb 做法,能获得 40pts 的好成绩
T2 画了半天图发现合法情况找一个三元环,删一个点二分图染色就行了
T3 没看懂题
T4 打了 \(n=5\) 的表,发现 \(n=10\) 搜不出来就弃了

然后开写 T2,写 checker 时发现我的做法可能会多连边,加了个差分约束,然后又保证不了两两不同,想了半天也不会,只能胡了个正确性、复杂度都不保证的做法过了样例,发现随机出的图都是无解。。。
11.30 不敢刚了,写了 T1 \(O(qm^{2}n^{2})\) 暴力

res

rk15 20+0+0+20

rk1 zwmy 50+0+50+20
rk2 陈天宇 20+30+40+20
rk4 ys 70+0+0+20
rk7 yzf 80+0+0+0
rk9 ycx 70+0+0+0

这场的题确实不简单,但考这么低纯粹是脑瘫,搞了 3h 的 T2 爆 \(0\)

想 T2 的时候没考虑全要求,结果写完发现假了
还有中间 10.30 是发现做不了 T2,没有果断放弃去写 T1,不然 T1 能拿 70pts
有个做法->写完->假了->调->爆 \(0\)。这剧本上演好多次了,也总结过好多次,为啥一点长进都没有啊。再有这种情况就强制三场开题就打暴力,然后再想正解

最后,保持心态!!!

洛希极限

关键点:能转移的两点被同一矩形包含

70pts:
对于一个点 \((i,j)\),最优决策点一定在它的上一行或上一列,且合法决策点一定是一段区间,考虑预处理出来 \(le[i,j],up[i,j]\) 表示上一行能转移到它的最左和上一列它的最上(KDT 矩形取 \(\min\)),暴力转移。
时间复杂度 \(O(q\sqrt{nm}+mn^2)\)

DP 比较好优化,单调队列即可。方案数可以开桶,队列变化时同步更新

以预处理 \(le\) 为例。按左端点升序扫矩形,这样每个点第一次被覆盖时取到最小值。
每列开并查集维护下一个没有被覆盖的点,倒序扫能覆盖到的列,能更新就更新,否则结束。容易发现每次至少覆盖一个点且每个点只会被覆盖一次

于是总复杂度降到了优秀的 \(O(q\alpha(mn)+mn)\)。注意清空时不能 memset

code
const int N = 2e3+5, mod = 1e9+7;
int n,m,q;

int le[N][N],up[N][N],f[N][N];
LL g[N][N];
vector<tuple<int,int,int>> a[N],b[N];

struct DSU {
	int fa[N],h[N],mx[N];
	void init(int n)
		{ For(i,1,n) fa[i] = i, h[i] = 1, mx[i] = i+1; }
	int find(int x) { return fa[x]==x ? x : fa[x]=find(fa[x]); }
	int nxt(int x) { return mx[find(x)]; }
	void merge(int x,int y) { if( (x=find(x)) != (y=find(y)) ) {
		if( h[x] > h[y] ) swap(x,y);
		ckmax(mx[y],mx[x]), fa[x] = y;
		if( h[x] == h[y] ) ++h[y];
	}}
} dsu[N];

struct Que {
	int l,r,q[N],a[N],b[N]; LL cnt[N];
	void push(int u,int x,int y) { q[++r] = u, cnt[a[r]=x] += (b[r]=y); }
	void pop_back() { cnt[a[r]] -= b[r], --r; }
	int front() { return q[l]; } int back() { return q[r]; }
	bool empty() { return l > r; }
	void clear() { l = 1, r = 0; mem(cnt,0,min(n,m)); }
	void valid(int x) { while( l<=r && q[l] < x ) cnt[a[l]] -= b[l], ++l; }
} qx,qy[N];

void MAIN() {
	read(n,m,q);
	For(i,1,q) {
		int x1,y1,x2,y2; read(x1,y1,x2,y2); ++x1,++y1;
		if( x1 <= x2 && y1 <= y2 ) a[x1].pb(x2,y1,y2), b[y1].pb(y2,x1,x2);
	}
	For(i,1,n) dsu[i].init(m);
	For(i,1,n) Rep(aa,0,a[i].size()) { int x2,y1,y2; tie(x2,y1,y2) = a[i][aa];
		// matrix: [i,y1]~[x2,y2]
		rFor(k,x2,i) {
			// current row: k
			if( up[k][y1] && dsu[k].nxt(y1) > y2 ) break;
			for(int j = y1; j <= y2; j = dsu[k].nxt(j)) if( !up[k][j] ) {
				// current column: j
				up[k][j] = i-1;
				if( j > 1 && up[k][j-1] ) dsu[k].merge(j-1,j);
				if( j < m && up[k][j+1] ) dsu[k].merge(j,j+1);
			}
		}
	}
	For(j,1,m) dsu[j].init(n);
	For(j,1,m) Rep(bb,0,b[j].size()) { int y2,x1,x2; tie(y2,x1,x2) = b[j][bb];
		rFor(k,y2,j) {
			if( le[x1][k] && dsu[k].nxt(x1) > x2 ) break;
			for(int i = x1; i <= x2; i = dsu[k].nxt(i)) if( !le[i][k] ) {
				le[i][k] = j-1;
				if( i > 1 && le[i-1][k] ) dsu[k].merge(i-1,i);
				if( i < n && le[i+1][k] ) dsu[k].merge(i,i+1);
			}
		}
	}
	For(i,1,n) For(j,1,m) {
		if( !le[i][j] ) le[i][j] = j;
		if( !up[i][j] ) up[i][j] = i;
	}
//	For(i,1,n) { For(j,1,m) cerr<<le[i][j]<<','<<up[i][j]<<' '; cerr<<endl; }
//	cerr<<endl;
	For(i,1,n) { qx.clear(); For(j,1,m) {
		if( le[i][j] < j ) {
			qx.valid(le[i][j]);
			while( !qx.empty() && f[i-1][j-1] > f[i-1][qx.back()] )
				qx.pop_back();
			qx.push(j-1,f[i-1][j-1],g[i-1][j-1]);
			int fro = qx.front();
			f[i][j] = f[i-1][fro]+1, g[i][j] = qx.cnt[f[i-1][fro]];
		}
		if( up[i][j] < i ) {
			qy[j-1].valid(up[i][j]);
			while( !qy[j-1].empty() && f[i-1][j-1] > f[qy[j-1].back()][j-1] )
				qy[j-1].pop_back();
			qy[j-1].push(i-1,f[i-1][j-1],g[i-1][j-1]);
			int fro = qy[j-1].front();
			if( f[fro][j-1]+1 > f[i][j] )
				f[i][j] = f[fro][j-1]+1, g[i][j] = qy[j-1].cnt[f[fro][j-1]];
			else if( f[fro][j-1]+1 == f[i][j] )
				g[i][j] += qy[j-1].cnt[f[fro][j-1]];
		}
		if( le[i][j] < j && up[i][j] < i && f[i-1][j-1]+1 == f[i][j] )
			g[i][j] -= g[i-1][j-1];
		if( !f[i][j] ) f[i][j] = g[i][j] = 1;
		g[i][j] %=mod;
	}}
//	For(i,1,n) { For(j,1,m) cerr<<f[i][j]<<','<<g[i][j]<<' '; cerr<<endl; }
//	cerr<<"---\n";
	int mx = 0; LL ans = 0;
	For(i,1,n) For(j,1,m)
		if( f[i][j] > mx ) mx = f[i][j], ans = g[i][j];
		else if( f[i][j] == mx ) ans += g[i][j];
	write(mx,' '), write((ans%mod+mod)%mod);
} signed main() {
	freopen("roche.in","r",stdin);
	freopen("roche.out","w",stdout);
	int T; read(T); while( T-- ) {
		For(i,1,n) For(j,1,m) le[i][j] = up[i][j] = f[i][j] = 0;
		For(i,1,n) a[i].clear();
		For(j,1,m) b[j].clear(), qy[j].clear();
		MAIN();
	}
	return ocl();
}

还有一个比较妙的 \(O(nq)\) 预处理 \(le\) 方法:容易发现 \(le[i,j]\le j\),因此在矩阵的每一行行尾打个取 \(\min\) 标记,向左递推即可,显然超出矩形范围后一定不优,不会错误更新。
根据这个做法可以得到 \(O(q\log n)\) 的扫描线,但会被卡常

特立独行的图

把点分成权值范围为 \([0,\frac{L}{2}),(\frac{L}{2},L]\) 的两组 \(A,B\),这样组内不会连边,形成二分图,需要特判权值为 \(\frac{L}{2}\) 的点,它会且仅会与权值为 \(0,L\) 的点连边

组内按权值升序排序(如何排下面讲),\(A\) 中的点一定与 \(B\) 的一段前缀连边,且对于 \(i,j\in A,i<j\),与 \(i\) 连边的点集包含 \(j\) 的(能连边的前缀长度不降),\(b\) 同理。
由此得到一个性质:每组点按权值排序的结果和按度数排序的相同

然后就很好构造方案了,考场上想的差分约束可以做,但 std 是 \(O(n)\) 贪心
先找到度数最大点 \(s\) 和与 \(s\) 连边的度数最大点 \(t\)\(s,t\) 分别是 \(A,B\) 中权值最大的点,根据与 \(s,t\) 的连边情况分出 \(A,B\)
对于 \(A_{i}\),分配权值 \(i\times l\),其中 \(l=\sqrt{L}>n\);对于 \(B_i\),先令 \(B_{i}=B_{i-1}-1\),用单调指针扫 \(A\) 中与 \(B_i\) 连边的权值最大点,\(B_{i}\) 在能连够边的情况下不断 \(-l\) 即可得到最小的解,也就保证了不会多连边

边界最好手模一下
非法情况一定要判全

code
const int N = 2e3+5, mod = 1e9+7;
int n,m,q;

int le[N][N],up[N][N],f[N][N];
LL g[N][N];
vector<tuple<int,int,int>> a[N],b[N];

struct DSU {
	int fa[N],h[N],mx[N];
	void init(int n)
		{ For(i,1,n) fa[i] = i, h[i] = 1, mx[i] = i+1; }
	int find(int x) { return fa[x]==x ? x : fa[x]=find(fa[x]); }
	int nxt(int x) { return mx[find(x)]; }
	void merge(int x,int y) { if( (x=find(x)) != (y=find(y)) ) {
		if( h[x] > h[y] ) swap(x,y);
		ckmax(mx[y],mx[x]), fa[x] = y;
		if( h[x] == h[y] ) ++h[y];
	}}
} dsu[N];

struct Que {
	int l,r,q[N],a[N],b[N]; LL cnt[N];
	void push(int u,int x,int y) { q[++r] = u, cnt[a[r]=x] += (b[r]=y); }
	void pop_back() { cnt[a[r]] -= b[r], --r; }
	int front() { return q[l]; } int back() { return q[r]; }
	bool empty() { return l > r; }
	void clear() { l = 1, r = 0; mem(cnt,0,min(n,m)); }
	void valid(int x) { while( l<=r && q[l] < x ) cnt[a[l]] -= b[l], ++l; }
} qx,qy[N];

void MAIN() {
	read(n,m,q);
	For(i,1,q) {
		int x1,y1,x2,y2; read(x1,y1,x2,y2); ++x1,++y1;
		if( x1 <= x2 && y1 <= y2 ) a[x1].pb(x2,y1,y2), b[y1].pb(y2,x1,x2);
	}
	For(i,1,n) dsu[i].init(m);
	For(i,1,n) Rep(aa,0,a[i].size()) { int x2,y1,y2; tie(x2,y1,y2) = a[i][aa];
		// matrix: [i,y1]~[x2,y2]
		rFor(k,x2,i) {
			// current row: k
			if( up[k][y1] && dsu[k].nxt(y1) > y2 ) break;
			for(int j = y1; j <= y2; j = dsu[k].nxt(j)) if( !up[k][j] ) {
				// current column: j
				up[k][j] = i-1;
				if( j > 1 && up[k][j-1] ) dsu[k].merge(j-1,j);
				if( j < m && up[k][j+1] ) dsu[k].merge(j,j+1);
			}
		}
	}
	For(j,1,m) dsu[j].init(n);
	For(j,1,m) Rep(bb,0,b[j].size()) { int y2,x1,x2; tie(y2,x1,x2) = b[j][bb];
		rFor(k,y2,j) {
			if( le[x1][k] && dsu[k].nxt(x1) > x2 ) break;
			for(int i = x1; i <= x2; i = dsu[k].nxt(i)) if( !le[i][k] ) {
				le[i][k] = j-1;
				if( i > 1 && le[i-1][k] ) dsu[k].merge(i-1,i);
				if( i < n && le[i+1][k] ) dsu[k].merge(i,i+1);
			}
		}
	}
	For(i,1,n) For(j,1,m) {
		if( !le[i][j] ) le[i][j] = j;
		if( !up[i][j] ) up[i][j] = i;
	}
//	For(i,1,n) { For(j,1,m) cerr<<le[i][j]<<','<<up[i][j]<<' '; cerr<<endl; }
//	cerr<<endl;
	For(i,1,n) { qx.clear(); For(j,1,m) {
		if( le[i][j] < j ) {
			qx.valid(le[i][j]);
			while( !qx.empty() && f[i-1][j-1] > f[i-1][qx.back()] )
				qx.pop_back();
			qx.push(j-1,f[i-1][j-1],g[i-1][j-1]);
			int fro = qx.front();
			f[i][j] = f[i-1][fro]+1, g[i][j] = qx.cnt[f[i-1][fro]];
		}
		if( up[i][j] < i ) {
			qy[j-1].valid(up[i][j]);
			while( !qy[j-1].empty() && f[i-1][j-1] > f[qy[j-1].back()][j-1] )
				qy[j-1].pop_back();
			qy[j-1].push(i-1,f[i-1][j-1],g[i-1][j-1]);
			int fro = qy[j-1].front();
			if( f[fro][j-1]+1 > f[i][j] )
				f[i][j] = f[fro][j-1]+1, g[i][j] = qy[j-1].cnt[f[fro][j-1]];
			else if( f[fro][j-1]+1 == f[i][j] )
				g[i][j] += qy[j-1].cnt[f[fro][j-1]];
		}
		if( le[i][j] < j && up[i][j] < i && f[i-1][j-1]+1 == f[i][j] )
			g[i][j] -= g[i-1][j-1];
		if( !f[i][j] ) f[i][j] = g[i][j] = 1;
		g[i][j] %=mod;
	}}
//	For(i,1,n) { For(j,1,m) cerr<<f[i][j]<<','<<g[i][j]<<' '; cerr<<endl; }
//	cerr<<"---\n";
	int mx = 0; LL ans = 0;
	For(i,1,n) For(j,1,m)
		if( f[i][j] > mx ) mx = f[i][j], ans = g[i][j];
		else if( f[i][j] == mx ) ans += g[i][j];
	write(mx,' '), write((ans%mod+mod)%mod);
} signed main() {
	freopen("roche.in","r",stdin);
	freopen("roche.out","w",stdout);
	int T; read(T); while( T-- ) {
		For(i,1,n) For(j,1,m) le[i][j] = up[i][j] = f[i][j] = 0;
		For(i,1,n) a[i].clear();
		For(j,1,m) b[j].clear(), qy[j].clear();
		MAIN();
	}
	return ocl();
}

玩游戏

不会导数、生成函数,暂鸽

骆驼

hz 学长的博客讲的很清楚了,提供一份自认清晰的代码

code
int odd[5][5] = {{1,20,12,-1,19},{7,15,3,6,14},{22,10,18,21,11},{2,5,13,-1,4},{8,16,23,9,17}},
	even[5][5] = {{1,12,5,2,13},{7,18,15,10,19},{23,3,-1,22,4},{16,11,6,17,14},{8,21,24,9,20}},
	up[5][5] = {{21,10,25,22,9},{15,18,7,12,17},{5,23,1,4,24},{20,11,16,19,8},{14,3,6,13,2}},
	down[5][5] = {{10,21,6,9,22},{16,13,24,19,14},{5,8,1,4,7},{11,20,15,12,23},{17,3,25,18,2}},
	le[5][5] = {{18,5,24,19,6},{10,21,16,9,22},{25,13,1,4,14},{17,8,23,20,7},{11,3,15,12,2}},
	ri[5][5] = {{18,10,24,17,9},{13,21,7,12,22},{5,16,1,4,25},{19,11,23,20,8},{14,3,6,15,2}},
	ul[5][5] = {{25,17,7,24,16},{12,4,21,13,5},{19,9,1,18,8},{22,14,6,23,15},{11,3,20,10,2}};

const int N = 1e3+5;
int n;

int m,cnt,a[N][N];

void move(int x,int y,int b[][5]) {
	x = (x-1)*5+1, y = (y-1)*5+1;
	For(i,0,4) For(j,0,4) a[x+i][y+j] = cnt+b[i][j];
	cnt += 25;
}

signed main() {
	freopen("camel.in","r",stdin);
	freopen("camel.out","w",stdout);
	read(n); m = n / 5;
	if( m == 1 ) puts("1 9 19 2 10\n14 22 5 13 21\n7 17 25 8 18\n4 12 20 3 11\n15 23 6 16 24"), exit(0);
	if( m & 1 ) {
		move(1,1,odd), a[4][4] = n*n-1, a[1][4] = n*n, cnt = 23;
		Rep(i,2,m) move(i,1,down); move(m,1,ri);
		rFor(i,m,3) {
			Rep(j,2,m) move(i,j,ri); move(i,m,up);
			if( --i > 2 ) { rFor(j,m,3) move(i,j,le); move(i,2,up); }
		}
		rFor(j,m,2) {
			move(2,j,up), move(1,j,le);
			--j;
			move(1,j,down), move(2,j,j>2?le:ul);
		}
	} else {
		move(1,1,even), a[3][3] = n*n, cnt = 24;
		Rep(i,2,m) move(i,1,down); move(m,1,ri);
		rFor(i,m,1) {
			Rep(j,2,m) move(i,j,ri); move(i,m,up);
			--i;
			rFor(j,m,3) move(i,j,le); move(i,2,i>1?up:le);
		}
	}
	For(i,1,n) { For(j,1,n) write(a[i][j],' '); putchar(10); }
	return ocl();
}
打表程序
const int dx[]={0,2,3,2,0,-2,-3,-2}, dy[]={3,2,0,-2,-3,-2,0,2};

int en,a[10][10];

bool valid(int x,int y) { return 1<=x&&x<=5&&1<=y&&y<=5 && !a[x][y]; }
void dfs(int x,int y) {
	if( x == 5 && y == 3 && a[x][y] ) { // 终点
		if( a[x][y] == en ) {
			putchar('{');
			For(i,1,5) {
				putchar('{');
				For(j,1,5) { printf("%d",a[i][j]); if( j < 5 ) putchar(','); }
				putchar('}'); if( i < 5 ) putchar(',');
			} putchar('}');
			exit(0);
		}
		return;
	}
	Rep(i,0,8) {
		int xx = x+dx[i], yy = y+dy[i];
		if( valid(xx,yy) ) a[xx][yy] = a[x][y]+1, dfs(xx,yy), a[xx][yy] = 0;
	}
}

signed main() {
//	freopen("camel.in","r",stdin);
//	freopen("camel.out","w",stdout);
	int s = 1, t = 1; // 起点
	a[3][3] = -1, --en; // 禁走
	a[s][t] = 1, dfs(s,t);
}
posted @ 2021-10-15 08:25  401rk8  阅读(51)  评论(0编辑  收藏  举报