8.09集训

上午

考试,被初中的dalao踩爆了 # 下午

讲课,主要讲的树,树形dp,树剖,dfn序,树的直径,换根dp,树形背包各种树上的操作

晚上

听学长的“杂记”

外加改题:

第一题

\(n\)个宽度为1的点,高度分别为\(1\sim n\)的排列,我们将这些条按照某种排列顺序从左到右连接在一起就形成了一个柱状图

(我也觉得特别丑.....)

\(n\)为8的时候,排列为:\(6,2,3,1,8,4,5,7\),如上图所示

现给定\(n\)\(x\),求\(1\text~n\)这个排列是否可以以某种方式排序之后,得到x这个排水量,其中\(1 \leq n \leq 1e6, 1 \leq x \leq 1e15\)

知道正解之后感觉就是个普及组的T1,可是当时的我,把问题搞复杂了。。。。

最大情况是\(n\)\(n-1\)在两头的位置,中间排放\(1\sim n - 2\),此时的答案为\(maxx=1+...+n-2\),当\(x>maxx\)时显然不行。

而当\(x \leq maxx\)时一定可以,我们强制\(n\)\(n-1\)是容器的边,就相当于选择\(1\text~n-2\)中的数使得其组成\(x\)

可以从大往小选,尽量选大的,选不了直接跳过,显然这种方法是正确的,没选的排序放到容器外面就保证不会积水。时间复杂度为\(O(n)\)

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
#include <bits/stdc++.h>
#define LL long long
#define debug
using namespace std;

const int N = 1e6+66;

int n, cnt;
LL x;
int a[N];

inline int thestars() {
cin >> n >> x;
int m = n - 2, maxx = 0;
for (int i = m; i; -- i) {
if (x >= i) {
x -= i;
a[n - 1 - i] = i;
}
}
if (x) puts("-1");
else {
for (int i = 1; i <= m; ++ i)
if (!a[i]) cout << i << ' ';
cout << n << ' ';
for (int i = 1; i <= m; ++ i)
if (a[i]) cout << i << ' ';
cout << n-1;
}
return 0;
}

int youngore = thestars();

signed main() {;}

第二题

我懒...

以下是ghj1222dalao的讲解:(让我们来一起%%%ghj1222)

考虑到N很小,限制答案的值只有两点之间的曼哈顿距离,题目可以转化为:

给定一个N个点的完全图(无向图)(边权为原格点图的曼哈顿距离)

你要满足尽量大的边,使得这个边所连接的两个点分属不同的集合、

你就从大往小贪心,尽量满足大的边,用一个带权并查集维护一个连通块内点与点之间必须在同一集合/必须不在同一集合,

直到你扫到一条边,边连接两个点在同一集合且这条边与之前记录信息冲突,这条边就不能删,它的长度就是答案

至于统计方案数,并查集每个连通块中都能分成两个集合,你要把他分成A和B,就有两种情况,最后方案数就是\(2^{连通块个数}\)

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
#include <cstdio>
#include <algorithm>
#define N 5010
using namespace std;
int px[N] , py[N] , head[N << 2] , vx[N * N / 2] , vy[N * N / 2] , nxt[N * N / 2] , cnt , f[N] , r[N];
inline void add(int x , int y , int z)
{
vx[++cnt] = x , vy[cnt] = y , nxt[cnt] = head[z] , head[z] = cnt;
}
int find(int x)
{
if(x == f[x]) return x;
int t = f[x];
f[x] = find(t) , r[x] ^= r[t];
return f[x];
}
int main()
{
int n , i , j , tx , ty , ans = 1 , num , tmp;
scanf("%d" , &n) , num = n;
for(i = 1 ; i <= n ; i ++ )
{
scanf("%d%d" , &px[i] , &py[i]) , f[i] = i , r[i] = 0;
for(j = 1 ; j < i ; j ++ )
add(j , i , abs(px[i] - px[j]) + abs(py[i] - py[j]));
}
for(i = 20000 ; ~i ; i -- )
{
tmp = 0;
for(j = head[i] ; j ; j = nxt[j])
{
tx = find(vx[j]) , ty = find(vy[j]);
if(tx != ty) f[tx] = ty , r[tx] = r[vx[j]] ^ r[vy[j]] ^ 1 , tmp ++ ;
else if(r[vx[j]] == r[vy[j]]) break;
}
if(j) break;
num -= tmp;
}
printf("%d\n" , i);
while(num -- ) ans = ans * 2 % 1000000007;
printf("%d\n" , ans);
return 0;
}

