标签:
A. 奥义商店
有一个商店,n个物品,每个物品有一个价格和一种颜色。
有m个操作,操作有两种,一种是修改一个位置的价格,另一种是购买,每次购买指定一个公差d和一个位置k,找到包含这个位置k公差为d的同色最长等差数列,买下所有物品。让你给这个位置染成t种颜色中的一种(你来指定),其他位置会随机染成t种颜色之一,并保证这n-1个物品中第j种颜色的恰好有c[j]个。求最小期望花费保留四位小数。
注意询问相互独立,询问不会买走物品。
1<=n,m<=10^5,∑t<=2*10^5。
首先我们考虑t=1的情况,这样我们就要找到所有mod d与k同余的数之和。
这是一个经典问题了,把d对于sqrt(n)分类,小于sqrt(n)的修改时直接处理,大于sqrt(n)的查询时直接暴力。
那么我们考虑,要使花的钱最少,你选择的肯定是出现概率最少的颜色,设它有c个。
那么我们考虑一个对答案有贡献的位置k±pd,只有k+d,k+...这p个都是颜色c这个位置才对答案有贡献,即n-1个中指定p个同色的概率。
考虑这样染色的方案数,那么剩下n-1-p个只能有c-p个为这个颜色,方案数就是C(n-1-p,c-p),而概率就是C(n-1-p,c-p)/C(n-1,c)。
设这个概率为f(p),那么f(p)=f(p-1)*(c-p+1)/(n-p)。(自己推啊)
然后最坑的地方到了...注意到t>1时由抽屉原理,c<=(n-1)/2,那么(c-p+1)/(n-p)<=((n-1)/2-p+1)/(n-p)=(0.5n-p+0.5)/(n-p)。
我们随手推一推:(0.5n-p+0.5)/(n-p)<0.5?0.5n-p+0.5<0.5n-0.5p?p>1。
所以f(p)是呈指数级递减的!那么p大于一定范围的时候我们就可以直接忽略f(p)。
大概取个100显然就够了,于是我们暴力算这个东西,大于100就退出。
#include <iostream> #include <stdio.h> #include <math.h> #include <string.h> #include <time.h> #include <stdlib.h> #include <string> #include <bitset> #include <vector> #include <set> #include <map> #include <queue> #include <algorithm> #include <sstream> #include <stack> #include <iomanip> using namespace std; #define pb push_back #define mp make_pair #define pii pair<int,int> #define ll long long #define ld double #define vi vector<int> #define fi first #define se second #define fe first int n,m,v[233333],cs[233333]; int sum[666][666],S,P=123; ld p[2333]; int main() { freopen("lzz.in","r",stdin); freopen("lzz.out","w",stdout); scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%d",v+i); S=max(int(sqrt(n)),1); for(int i=1;i<=S;i++) { for(int j=1;j<=n;j++) sum[i][j%i]+=v[j]; } while(m--) { int s; scanf("%d",&s); if(s==1) { int a,b; scanf("%d%d",&a,&b); for(int i=1;i<=S;i++) sum[i][a%i]+=b-v[a]; v[a]=b; continue; } int t,k,d,c=2000000000; scanf("%d%d%d",&t,&k,&d); for(int i=1;i<=t;i++) scanf("%d",cs+i), c=min(c,cs[i]); if(t==1) { if(d<=s) printf("%.4lf\n",(double)sum[d][k%d]); else { ld rp=0; for(int s=k;s<=n;s+=d) rp+=v[s]; for(int s=k-d;s>0;s-=d) rp+=v[s]; printf("%.4lf\n",rp); } continue; } p[0]=1; for(int i=1;i<=P;i++) if(i>c) p[i]=0; else p[i]=p[i-1]*(c-i+1)/(n-i); ld ans=0; for(int s=k,t=0;s<=n&&t<=P;s+=d,++t) ans+=v[s]*p[t]; for(int s=k-d,t=1;s>0&&t<=P;s-=d,++t) ans+=v[s]*p[t]; printf("%.4lf\n",ans); } }
B. 访问计划
一棵n个点有边权的树,现在要从根节点出发遍历每条边并返回根节点,沿路行走的花费为边权,此外可以使用不超过k次传送门,每次花费c元跳到任意一个节点。求最小花费。
1<=n,k<=2000,多组数据,∑n<=10000,答案在int范围内。
首先每条树边只会经过1或2次,这个似乎十分显然,证明可以用欧拉回路的判定来证。
而传送相当于是添加了一些能且仅能经过一次的边,那么每条边走过的奇偶性与该字数内新加顶点奇偶性相同,因为没有传送因为要来回必然是偶数,两个顶点都在子树内的传送对这条边没有意义,有一次传送就会是奇数。
所以(fa[x],x)这条边会经过2-(x子树内新加点个数)%2次。
那么我们只要记f[a][b]为a子树内有b个新加顶点的代价,直接树上背包转移即可。
由jsoi2016那题的证明这样做是O(n^2)的(我不会证啊qaq)
最后显然用f[1][2x]+x*c更新答案即可。
#include <iostream> #include <stdio.h> #include <math.h> #include <string.h> #include <time.h> #include <stdlib.h> #include <string> #include <bitset> #include <vector> #include <set> #include <map> #include <queue> #include <algorithm> #include <sstream> #include <stack> #include <iomanip> using namespace std; #define pb push_back #define mp make_pair #define pii pair<int,int> #define ll long long #define ld double #define vi vector<int> #define fi first #define se second #define fe first #define SZ 666666 int n,k,c,fst[SZ],vb[SZ],vc[SZ],nxt[SZ],M=0; void ad_de(int a,int b,int c) { ++M; nxt[M]=fst[a]; fst[a]=M; vb[M]=b; vc[M]=c; } void gmin(int& x,int y) { if(x>y) x=y; } int siz[SZ],cst[2005][2005],tmp[2005]; void dp(int x,int f) { siz[x]=cst[x][0]=0; for(int e=fst[x];e;e=nxt[e]) { int b=vb[e]; if(b==f) continue; dp(b,x); memset(tmp,127/3,(siz[x]+siz[b]+1)*sizeof(int)); for(int p=0;p<=siz[x];p++) { for(int q=0;q<=siz[b];q++) gmin(tmp[p+q],cst[x][p]+cst[b][q]+vc[e]*(2-(q&1))); } siz[x]+=siz[b]; for(int i=0;i<=siz[x];i++) cst[x][i]=tmp[i]; } cst[x][++siz[x]]=1000000000; for(int i=siz[x];i>=1;i--) gmin(cst[x][i],cst[x][i-1]); } int main() { freopen("mzz.in","r",stdin); freopen("mzz.out","w",stdout); while(scanf("%d%d%d",&n,&k,&c)!=EOF) { M=0; for(int i=1;i<=n;i++) fst[i]=0; for(int i=1;i<n;i++) { int a,b,c; scanf("%d%d%d",&a,&b,&c); ++a; ++b; ad_de(a,b,c); ad_de(b,a,c); } dp(1,0); int ans=2000000000; for(int i=0;i<=k&&i+i<=n;i++) gmin(ans,cst[1][i+i]+i*c); printf("%d\n",ans); } }
标签:
原文地址:http://www.cnblogs.com/zzqsblog/p/5730207.html