今天的出题人游戏似乎玩得很多呢qwq
T1 loopers
我想永远当你最珍贵的宝物………
首先,我永远恨概率期望!!!!!!其实就是因为我太蒻了,做不来题
题目说了,下一次走到一个点 i 的概率为
a
i
/
∑
没
选
过
的
点
j
a
j
a_i / \sum_{没选过的点j}a_j
ai/∑没选过的点jaj,所以我们得到在第一个点之前选到第 i 个点的概率为:
a
i
a
1
+
a
i
\frac{a_i}{a_1+a_i}
a1+aiai。再乘上这个节点的值
a
i
a_i
ai 就是这个点的数学期望。然后我们把所有点的期望加起来就得到总的期望:
E
=
∑
i
=
2
n
a
i
2
a
i
+
a
1
+
a
1
E = \sum_{i=2}^{n} \frac{a_i^2}{a_i+a_1} + a_1
E=i=2∑nai+a1ai2+a1
我们因为要对答案取模,所以要算出 1 ~
a
m
a
x
a_{max}
amax 的所有逆元,所以我们用线性递推的方法。最后的时间复杂度是
O
(
1
0
8
+
n
)
O(10^8 + n)
O(108+n)
#include<bits/stdc++.h>
using namespace std;
#define in read()
#define MAXN 10001000
#define MAXA int(1e8 + 100)
#define MOD int(1e9 + 7)
int read(){
int x = 0; char c = getchar();
while(c < '0' or c > '9') c = getchar();
while('0' <= c and c <= '9'){
x = x * 10 + c - '0'; c = getchar();
}
return x;
}
int n = 0;
int a[MAXN] = { 0 };
int inv[MAXA] = { 0 };
int main(){
n = in;
for(int i = 1; i <= n; i++){
a[i] = in;
}
inv[1] = 1;
for(int i = 2; i <= 100000000; i++)
inv[i] = (1ll * MOD - MOD / i) * inv[MOD % i] % MOD;
long long ans = 0;
for(int i = 2; i <= n; i++){
ans = (ans + 1ll * a[i] * a[i] % MOD * inv[a[i] + a[1]] % MOD) % MOD;
}
ans += a[1]; ans %= MOD;
printf("%lld\n", ans);
return 0;
}
T2 rewrite
纵使新月的微光、明灭的星辰、街头的灯火全然散尽
纵使黑暗吞噬一切,我仍不会离你而去
本题正解要用到斜率优化 DP,我太蒻了,不会打斜率优化不过最近正在学,浅谈斜率优化,等学会了再回来打正解。所以这里先打一个50分的暴力。
首先让我们看看 main 函数里的东西(输入部分):
n = in; k = in;
for(int i = 1; i <= n; i++){
a[i] = in;
if(a[i] == 0){
// idx[i] 是第 i 个 0 的位置 cnt 是 0 的数量
idx[++cnt] = i;
}
}
首先第二个点先说这个因为这个最简单,满足所有的的位置都不是 0 ,所以直接 nlogn 暴力树状数组求逆序对就好了。
inline ll lowbit(ll x){
return x & -x;
}
inline void add(ll index, ll val){
for(; index <= n; index += lowbit(index)){
c[index] += val;
}
}
inline ll query(ll x){
ll ans = 0;
for(; x; x -= lowbit(x)){
ans += c[x];
}
return ans;
}
ll inverPair(ll n, ll a[]){
for(ll i = 1; i <= 4 * k; i++) c[i] = 0;
ll ans = 0;
for(ll i = n; i >= 1; i--){
ans += query(a[i] - 1ll);
add(a[i], 1ll);
}
return ans;
}
// 下面的内容在 main() 函数里
if(cnt == 0){ // 0 的数量为 0
printf("%lld\n", inverPair(n, a));
return 0;
}
然后是第一个点打最暴力的暴力也最多 8 8 8^8 88 可以过,那就直接 dfs 走起。
ll dfs(ll x){
ll ans = 0;
if(x > cnt - 1ll){
return inverPair(n, a);
}
ll p = -1;
for(ll i = 1; i <= k; i++){
a[idx[x + 1ll]] = i;
ans = max(ans, dfs(x + 1ll));
a[idx[x + 1ll]] = 0;
}
return ans;
}
// 下面的内容在 main() 函数里
if(n <= 8 and k <= 8){
printf("%lld\n", dfs(0));
}
第三个点只有一个位置是 0,所以暴力枚举的话就是 klogn,能过,那就枚举!
// 下面的内容在 main() 函数里
if(cnt == 1){ // 只有一个 0
ll ans = 0;
for(ll i = 1ll; i <= k; i++){
a[idx[1]] = i;
ans = max(ans, inverPair(n, a));
a[idx[1]] = 0;
}
printf("%lld\n", ans);
return 0;
}
第四和第五个点,全是0,这个就有点点麻烦了,但其实也还好。显然我们要填写的数段一定是不上升的就比如说若干个 k 然后若干个 k-1 然后若干个 k-2 … 最后若干个 1。而且我们要让每一段尽量平均。所以我们每段取
⌊
n
k
⌋
\lfloor \frac{n}{k} \rfloor
⌊kn⌋ 个数,剩下的 n mod k 个数就都填 1就行了。所以我们的答案是:
a
n
s
=
∑
i
=
1
k
−
1
⌊
n
k
⌋
(
n
−
i
⌊
n
k
⌋
)
ans = \sum_{i=1}^{k-1}\lfloor \frac{n}{k} \rfloor \Big( n - i \lfloor \frac{n}{k} \rfloor \Big)
ans=i=1∑k−1⌊kn⌋(n−i⌊kn⌋)
// 下面的内容在 main() 函数里
if(cnt == n){
ll ans = 0;
for(ll i = 1ll; i <= k - 1; i++){
ans += (n / k) * (n - i * (n / k));
}
printf("%lld\n", ans);
return 0;
}
愉快的拿到 50 分。
T3 pockets
眼前这个少女的笑容,和她这副喃喃自语的样子,仿佛与我记忆深处的某个东西产生了共鸣。
月光洒落在少女的侧脸上,居然让我感到一种几乎要落泪的怀念感。
那一定是……
算法标签:差分,线段树,哈希
很好,不会!不过我还是打了一个线段树,本来想着骗个 10 分就好了,结果骗出来 35 分qwq。
下面是骗分代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define in read()
#define MAXN 500500
int read(){
int x = 0; char c = getchar();
while(c < '0' or c > '9') c = getchar();
while('0' <= c and c <= '9'){
x = x * 10 + c - '0'; c = getchar();
}
return x;
}
int n = 0; int q = 0; int P = 0;
int a[MAXN] = { 0 };
struct SegTree{
int l; int r;
ll dat; int lazy;
}t[4 * MAXN];
int ls(int p){
return p << 1;
}
int rs(int p){
return p << 1 | 1;
}
void pushup(int p){
t[p].dat = t[ls(p)].dat + t[rs(p)].dat;
}
void pushdown(int p){
t[ls(p)].dat += (ll)t[p].lazy * (ll)(t[ls(p)].r - t[ls(p)].l + 1);
t[rs(p)].dat += (ll)t[p].lazy * (ll)(t[rs(p)].r - t[rs(p)].l + 1);
t[ls(p)].lazy += t[p].lazy; t[rs(p)].lazy += t[p].lazy;
t[p].lazy = 0;
}
void build(int p, int l, int r){
t[p].l = l; t[p].r = r;
if(l == r){
t[p].dat = a[l] % P; return;
}
int mid = (l + r) >> 1;
build(ls(p), l, mid);
build(rs(p), mid + 1, r);
pushup(p);
}
void update(int p, int l, int r, int val){
if(l <= t[p].l and t[p].r <= r){
t[p].dat += (ll)(t[p].r - t[p].l + 1) * (ll)val;
t[p].lazy += val; return;
}
pushdown(p);
int mid = (t[p].l + t[p].r) >> 1;
if(l <= mid) update(ls(p), l, r, val);
if(r > mid) update(rs(p), l, r, val);
pushup(p);
}
ll query(int p, int l, int r){
if(l <= t[p].l and t[p].r <= r)
return t[p].dat;
pushdown(p);
ll ans = 0;
int mid = (t[p].l + t[p].r) >> 1;
if(l <= mid) ans += query(ls(p), l, r);
if(r > mid) ans += query(rs(p), l, r);
return ans;
}
int main(){
n = in; q = in; P = in;
for(int i = 1; i <= n; i++){
a[i] = in;
}
build(1, 1, n);
for(int i = 1; i <= q; i++){
int op = in; int l = in; int r = in; int k = in;
if(op == 1){
update(1, l, r, k);
}
else if(op == 2){
bool is = true;
while(k--){
if((query(1, l, l) % P) != (query(1, r, r) % P)){
is = false; break;
}
l++; r++;
}
if(is) printf("ye5\n");
else printf("n0\n");
}
}
return 0;
}
T4 dohnadohna
一起来干坏事吧
开玩笑,谁会 T4 啊 打了一个 30 分做法,如果没有删除节点的话,就直接在有向无环图 (DAG) 上跑背包就好了。然后暴力枚举每个点并删除,复杂度 O(nmk)。