CF 494 题解

A

前面的 # 都贪心只填一个 ),用最后一个 # 调整即可。

还是最后扫一遍判断比较靠谱。。尝试 \(O(1)\) 特判挂了。。

B

\(f_i\) 表示最后一个区间右端点选在 \(i\) 的概率,我们对于每个点 \(i\),处理出 \(l_i\) 表示最大的数满足 \([l_i,i]\) 包含串 \(T\)(可以用哈希/kmp轻松处理)。

然后转移的话先枚举这一段在哪里结束,再枚举下一段在哪里开始,转移是

\[f_i = \sum_{j \leq p} \sum_{k < j} f_k \]

顺便维护个前缀和就好了。

C

区间只有相含和相离的限制,让我们想到按照包含关系对区间建树。

建树后可以在树上搞个dp:设 \(f_{v,i}\) 表示 \(v\) 点所控制的区间,最大值 \(\leq i\) 的方案数。转移:

\[f_{v,i} = p_v(\prod_{x \in son_v} f_{x,i-1}) + (1-p_v)(\prod_{x \in son_v}f_{x,i}) \]

但是值域太大了怎么办?设 \(v\) 控制的区间的最大值为 \(mx_v\),发现 \(i\) 的取值只有可能是 \([mx_v,mx_v+m]\),所以就将状态压缩到了 \(O(nm)\)。时间复杂度 \(O(nm)\)

注意这里初始化的时候要注意一下:\(f_{v,mx}\) 只能用 \((1-p_v)\) 的概率从子树转移,为了防止没有考虑全新增数贡献的最大值。

D

我们设一次询问是询问 \(v\)\(u\) 的所有子树内的点的答案(和题目是反着的)。

显然我们先预处理出 \(all_v\) 表示 \(v\) 到所有点的距离的平方和,就可以只用求题目中给出的式子两者之一了。(这点很重要,要不然代码会很麻烦)

那么如果 \(v\)\(u\) 的子树外:我们如果能预先处理出每个子树的距离和,距离平方和,子树大小,记为 \(sm_x,sm2_x,sz_x\),设 \(dis(u,v) = d\),根据 \((x+d)^2 = x^2+d^2+2xd\) ,就可以用这些值更新答案。

如果 \(v\)\(u\) 的子树内:发现直接往上跳要维护一堆东西和减去重复贡献,很烦。我们考虑后面的咋做:只需要类似的求出 \(ss_x,ss2_x\) 表示 \(x\) 到子树外的所有点的距离和,距离平方和,就可以类似的求出答案了。

时间复杂度 \(O(n \log n)\),可以优化到 \(O(n)\)

E

棋盘翻转颜色问题有一个经典的结论是这个局面等价于每个黑色点单独存在的局面的和。

如果是一维情况,一个经典的结论是 \(i\) 点的 SG 值是 \(\text{lowbit}(i)\)

如果是二维情况无限制,一个经典的结论是 \((i,j)\) 的 SG 值是 \(\min\{\text{lowbit}(i),\text{lowbit}(j)\}\)

而这个题有限制,打表可以发现 SG 值是 \(\min\{\text{lowbit}(i),\text{lowbit}(j),\text{greatbit}(k)\}\)

我们考虑去求出 \(ans_i\) 表示 \(\text{lowbit} \geq i\) 的点的个数,这个相当于将矩形 \((x_1,y_1,x_2,y_2)\) 变成 \((\lceil\frac{x_1}{2^i}\rceil,\lceil\frac{y_1}{2^i}\rceil,\lfloor \frac{x_2}{2^i} \rfloor,\lfloor \frac{y_2}{2^i}\rfloor)\),然后求面积并。注意面积并的求法:规定线段树上一个区间代表左开右闭的区间。

#include <bits/stdc++.h>

#define fi first
#define se second
#define db double
#define P std::pair<int,int>
#define LL long long
#define pb push_back
#define MP std::make_pair
#define all(x) x.begin(),x.end()
#define CLR(i,a) memset(i,a,sizeof(i))
#define FOR(i,a,b) for(int i = a;i <= b;++i)
#define ROF(i,a,b) for(int i = a;i >= b;--i)
#define DEBUG(x) std::cerr << #x << '=' << x << std::endl

