码迷,mamicode.com
首页 > 其他好文 > 详细

十连测Day1 题解

时间:2016-08-02 19:21:40      阅读:219      评论:0      收藏:0      [点我收藏+]

标签:

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);
    }
}
C. 被虐傻了 待更

十连测Day1 题解

标签:

原文地址:http://www.cnblogs.com/zzqsblog/p/5730207.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!