2018-02-24考试

题目

T1: [POI2006]KRA-The Disks
T2: [POI2007]POW-The Flood
T3: [POI2007]MEG-Megalopolis
T4: [POI2006]ORK-Ploughing

结果

主要题解

T2

我们知道,如果一个点和一个海拔不高于它的点相连
那么连在那个点是更优的,所以考虑按照每个点的海拔排序
既然按照海拔排序,相邻的海拔递增的点可以放在同一个集合里面讨论
考虑使用并查集,每一个集合中只需要有一个抽水机即可

每次从海拔最低的点中选出一个点
将它和它周围的海拔比当前海拔低的点直接链接在一起
同时,维护每个并查集是否存在抽水机
如果当前点是城市,并且所在的并查集中有抽水机了
显然是不用再额外增加抽水机了
但是,如果当前点和周围的点合并完之后,所在集合依然没有抽水机
因为它所在的集合周围的点海拔一定更高,不可能有抽水机
所以在当前集合中一点要放一个抽水机,
那么,给当前集合放一个抽水机,同时答案加一即可

T4

难度较大
我们知道,如果我们选定了以横向为主,或者纵向为主,
那么就有尽可能减少另一个方向上耕地的次数

所以分开贪心,但是本质相同,所以接下来只考虑纵向为主

既然确定了以纵向为主,那么就要尽可能减少横向操作的次数
所以,只要能够纵向耕地,就不考虑横向耕地
可是,如果到了某个时候,纵向无法耕了
此时必须横向耕
但是我们不知道应该从上面开始还是下面开始

为了解决这个问题
我们假设上面最多耕的次数是有限次
换种想法,我们假设上面至少耕这么多次
既然上面的次数确定,那么下方的耕地次数越少越好
所以耕地的优先级:
左-右-上-下
只要能够耕就一定耕
这样,枚举-贪心的做法就可以做啦
横向、纵向分别算一遍
时间复杂度$O((n+m)^2)$

代码

T1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#include<bits/stdc++.h>
#define RG register
#define file(x) freopen(#x".in", "r", stdin);freopen(#x".out", "w", stdout);
#define clear(x, y) memset(x, y, sizeof(x));
using namespace std;

inline int read()
{
int data=0, w=1;
char ch=getchar();
while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
if(ch=='-') w=-1, ch=getchar();
while(ch>='0'&&ch<='9') data=(data<<3)+(data<<1)+(ch^48), ch=getchar();
return data*w;
}

const int maxn(300010);
int n, m, r[maxn], k[maxn], b[maxn << 1], cnt, val[maxn], minv[maxn << 2], ans;
map<int, int> id;

#define son(i) ((root << 1) | i)
inline void build(int root=1, int l=1, int r=n)
{
if(l==r) { minv[root]=val[l]; return; }
int mid(l+r>>1); build(son(0), l, mid); build(son(1), mid+1, r);
minv[root]=min(minv[son(0)], minv[son(1)]);
}

const int I(2147483647);
inline int query(int ql, int qr, int root=1, int l=1, int r=n)
{
if(r<ql || l>qr) return I;
if(ql<=l && r<=qr) return minv[root];
int mid(l+r>>1);
return min(query(ql, qr, son(0), l, mid), query(ql, qr, son(1), mid+1, r));
}

int main()
{
n=read(); m=read();
for(RG int i=1;i<=n;i++) r[i]=b[i]=read();
for(RG int i=1;i<=m;i++) k[i]=b[n+i]=read();
sort(b+1, b+n+m+1);
cnt=unique(b+1, b+n+m+1)-b-1;
for(RG int i=1;i<=cnt;i++) id[b[i]]=i;
for(RG int i=1;i<=n;i++)
{
r[i]=id[r[i]];
if(!val[r[i]]) val[r[i]]=i;
}
for(RG int i=1;i<=cnt;i++) if(!val[i]) val[i]=n+1;
for(RG int i=1;i<=m;i++) k[i]=id[k[i]];
build(); ans=n+1;
for(RG int i=1;i<=m;i++)
{
if(k[i]==1) ans--;
else
{
int x=query(1, k[i]-1)-1;
ans=min(ans-1, x);
}
if(ans==0) break;
}
printf("%d\n", ans);
return 0;
}

