省选模拟赛 7

被咕咕咕了的博客

A

幂等性相当强

根据群的定义,这玩意不可能是加法

不妨定义为max!

那么我们会发现查询相当于区间max

而这个东西.....要定义一些东西才行吧?

显然我们有nlognnlogn个机会定义max....?

树上max?

直接输出1是一种合法的解

但是不能这样

我们每个点向上跳log步得到的最大值答案都搞出来,就是用O(nlogn)O(nlogn)搞出一个树上ST表

剩下还有3次机会给我们的q询问

也很简单,之前树上倍增不是跳log次吗?

但是这次我们左边用两个就能拼出一个最大值

然后右边用两个也能拼出一个最大值

二者之前再拼出一个值

正好用了3次,卡满了2e6....

B

插头dp!

这个形态....防止计重是相当难的唉

记录什么呢?

_____|-----

划分线上每个格子向哪个方向延伸?

不对,是是否向下延伸?

是的,你会发现我们能记录很多是否向下的0/1变量

如果向下为1,那我们这个位置下一刻只能放0,否则下个位置只能放1

设计这样一个状态,我们可能会转移出不太对劲的状态吧?就是计重了?

是的!因为你会发现这样我们没有考虑颜色,而直接将颜色计入转移系数显然会算重!

因为我们本质不同的方案数只和颜色相同,而一个染色方案是否有效却和我们的插头dp相同

所以说我们打算外层dp一下染色方案并状压内层插头形态

哼,怎么可能,状压的是内层所有可行的插头方案,就是比如有2^16个插头形态,就记录2^2^16

这题n是6,所以是2^2^6=2642^64

一个ll就能存下来

当然,这个东西显然要用map记录状态并转移,因为本质不同的染色方案可能对应了相同的边界颜色....

总而言之,dp套dp

code:



#pragma G++ optimize(2)
#pragma G++ optimize(3)

#include<iostream>
#include<cstring>
#include<cstdio>
#include<map>
using std::map;
using std::pair;
using std::make_pair;
const int P = 998244353;
#define ull unsigned long long
#define ll long long
#define fi first
#define se second
#define mkp(x,y) make_pair(x,y)
map<pair<ll, ull>, ll> dp[3];
int n, m;
inline void update(int x, int y, ull Sc, ll vl, ull S, int t, int nc) {
	ull nS = 0;
	for(int plug = 0; plug < (1 << n); ++plug) {
		if(S >> plug & 1) {//这个状态有了
			int upc = Sc >> y & 1;//上方颜色
			int upplug = plug >> y & 1;//上方插头
			int Lc = 0, Lplug = 0;

			if(y)Lc = Sc >> (y - 1) & 1, Lplug = plug >> (y - 1) & 1;//不是第一个,0是第一个
			if(upplug) {//有下插头
				if(nc != upc)//如果颜色不一样
					nS |= 1ull << (plug ^ (1 << y));//除掉下插头
			} else {//没有下插头
				if(y && nc != Lc && Lplug) { //有右插头
					nS |= 1ull << (plug ^ (1 << (y - 1)));//除掉右插头
				}
				nS |= 1ull << (plug ^ (1 << y)); //下插头
			}
		}
	}
	if(nS)(dp[t][mkp(Sc ^ ((Sc >> y & 1) << y) ^ (nc << y), nS)] += vl) %= P;
}

int main() {
	// int n, m;
	int t = 0;

	dp[0][mkp(0, 1)] = 1;
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= m; ++i) {
		for(int j = 0; j < n; ++j) {
			t ^= 1;
			dp[t].clear();
			for(auto it : dp[t ^ 1]) {//C++11
				ll vl = it.se;
				ll col = it.fi.fi;
				ull S = it.fi.se;
				for(int k = 0; k < 2; ++k) {
					update(i, j, col, vl, S, t, k);
				}
			}
		}
	}
	ll ans = 0;
	for(auto it : dp[t])
		if(it.fi.se & 1)
			ans = (ans + it.se) % P;
	printf("%lld\n", ans);
	return 0;
}



C

有一个结论:如果选择的长度为k的区间不包括两边子区间的最大值的话,一定无法更新答案

假设最大值为x次大值为y

而左边最次大值为A,a右边最次大值为B,b

显然x+y最大为a+b

而此时,若b>A,B+b大于a+b

若a>B,a+A大于a+b

除外则也显然不优

那么直接用线段树维护即可

finish