【学习笔记】NOIP爆零赛5

poly

原题是 codechef POINPOLY

主要看到是计算几何背景就很麻。

正解考虑抽屉原理,把每个顶点的坐标按奇偶性分成四类,由抽屉原理,至少有一类会包含 n 4 \frac{n}{4} 4n个点。这些点两两配对,取中点,中点就是整点。只要这些点不是一条边的两个端点,那么终点必在多边形内。

代码非常好写。

考场上乱搞只得了 80 p t s 80pts 80pts

#include<bits/stdc++.h>
#define ll long long
#define db double
#define pb push_back
#define inf 0x3f3f3f3f3f3f3f3f
#define fi first
#define se second
using namespace std;
struct node{
	int x,y;
}a[100005];
map<pair<int,int>,int>id;
vector<int>v[2][2];
vector<pair<int,int>>ans;
int n,m;
void solve(){
	cin>>n;for(int i=0;i<2;i++)for(int j=0;j<2;j++)v[i][j].clear();
	ans.clear(),id.clear();
	for(int i=0;i<n;i++){
		cin>>a[i].x>>a[i].y;
		v[abs(a[i].x)&1][abs(a[i].y)&1].pb(i);
	}m=n/10;
	for(int mx1=0;mx1<2;mx1++){
		for(int mx2=0;mx2<2;mx2++){
			for(int i=0;i<v[mx1][mx2].size();i++){
				for(int j=i+1;j<v[mx1][mx2].size();j++){
					int x=v[mx1][mx2][i],y=v[mx1][mx2][j];
					if((x+1)%n!=y&&id.find({(a[x].x+a[y].x)/2,(a[x].y+a[y].y)/2})==id.end()){
						ans.pb({(a[x].x+a[y].x)/2,(a[x].y+a[y].y)/2});
						id[{(a[x].x+a[y].x)/2,(a[x].y+a[y].y)/2}]=1;
						if(!(--m))return;
					}
				}
			}		
		}
	}
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
	int T;cin>>T;
	while(T--){
		solve();
		for(auto x:ans)cout<<x.fi<<' '<<x.se<<"\n";
	}
}

Board Game

这题很妙啊。

应该能一眼看出 O ( n m 2 n m ) O(nm2^{nm}) O(nm2nm)的傻瓜做法,可以得到 50 p t s 50pts 50pts

正解非常脑洞:考虑将最后 5 5 5位提出来,这样恰好有 32 32 32个子集,可以用一个 unsigned int \text{unsigned int} unsigned int存下来。这很像一个 d p dp dp d p dp dp

1.1 1.1 1.1 对于高维前缀和部分,分成后 5 5 5位和前 22 22 22位的转移。设 f s f_s fs表示前 22 22 22位的二进制状态为 s s s时,后 5 5 5位所有子集的状态。先转移后 5 5 5位,类似普通高维前缀和,枚举数位 i i i以及 s ′ s' s,若 s ′ s' s的第 i i i位是 1 1 1并且 s ⊕ 2 i s\oplus 2^i s2i f s f_s fs中,那么将 s s s加入 f s f_s fs中。然后转移前 22 22 22位,枚举数位 i i i以及 s ′ s' s,若 s ′ s' s的第 i i i位是 1 1 1,那么将 f s ⊕ 2 i f_{s\oplus 2^i} fs2i中的元素全部加入 f s f_s fs中。
1.2 1.2 1.2 对于 d p dp dp部分,同样分成后 5 5 5位和前 22 22 22位。设 d p s dp_s dps表示前 22 22 22位的二进制状态为 s s s时,后 5 5 5位的 d p dp dp值为 1 1 1的子集的集合。这里值为 1 1 1表示必胜。初始高维前缀和结果为 1 1 1的状态 d p dp dp值为 0 0 0。一般的,如果能转移到一个值为 0 0 0的点那么 d p dp dp值为 1 1 1,否则 d p dp dp值为 0 0 0。倒序枚举 s s s,先做外层转移,枚举第 i i i位,若 s s s的第 i i i位是 0 0 0,将 d p s ⊕ 2 i dp_{s\oplus 2^i} dps2i中值为 0 0 0的子集全部加入 d p s dp_s dps中。然后做内层转移,具体过程类似。

