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
s⊕2i在
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}
fs⊕2i中的元素全部加入
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}
dps⊕2i中值为
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,真是抽象