T2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#include<bits/stdc++.h>
#define RG register
#define file(x) freopen(#x".in", "r", stdin);freopen(#x".out", "w", stdout);
#define clear(x, y) memset(x, y, sizeof(x));
using namespace std;

inline int read()
{
int data=0, w=1;
char ch=getchar();
while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
if(ch=='-') w=-1, ch=getchar();
while(ch>='0'&&ch<='9') data=(data<<3)+(data<<1)+(ch^48), ch=getchar();
return data*w;
}

const int maxn(1010);
const int x[4]={1, 0, -1, 0};
const int y[4]={0, 1, 0, -1};
struct point { int a, b, h; } p[maxn * maxn];
int fa[maxn * maxn], n, m, cnt, high, ans, g[maxn][maxn];
bool put[maxn * maxn], need[maxn * maxn];

inline int find(const int &x) { return fa[x]==x ? x : fa[x]=find(fa[x]); }
inline int num(const point &p) { return (p.a-1)*m+p.b; }
inline int num(const int &a, const int &b) { return (a-1)*m+b; }
inline bool cmp(const point &a, const point &b) { return a.h<b.h; }

int main()
{
n=read(); m=read();
for(RG int i=1;i<=n;i++)
for(RG int j=1;j<=m;j++)
{
p[++cnt]=(point){i, j, abs(g[i][j]=read())};
int k(num(p[cnt]));
if(g[i][j]>0) need[k]=true;
else g[i][j]=-g[i][j];
fa[k]=k;
}
sort(p+1, p+cnt+1, cmp);
int pos=1;
for(RG int i=1;i<=cnt;i++)
{
int fx=find(num(p[i]));
for(RG int j=0;j<4;j++)
{
int _=p[i].a+x[j], __=p[i].b+y[j];
if(!(_>0&&__>0&&_<=n&&__<=m)) continue;
if(g[_][__]>g[p[i].a][p[i].b]) continue;
int fy=find(num(_, __));
if(fx!=fy) fa[fx]=fy, put[fy]|=put[fx];
fx=find(fx);
}
if((!put[fx])&&p[i].h<p[i+1].h)
for(RG int j=pos;j<=i;j++)
if(need[j]) { ans++; put[fx]=true; break; }
if(p[i].h<p[i+1].h) pos=i+1;
}
printf("%d\n", ans);
return 0;
}

T3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
#include<bits/stdc++.h>
#define RG register
#define file(x) freopen(#x".in", "r", stdin);freopen(#x".out", "w", stdout);
#define for_edge(i, x) for(RG int i=head[x];i;i=e[i].next)
#define clear(x, y) memset(x, y, sizeof(x));
using namespace std;

inline int read()
{
int data=0, w=1;
char ch=getchar();
while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
if(ch=='-') w=-1, ch=getchar();
while(ch>='0'&&ch<='9') data=(data<<3)+(data<<1)+(ch^48), ch=getchar();
return data*w;
}

const int maxn(1e5+10);
struct edge { int next, to; } e[maxn << 1];
int head[maxn], e_num, n, m;
inline void add_edge(int from, int to) { e[++e_num]=(edge){head[from], to}; head[from]=e_num; }

int fa[maxn], size[maxn], heavy[maxn], val[maxn];
void dfs(int x)
{
size[x]=1;
int _max=0;
for_edge(i, x)
{
int to=e[i].to;
if(to==fa[x]) continue;
fa[to]=x; dfs(to);
size[x]+=size[to];
if(_max < size[to]) _max=size[to], heavy[x]=to;
}
}

int pos[maxn], cnt_pos[maxn], belong[maxn], cnt_node;
void dfs(int x, int chain)
{
belong[x]=chain;
pos[x]=++cnt_node;
cnt_pos[cnt_node]=x;
int k=heavy[x];
if(!k) return;
dfs(k, chain);
for_edge(i, x)
{
int to=e[i].to;
if(to==fa[x] || to==k) continue;
dfs(to, to);
}
}

