其他分享
首页 > 其他分享> > 「CometOJ」Contest #11

「CometOJ」Contest #11

作者:互联网

Link

Aeon

显然字典序最大就是把最小的字母放在最后

Business

[动态规划]

简单dp

dp[i][j]dp[i][j]dp[i][j]表示到第iii天,当前有jjj块钱,最后返还的钱最多为多少

完全背包转移

Celebration

Description

有一个 ,求把它分成三段,使得每一段内无重复元素,且三段长度可以作为某个三角形的三边的方案数。

一个拆分方案可以看作一个三元组 (a,b,c)(a,b,c)(a,b,c),其中 0<a<b<cn0lt alt b lt c le n0<a<b<c≤n,表示在第 a,b,ca,b,ca,b,c个位置之前断开。两个拆分不同当且仅当其对应的三元组不同。

n2×106nle 2times10^6n≤2×10​6​​

Solution

[计数] [树状数组]

定义长度不超过 n12frac{n-1}{2}​2​​n−1​​ ,且不含重复颜色的段为合法的段。记 prexpre_xpre​x​​ 为以 为右端点的合法段最远的左端点,nxtxnxt_xnxt​x​​ 为以 xxx 为左端点的合法段最远的右端点。

先枚举题目中的aaa,那么b(a,nxta+1]bin(a, nxt_a + 1]b∈(a,nxt​a​​+1]。在确定了a,ba, ba,b的位置后,合法的ccc位于(b,nxtb+1](b, nxt_b + 1](b,nxt​b​​+1]和[prea,n][pre_a, n][pre​a​​,n]的交集中

注意,这里的preapre_apre​a​​必须是大于aaa的,即绕了一圈绕到右边去。否则一定不合法

可以用树状数组维护:

从左往右枚举aaa,把合法的bbb对应的(b,nxtb+1](b, nxt_b + 1](b,nxt​b​​+1]这段区间在树状数组中+1;查询就直接查[prea,n][pre_a, n][pre​a​​,n]的区间和;在离开aaa的时候把(a,nxta+1](a, nxt_a + 1](a,nxt​a​​+1]区间-1

Code
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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126



#define x first
#define y second
#define y1 Y1
#define y2 Y2
#define mp make_pair
#define pb push_back
#define DEBUG(x) cout << #x << " = " << x << endl;

using namespace std;

typedef long long LL;
typedef pair <int, int> pii;

template <typename T> inline int (T &a, T b) { return a < b ? a = b, 1 : 0; }
template <typename T> inline int Chkmin (T &a, T b) { return a > b ? a = b, 1 : 0; }
template <typename T> inline T read ()
{
T sum = 0, fl = 1; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') fl = -1;
for (; isdigit(ch); ch = getchar()) sum = (sum << 3) + (sum << 1) + ch - '0';
return sum * fl;
}

inline void proc_status ()
{
ifstream t ("/proc/self/status");
cerr << string (istreambuf_iterator <char> (t), istreambuf_iterator <char> ()) << endl;
}

const int Maxn = 2e6 + 10;

int N;
int A[Maxn];
int vis[Maxn];
int L[Maxn], R[Maxn];

inline int fix (int x) { return ((x - 1) % N + N) % N + 1; }

namespace BIT
{
struct bit
{
LL sum[Maxn];
inline void add (int x, int val) { for (; x <= N; x += x & (-x)) sum[x] += val; }
inline LL query (int x) { LL ans = 0; for (; x; x -= x & (-x)) ans += sum[x]; return ans; }
} A, B;

inline void update (int x, int y, int val)
{
if (x > y) return ;
A.add (x, val * x), A.add (y + 1, -val * (y + 1));
B.add (x, val), B.add (y + 1, -val);
}

inline LL query (int x) { return B.query (x) * (x + 1) - A.query (x); }

inline LL query (int x, int y) { if (x > y) return 0; return query (y) - query (x - 1); }
}

