真是非常可惜……一是阈值不会用;二是期望不熟悉。考场的idea已经直逼标算了。
题目描述
乐滋滋经常参加各种拍卖会,前不久,他收到了来自滋滋国最大的商店——奥义商店的拍卖会邀请函。
为了让拍卖会看起来更加奥妙重重,奥义商店设置了一个极为复杂的拍卖规则:一共 $n$ 个物品排成一排,第 $i$ 个物品价格是 $v_i$ 。对于一次拍卖,商店会指定 $t$ 种颜色,并对每种颜色指定一个数目 $c_j$ 满足 $\sum_{j=1}^{t}c_j=n-1$ ,另外还会指定一个下标 $k$ 和一个公差 $d$ 。
买家需要给第 $k$ 个物品染上一种颜色(在 $t$ 种颜色中选择一种)。接着,把剩下的 $n-1$ 个物品随机染成 $t$ 种颜色之一,并保证这 $n-1$ 个物品中第 $j$ 种颜色的恰好有 $c_j$个。
买家需要购买的物品按这样的方式计算:找到 $k$ 所在的最长的以 $d$ 为公差的等差数列 $a_x=kx+d(x\in [L,R],L\le 0 \le R)$ 满足其中所有物品都与第 $k$ 个物品同色。买家需要买下这个等差数列中的所有物品,显然花费就是 $\sum_{x=L}^{R}v_{k+xd}$ 。
乐滋滋最近出现了点“经济危机”,希望你能帮他给第 $k$ 个物品选择合适的颜色,以此来最小化他花费的期望,你只需要输出这个期望即可。
当然商品的价格是可能出现变动的,你需要维护这些变化。
数据范围
$1 \leq n,m \leq 10^5$
题目分析
我考试时候在想什么,为什么现在看这题觉得好简单啊……
不知道过几天还会不会做
这里是一些零碎的做完题的想法:既然要求期望代价最小,那么每一次第$k$个物品应该染成$min$ $c_j$。然后把期望拆开,就是每一个位置被统计到的概率乘以它的权值。这个概率不是很难算,就是从前一个位置转移过来的概率。
以上就是一个正确但是非常慢的想法。
算法的复杂度是$O(mn/d)$的,当$d$比较小时,复杂度就接近$O(nm)$。
可能会说:那么把d阈值一下就好了嘛,d小的时候用$s[d][j]$表示模$d$为j的vi总和。然而只阈值d只能在$t=1$时有效。若$t!=1$还是需要枚举计算期望。然后非常玄学的精度阈值就来了:
但是要注意!最好不要形如 if (sta*v[k-i*d] < 1e-13) break; 的精度阈值。因为尽管看上去增量已经很小了,但实际上是不能这样“意会”的。
所以以上就是这题的两个玄学阈值。
1 #include<cstdio> 2 #include<cctype> 3 const int maxn = 100035; 4 5 double ans,sta,s[103][103]; 6 int n,m,t,k,d,mn; 7 int v[maxn],c[maxn]; 8 9 int read() 10 { 11 char ch = getchar(); 12 int num = 0; 13 bool fl = 0; 14 for (; !isdigit(ch); ch=getchar()) 15 if (ch=='-') fl = 1; 16 for (; isdigit(ch); ch=getchar()) 17 num = (num<<1)+(num<<3)+ch-48; 18 if (fl) num = -num; 19 return num; 20 } 21 int min(int a, int b){return a<b?a:b;} 22 int main() 23 { 24 freopen("lzz.in","r",stdin); 25 freopen("lzz.out","w",stdout); 26 n = read(), m = read(); 27 for (int i=1; i<=n; i++) 28 { 29 v[i] = read(); 30 for (int j=1; j<=100; j++) 31 s[j][i%j] += v[i]; 32 } 33 while (m--) 34 { 35 int opt = read(); 36 if (opt==1){ 37 int x = read(), y = read(); 38 for (int j=1; j<=100; j++) 39 s[j][x%j] += y-v[x]; 40 v[x] = y; 41 }else{ 42 t = read(), k = read(), d = read(), mn = n-1, ans = v[k], sta = 1.0; 43 for (int i=1; i<=t; i++) c[i] = read(), mn = min(mn, c[i]); 44 if (t==1){ 45 if (d<=100) ans = s[d][k%d]; 46 else{ 47 for (int i=k+d; i<=n; i+=d) ans += v[i]; 48 for (int i=k-d; i>=1; i-=d) ans += v[i]; 49 } 50 }else{ 51 for (int i=1; i<=100&&k+i*d<=n; i++) 52 { 53 sta *= 1.0*(mn-i+1)/(n-i); 54 ans += sta*v[k+i*d]; 55 } 56 sta = 1.0; 57 for (int i=1; i<=100&&k-i*d>=1; i++) 58 { 59 sta *= 1.0*(mn-i+1)/(n-i); 60 ans += sta*v[k-i*d]; 61 } 62 } 63 printf("%.6lf\n",ans); 64 } 65 } 66 return 0; 67 }
鬼知道我考试时候在写什么……
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include<bits/stdc++.h> const int maxn = 100035; double ans,sta; int n,m,t,k,d,mn; int v[maxn],c[maxn]; int read() { char ch = getchar(); int num = 0; bool fl = 0; for (; !isdigit(ch); ch=getchar()) if (ch=='-') fl = 1; for (; isdigit(ch); ch=getchar()) num = (num<<1)+(num<<3)+ch-48; if (fl) num = -num; return num; } int main() { freopen("lzz.in","r",stdin); freopen("lzz.out","w",stdout); n = read(), m = read(); for (int i=1; i<=n; i++) v[i] = read(); while (m--) { int opt = read(); if (opt==1){ int x = read(), y = read(); v[x] = y; } else{ t = read(), k = read(), d = read(), mn = n-1, ans = v[d], sta = 1.0; for (int i=1; i<=t; i++) c[i] = read(), mn = std::min(mn, c[i]); for (int i=2; i<=c[i]+1; i++) { sta *= (c[i]-i+2.0)/(n-i+1.0); ans += sta*v[i]; } printf("%.6lf\n",ans); } } return 0; }
END