int sum[maxn << 2];
#define son(i) ((root<<1)|i)
inline void build(int root=1, int l=1, int r=cnt_node)
{
if(l==r) { sum[root]=val[cnt_pos[l]]; return; }
int mid(l+r>>1); build(son(0), l, mid); build(son(1), mid+1, r);
sum[root]=sum[son(0)]+sum[son(1)];
}

inline void update(int id, int root=1, int l=1, int r=cnt_node)
{
if(l==r) { sum[root]=0; return; }
int mid(l+r>>1);
if(id<=mid) update(id, son(0), l, mid);
else update(id, son(1), mid+1, r);
sum[root]=sum[son(0)]+sum[son(1)];
}

inline int query(int ql, int qr, int root=1, int l=1, int r=cnt_node)
{
if(r<ql || l>qr) return 0;
if(ql<=l && r<=qr) return sum[root];
int mid(l+r>>1);
return query(ql, qr, son(0), l, mid)+query(ql, qr, son(1), mid+1, r);
}

inline int query_chain(int a)
{
int ans=0;
while(belong[a]!=1)
{
ans+=query(pos[belong[a]], pos[a]);
a=fa[belong[a]];
}
ans+=query(1, pos[a]);
return ans;
}

char s[3];
int a, b;
int main()
{
n=read();
for(RG int i=1;i<n;i++) a=read(), b=read(), add_edge(a, b), add_edge(b, a), val[i+1]=1;
dfs(1); dfs(1, 1); build();
m=read();
for(RG int i=1;i<n+m;i++)
{
scanf("%s", s);
a=read();
if(s[0]=='W') printf("%d\n", query_chain(a));
else b=read(), update(pos[b]);
}
return 0;
}

T4

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include<bits/stdc++.h>
#define RG register
#define file(x) freopen(#x".in", "r", stdin);freopen(#x".out", "w", stdout);
#define clear(x, y) memset(x, y, sizeof(x));
using namespace std;

inline int read()
{
int data=0, w=1;
char ch=getchar();
while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
if(ch=='-') w=-1, ch=getchar();
while(ch>='0'&&ch<='9') data=(data<<3)+(data<<1)+(ch^48), ch=getchar();
return data*w;
}

const int maxn(2010);
#define orz [maxn][maxn]
int g orz, s1 orz, s2 orz, n, m, K;

inline int Tanl(int l)
{
int l1=1, l2=n, r1=1, r2=m, ans=0;
while(l1<=l2 && r1<=r2)
{
ans++;
if(s1[l1][r2]-s1[l1][r1-1]<=K) { l1++; continue; }
if(s1[l2][r2]-s1[l2][r1-1]<=K) { l2--; continue; }
if(s2[l2][r1]-s2[l1-1][r1]<=K && r1<l) { r1++; continue; }
if(s2[l2][r2]-s2[l1-1][r2]<=K) { r2--; continue; }
ans=2147483647; break;
}
return ans;
}
inline int Tanu(int u)
{
int l1=1, l2=n, r1=1, r2=m, ans=0;
while(l1<=l2 && r1<=r2)
{
ans++;
if(s2[l2][r1]-s2[l1-1][r1]<=K) { r1++; continue; }
if(s2[l2][r2]-s2[l1-1][r2]<=K) { r2--; continue; }
if(s1[l1][r2]-s1[l1][r1-1]<=K && l1<u) { l1++; continue; }
if(s1[l2][r2]-s1[l2][r1-1]<=K) { l2--; continue; }
ans=2147483647; break;
}
return ans;
}

int ans=2147483647;
int main()
{
K=read(); m=read(); n=read();
for(RG int i=1;i<=n;i++) for(RG int j=1;j<=m;j++) g[i][j]=read(), s2[i][j]=s2[i-1][j]+g[i][j], s1[i][j]=s1[i][j-1]+g[i][j];
for(RG int i=1;i<=n;i++) ans=min(ans, Tanu(i));
for(RG int i=1;i<=m;i++) ans=min(ans, Tanl(i));
printf("%d\n", ans);
return 0;
}

总结

在开考前,李老师跟我们说这套题很简单,但是我并没有想出稍微难一点的两道题,而只打了骗分暴力。
看来我以后还要多加练习,开拓思路。

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×