这样复杂度优化成了 O ( 2 n m ) O(2^{nm}) O(2nm)。不过这样有一个 5 5 5的常数,可以再优化一下。

#include<bits/stdc++.h>
using namespace std;
int n,m,K,h,w,S1,S2;
unsigned int v[1<<22],dp[1<<22],adj[1<<5],sub[1<<5];
string ss;
int main(){
	cin>>n>>m>>K;S1=1<<n*m-5,S2=1<<5;
	for(int i=1;i<=K;i++){
		int s=0;cin>>h>>w;
		for(int j=0;j<h;j++){
			cin>>ss;
			for(int k=0;k<w;k++){
				if(ss[k]=='1')s+=1<<j*m+k;
			}
		}
		for(int j=0;j<=n-h;j++){
			for(int k=0;k<=m-w;k++){
				int s2=s<<j*m+k;
				v[s2>>5]|=1<<(s2&31);
			}
		}
	}
	for(int i=0;i<S2;i++){
		for(int j=0;j<=i;j++){
			if((i|j)==i)sub[i]|=1<<j;
		}
		for(int j=0;j<5;j++){
			if(!(i>>j&1))adj[i]|=1<<(i|(1<<j));
		}
	}
	for(int i=0;i<S1;i++){
		for(int j=0;j<S2;j++){
			if(v[i]&sub[j])v[i]|=1<<j;
		}
	}
	for(int i=0;i<n*m-5;i++){
		for(int j=0;j<S1;j++){
			if(j>>i&1){
				v[j]|=v[j^(1<<i)];
			}
		}
	}
	for(int i=S1-1;i>=0;i--){
		for(int j=0;j<n*m-5;j++){
			if(!(i>>j&1)){
				dp[i]|=(-1-dp[i|(1<<j)])&(-1-v[i]);
			}
		}
		for(int j=S2-1;j>=0;j--){
			if(((-1-v[i])>>j&1)&&(((-1-dp[i])|v[i])&(adj[j]))){
				dp[i]|=1<<j;
			}
		}
	}
	cout<<((dp[0]&1)?"Alice":"Bob");
}

harmony

这题更抽象。大数据结构题你开1 s s s逗我玩呢,不过鉴于有人确实在 0.5 s 0.5s 0.5s内过了因此我也不太好说什么,况且 noi \text{noi} noi确实有过卡常数的先例,我只能说我是跑了 4 s 4s 4s的丝薄

维护方式非常脑瘫。用分块分别维护每一块内每种颜色的出现次数,然后用线段树维护可能成为答案的集合。这里我在用线段树维护时没有想到题解的做法,因此合并的复杂度可能高了些。按照题解的说法,每次重复删去 6 6 6个互不相同的数,最后剩下的本质不同的数的个数肯定不超过 6 6 6个,对这些数暴力算一下即可。

这题给我唯一的教训是,考场上不要去冲性价比很低的数据结构。因为很有可能你打完过后才发现自己的做法和正解的不一样,而你又发现怎么改都会超时。并且数据结构一写就停不下来,对其他题目的思考也有非常坏的影响,因此建议数据结构留到最后做,就算做不出来也有暴力打底 况且我是数据结构的丝薄

wait

这题应该是比较水的。当然放在 t 4 t4 t4也很抽象,其实应该多想想这道题的,况且部分分的构造也非常 trick \text{trick} trick

首先考虑离散化。对于 w i = − 1 w_i=-1 wi=1的点,如果我们把 [ l , r ) [l,r) [l,r)看成 l l l r r r的一条边,然后将其定向,把度为奇数的点连起来再跑欧拉回路,这样我们发现每个点恰好被覆盖的黑白区间数目相等,去掉加的那些虚边后显然还是合法的。这样就有 45 p t s 45pts 45pts

对于一般的情况,我们还是先加虚边,再钦定每一条边的方向,最后跑网络流即可。

把网络流板题放 t 4 t4 t4,真是抽象

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值