http://codeforces.com/contest/1330

B. Dreamoon Likes Permutations

 

题意:给定一个数列,分成两部分,问有几种分割方式使得两部分都是一个排列。

遍历数列,判断左边是否是排列,右边是否是排列。

是排列则不能有相同的数,所以要记录vis,如果已知都不相同,且个数又是一定的,如果是排列,那么总和一定是唯一的,先预处理出每个排列的和,再直接判断即可。

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
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 2e5 + 10;
const int INF = 0x3f3f3f3f;
int T;
int a[N], n, c1[N], c2[N], vis[N];
ll sum[N];
void ck() {
fill(vis, vis + n + 1, 0);
ll tmp = 0;
for (int i = 1; i <= n; i++) {
if (vis[a[i]])break;
vis[a[i]] = 1;
tmp += a[i];
if (sum[i] == tmp)c1[i] = 1;
}
tmp = 0;
fill(vis, vis + n + 1, 0);
for (int i = n; i >= 1; i--) {
if (vis[a[i]])return;
vis[a[i]] = 1;
tmp += a[i];
if (sum[n - i + 1] == tmp)c2[i] = 1;
}
}
typedef pair<int, int>pii;
vector<pii>vc;
int main() {
scanf("%d", &T);
for (int i = 1; i < N; i++)sum[i] = sum[i - 1] + i;
while (T--) {
scanf("%d", &n);
for (int i = 1; i <= n; i++)scanf("%d", &a[i]);
fill(c1, c1 + n + 1, 0);
fill(c2, c2 + n + 1, 0);
ck();
vc.clear();
for (int i = 1; i <= n; i++)
if (c1[i] && c2[i + 1])
vc.push_back(pii(i, n - i));
printf("%d\n", (int)vc.size());
for (pii p : vc)printf("%d %d\n", p.first, p.second);
}
return 0;
}

 

C. Dreamoon Likes Coloring

 

题意:有长为m的操作序列,按顺序选择,任选一个位置,给后面长为 lil_i 的区间图上颜色 ii,要求最终每个位置都有颜色,且每种颜色都有,求每次操作选的位置。

贪心模拟

每次涂完要保证后面的长度撑满了能布满后面的区间。

每次判断如果当前操作放不下,或者后面的撑满了也填不满,则不行。

就是细节比较烦,思路很直接。

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
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 2e5 + 10;
const int INF = 0x3f3f3f3f;
int n, m;
ll l[N];
ll ans[N];
ll sum[N];
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; i++) {
scanf("%lld", &l[i]);
sum[i] = sum[i - 1] + l[i];
}
if (sum[m] < n || l[m] + m - 1>n) { puts("-1"); return 0; }
ans[1] = 1;
for (int i = 2; i <= m; i++) {
ans[i] = max(ans[i - 1] + 1, n - (sum[m] - sum[i - 1]) + 1);
if (ans[i - 1] + l[i - 1] - 1 + sum[m] - sum[i - 1] < n || l[i] + ans[i - 1] - 1 >= n) {
puts("-1"); return 0;
}
}
for (int i = 1; i <= m; i++)printf("%lld%s", ans[i], i == m ? "\n" : " ");
return 0;
}

 

D. Dreamoon Likes Sequences

 

题意:构造数列,满足严格递增,且最大值小于等于d,最小值大于等于1,并且前缀异或和也严格递增,问有几种构造方式。

dp+组合数学

首先要发现数列的每个数最高位一定要不同,可以从第一二个元素如果最高位相同,则异或和一定更小来判断,再类推到后面。

再枚举数列长度,确定长度n的情况下,就是要选出n个位置作为最高位,一个数字确定最高位后,可能的取值个数就是 2i2^i,那么用乘法原理把每个数可能的取值个数乘起来,就是当前最高位的情况下,数列的个数。再用加法原理得到其它最高位情况的值。再改变数列长度,得到最终的结果。

问题就在于已知要取n个,有几种数列,可以dp来做,dp[i][j]dp[i][j] 表示前i位,取j位的情况数。

dp[i][j]=(dp[i1][j]+dp[i1][j1]2i1)dp[i][j]=(dp[i-1][j]+dp[i-1][j-1]*2^{i-1})

注意要满足小于等于d,所以取d的最高位时要特殊处理。

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
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 2e5 + 10;
const int INF = 0x3f3f3f3f;
int T;
ll dp[40][40], d, m, p[40];
int main() {
scanf("%d", &T);
while (T--) {
scanf("%lld%lld", &d, &m);
ll n = 0, tmp = d;
while (tmp) {
n++;
tmp >>= 1;
}
ll rs = ((d ^ (1ll << (n - 1))) + 1) % m;
p[0] = 1;
for (int i = 1; i <= n; i++)p[i] = p[i - 1] * 2 % m;
memset(dp, 0, sizeof(dp));
for (int i = 0; i <= n; i++)dp[i][0] = 1;
for (int i = 1; i <= n; i++) {
ll tp = (i == n ? rs : p[i - 1]);
for (int j = 1; j <= i; j++)
dp[i][j] = (dp[i - 1][j] + dp[i - 1][j - 1] * tp % m) % m;
}
ll ans = 0;
for (int i = 1; i <= n; i++)ans = (ans + dp[n][i]) % m;
printf("%lld\n", ans);
}
return 0;
}

 

E. Drazil Likes Heap

 

题意:有一棵树每个节点大于左右儿子的高度位h的满二叉树,要求删除成为高度为g的满二叉树,且剩下的值之和最小。

贪心

删除的时候把重儿子提上来,再依次提重链,直到最后,可以发现删除后仍然是每个节点大于左右儿子,也就是个大顶堆,那么每次都删树根一定能使得剩下的值和最小,但是可能最终不是个满二叉树。

那么就要尽可能地删除根,直到根的重链长度小于等于g,这里的重链指按照点权值,而非结点数,所以重链的长度可以始终logn得到。

对于所有节点都是这样,从上向下遍历,尽可能地删除,直到重链的长度小于等于g,由于左右子树之间没有影响,所以左右儿子都要dfs。

每次删除和查询长度的复杂度都为 logn\log n,复杂度为 O(nlogn)\mathcal{O}(n\log 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
37
38
39
40
41
42
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 1e7 + 10;
const int INF = 0x3f3f3f3f;
int T;
int h, g, a[N];
#define lson (u<<1)
#define rson (u<<1|1)
#define bson (a[lson]>a[rson]?lson:rson)
int dep(int u) {
if (a[u] == 0)return 0;
return dep(bson) + 1;
}
void del(int u) {
if (!a[u])return;
a[u] = a[bson];
del(bson);
}
vector<int>vc;
void dfs(int u, int d) {
if (!a[u])return;
while (dep(u) + d > g)vc.push_back(u), del(u);
dfs(lson, d + 1); dfs(rson, d + 1);
}
int main() {
scanf("%d", &T);
while (T--) {
vc.clear();
scanf("%d%d", &h, &g);
fill(a, a + (1 << (h + 1) + 2), 0);
for (int i = 1; i < (1 << h); i++)
scanf("%d", &a[i]);
dfs(1, 0);
ll sum = 0;
for (int i = 1; i < (1 << g); i++)sum += a[i];
printf("%lld\n", sum);
for (int v : vc)printf("%d ", v);
puts("");
}
return 0;
}