第三题

又是一道码农题

数据量不超过15

显然是模拟:

大致思路:对于每一轮都扫一遍整个图形,最多的复杂度\(O(15*15*10)\),每次都找出靠左靠下的联通块,然后消掉,把他上边的玩意往下掉,他右边的玩意往左挪窝

每次都这么搞,直到全图没有块或者不能消为止:

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
#include <cstdio>
#include <vector>
#include <cstring>
#include <utility>
#define N 20
using namespace std;
typedef pair<int , int> pr;
vector<pr> v[N * N];
int n , m , a[N][N] , b[N][N] , c[N][N] , tx[N] , ty , vis[N][N];
char str[N];
inline int ctoi(char c)
{
return c == 'R' ? 1 : c == 'G' ? 2 : 3;
}
inline char itoc(int a)
{
return a == 1 ? 'R' : a == 2 ? 'G' : 'B';
}
void floodfill(int x , int y , int k)
{
v[k].push_back(pr(x , y)) , vis[x][y] = 1;
if(x > 1 && a[x - 1][y] == a[x][y] && !vis[x - 1][y]) floodfill(x - 1 , y , k);
if(x < n && a[x + 1][y] == a[x][y] && !vis[x + 1][y]) floodfill(x + 1 , y , k);
if(y > 1 && a[x][y - 1] == a[x][y] && !vis[x][y - 1]) floodfill(x , y - 1 , k);
if(y < m && a[x][y + 1] == a[x][y] && !vis[x][y + 1]) floodfill(x , y + 1 , k);
}
int main()
{
int T , cc;
scanf("%d" , &T);
for(cc = 1 ; cc <= T ; cc ++ )
{
int i , j , cnt , sum = 0 , step;
vector<pr>::iterator it;
scanf("%d%d" , &n , &m) , cnt = n * m;
for(i = n ; i >= 1 ; i -- )
{
scanf("%s" , str + 1);
for(j = 1 ; j <= m ; j ++ ) a[i][j] = ctoi(str[j]);
}
printf("Game %d:\n\n" , cc);
for(step = 1 ; cnt ; step ++ )
{
memset(vis , 0 , sizeof(vis));
int mx = 0 , pi , id = 0;
for(j = 1 ; j <= m ; j ++ )
{
for(i = 1 ; i <= n ; i ++ )
{
if(a[i][j] && !vis[i][j])
{
id ++ , v[id].clear() , floodfill(i , j , id);
if(mx < (int)v[id].size()) mx = v[id].size() , pi = id;
}
}
}
if(mx < 2) break;
printf("Move %d at (%d,%d): removed %d balls of color %c, got %d points.\n" , step , v[pi][0].first , v[pi][0].second , mx , itoc(a[v[pi][0].first][v[pi][0].second]) , (mx - 2) * (mx - 2));
sum += (mx - 2) * (mx - 2) , cnt -= mx;
for(it = v[pi].begin() ; it != v[pi].end() ; it ++ ) a[it->first][it->second] = 0;
memset(b , 0 , sizeof(b)) , memset(tx , 0 , sizeof(tx)) , memset(c , 0 , sizeof(c)) , ty = 0;
for(i = 1 ; i <= n ; i ++ )
for(j = 1 ; j <= m ; j ++ )
if(a[i][j] != 0)
b[++tx[j]][j] = a[i][j];
for(j = 1 ; j <= m ; j ++ )
{
if(tx[j] != 0)
{
ty ++ ;
for(i = 1 ; i <= n ; i ++ ) c[i][ty] = b[i][j];
}
}
for(i = 1 ; i <= n ; i ++ )
for(j = 1 ; j <= m ; j ++ )
a[i][j] = c[i][j];
}
if(!cnt) sum += 1000;
printf("Final score: %d, with %d balls remaining.\n\n" , sum , cnt);
}
return 0;
}