const int MAXN = 4e5 + 5;

int n,m,k;
int pw[31];

struct Node{
	int x1,y1,x2,y2;
	Node(int x1=0,int y1=0,int x2=0,int y2=0) : x1(x1),y1(y1),x2(x2),y2(y2) {}
}a[MAXN],b[MAXN];

struct Event{
	int x,y1,y2,d;
	Event(int x=0,int y1=0,int y2=0,int d=0) : x(x),y1(y1),y2(y2),d(d) {}
	
	inline bool operator < (const Event &t) const {
		return x < t.x;
	}
};

LL sm[MAXN<<2];
int tag[MAXN<<2];
#define lc ((x)<<1)
#define rc ((x)<<1|1)
std::vector<int> S;

inline void build(int x,int l,int r){
	tag[x] = sm[x] = 0;
	if(l == r) return;
	int mid = (l + r) >> 1;
	build(lc,l,mid);build(rc,mid+1,r);
}

inline void modify(int x,int l,int r,int L,int R,int d){
	if(L > R) return;
	int len = S[r-1]-(l==1?0:S[l-2]);
	if(l == L && r == R){
		tag[x] += d;
		if(tag[x]) sm[x] = len;
		else sm[x] = sm[lc]+sm[rc];
		return;
	}
	int mid = (l + r) >> 1;
	if(R <= mid) modify(lc,l,mid,L,R,d);
	else if(L > mid) modify(rc,mid+1,r,L,R,d);
	else modify(lc,l,mid,L,mid,d),modify(rc,mid+1,r,mid+1,R,d);
	if(tag[x]) sm[x] = len;
	else sm[x] = sm[lc]+sm[rc];
}

inline int work(int c){
	int U = (1<<c)-1;
	S.clear();
	std::vector<Event> G;
	FOR(i,1,m){
		a[i].x1 = (b[i].x1>>c);
		a[i].y1 = (b[i].y1>>c);
		a[i].x2 = (b[i].x2>>c);
		a[i].y2 = (b[i].y2>>c);
		if(a[i].x1 > a[i].x2 || a[i].y1 > a[i].y2) continue;
		if(b[i].x1&U) a[i].x1++;
		if(b[i].y1&U) a[i].y1++;
		S.pb(a[i].y1-1);S.pb(a[i].y2);
		G.pb(Event(a[i].x1,a[i].y1-1,a[i].y2,1));
		G.pb(Event(a[i].x2+1,a[i].y1-1,a[i].y2,-1));
	}
	std::sort(all(S));S.erase(std::unique(all(S)),S.end());
	for(auto &x:G){
		x.y1 = std::lower_bound(all(S),x.y1)-S.begin()+1;
		x.y2 = std::lower_bound(all(S),x.y2)-S.begin()+1;
	}
	int m = S.size();
	std::sort(all(G));build(1,1,m);
	int las = 0,ans = 0,ps = 0;
	while(ps < G.size()){
		int t = G[ps].x;
		ans += (G[ps].x-las)*sm[1];
		while(ps+1 < G.size() && G[ps+1].x == t) modify(1,1,m,G[ps].y1+1,G[ps].y2,G[ps].d),++ps;
		modify(1,1,m,G[ps].y1+1,G[ps].y2,G[ps].d);
		las = G[ps].x;++ps;
	}
	return ans;
}

int ans[MAXN];

int main(){
	scanf("%d%d%d",&n,&m,&k);
	FOR(i,1,m) scanf("%d%d%d%d",&b[i].x1,&b[i].y1,&b[i].x2,&b[i].y2);
	FOR(i,0,30) ans[i] = work(i);
	int p = 0;
	while((1<<(p+1)) <= k) ++p;
	FOR(i,0,p-1){
		if((ans[i]-ans[i+1])&1){
			puts("Hamed");
			return 0;
		}
	}
	puts(ans[p]&1?"Hamed":"Malek");
	return 0;
}
posted @ 2020-10-13 22:01  RainAir  阅读(66)  评论(0编辑  收藏  举报