inline void Init ()
{
int r = 0;
for (int i = 1; i <= N; ++i)
{
while (r < N && !vis[A[r + 1]]) ++r, ++vis[A[r]];
R[i] = min (r, i + (N - 1) / 2 - 1);
if (vis[A[i]]) --vis[A[i]];
}

memset (vis, 0, sizeof vis);
int l = 1;
while (!vis[A[fix (l - 1)]]) l = fix (l - 1), ++vis[A[l]];
L[1] = fix (max (l, N - (N - 1) / 2 + 1));

for (int i = 2; i <= (N - 1) / 2; ++i)
{
while (vis[A[i - 1]]) --vis[A[l]], l = fix (l + 1);
if (l < i) break;
L[i] = max (l, fix ((i - 1 + N) - (N - 1) / 2 + 1));
++vis[A[i - 1]];
}
}

inline void Solve ()
{
Init ();

LL ans = 0;
int p = 1;
for (int i = 1; i <= N; ++i)
{
if (L[i] < i) break;
while (p < N && p + 1 <= R[i] + 1)
{
++p;
BIT :: update (p + 1, R[p] + 1, 1);
}
ans += BIT :: query (L[i], N);
BIT :: update ((i + 1) + 1, R[i + 1] + 1, -1);
}

cout << ans << endl;
}

inline void Input ()
{
N = read<int>();
for (int i = 1; i <= N; ++i) A[i] = read<int>();
}

int main()
{

#ifdef hk_cnyali
freopen("C.in", "r", stdin);
freopen("C.out", "w", stdout);
#endif

Input ();
Solve ();

return 0;
}

Disaster

[kruskal重构树]

kruskal重构树模板题

Effort

Description

mmm种数据结构(可把数据结构想像成游戏中的种族),第 iii种有 aia_ia​i​​个,每个可给敌人造成至少 111 次,至多 bib_ib​i​​次伤害。有n nn名敌人,每人承担至少一次伤害。求总情况数模 998244353998244353998244353的值

数据结构两两不同 (同种的任两个也不同),敌人两两不同。

两种方案不同当且仅当某个数据结构造成的伤害不同,或某个敌人受到的伤害不同。

n×m105,ai105,bi <998244353n times mle 10^5, a_ile 10^5, b_i < 998244353n×m≤10​5​​,a​i​​≤10​5​​,b​i​​ <998244353

Solution

[组合数学] [生成函数] [多项式] [NTT]

注意到每个敌人的伤害是无序的,即这个敌人在被第几次攻击到都是一样的,而其他限制都是有序

于是可以钦定攻击顺序和受到伤害的顺序。形象地理解就是先把所有数据结构按顺序摆一排,确定每数据结构攻击多少次,这样就确定出一个攻击序列。再在这个攻击序列上插n1n-1n−1个板就是这个攻击序列方案数

看上去这是由两个部分构成的(先确定攻击序列,再插板),但实际上可以同时算

Fi(x)F_i(x)F​i​​(x)表示一个iii种数据结构插板方案的生成函数,它的kkk次项系数表示插kkk个板的方案数。那么[xk]Fiai(x)displaystyle [x^k]F_i^{a_i}(x)[x​k​​]F​i​a​i​​​​(x)就是在第iii种里插kkk个板的方案数了


考虑如何求Fi(x)F_i(x)F​i​​(x)

kkk项系数其实就是

(1k)+(2k)++(bik) binom{1}{k} + binom{2}{k} + cdots + binom{b_i}{k} (​k​1​​)+(​k​2​​)+⋯+(​k​b​i​​​​)

枚举这个数据结构攻击ttt次

那么就相当于有ttt个空位(最后一个位置也是空位,但最后一个数据结构不是,需要单独考虑),插kkk个板,就是(tk)binom{t}{k}(​k​t​​)

发现除了k=0k=0k=0之外,都是是杨辉三角一列的之和,就等于(bi+1k+1)binom{b_i + 1}{k + 1}(​k+1​b​i​​+1​​)

因为bib_ib​i​​很大,不能直接算,但是kkk比较小,所以可以先O(1)O(1)O(1)计算出k=1k=1k=1时的值,然后O(1)O(1)O(1)递推下一个kkk的值


由于只有n1n-1n−1个板,所以多项式长度始终不超过n1n-1n−1。直接做多项式快速幂即可,再把mmm个多项式依次合起来

Code
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
大专栏

标签:11,return,Contest,int,vis,query,inline,数据结构,CometOJ
来源: https://www.cnblogs.com/lijianming180/p/12361113.html