6.12联考题解

A:
对于 T = 1 的询问分块,对于 <= n 的K,维护序列中 m o d   K = i   的和,对于 > n 的K,每次询问直接暴力跳
O ( n n )
对于 T 1 的询问
首先肯定贪心的染颜色数最少的那种颜色,设有 c 个,将颜色转为标号 0 ~ n 1 ,若随机值落在 [ 0 , c 1 ] 间就是这种颜色,于是一种比较暴力的做法就是直接枚一遍 k + i d ,计算每个位置贡献的概率带上权累计进答案,这样做是 O ( n m )
注意到因为 T 1 ,而且我们选的是最少的那种颜色,所以一定有 c n 1 <= 1 2
因为我们答案只要求4位的精度,当n很大时,其实大概当我们枚举的 i 的绝对值 | i | > 200 k + i d 贡献进答案的概率已经是小数点后好几十位了,他的贡献你计不计算都对答案没有影响
于是我们可以对 T 1 做到 O ( 400 n )

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
#define ld long double
using namespace std;

inline void read(int &x)
{
    char c; while(!((c=getchar())>='0'&&c<='9'));
    x=c-'0';
    while((c=getchar())>='0'&&c<='9') (x*=10)+=c-'0';
}
const int maxn = 210000;

int n,m;
int val[maxn]; 
ll sum[600][600]; int N;

int T,k,d;
int t[maxn];

int main()
{
    freopen("lzz.in","r",stdin);
    freopen("lzz.out","w",stdout);

    read(n); read(m); N=500;
    for(int i=1;i<=n;i++) 
    {
        read(val[i]);
        for(int j=1;j<=N;j++) sum[j][i%j]+=val[i];
    }
    while(m--)
    {
        int type; read(type);
        if(type==1)
        {
            int x,y; read(x); read(y);
            for(int j=1;j<=N;j++) sum[j][x%j]+=y-val[x];
            val[x]=y;
        }
        else
        {
            read(T); read(k); read(d);
            for(int i=1;i<=T;i++) read(t[i]);
            if(T==1&&d<=N) { printf("%.4Lf\n",(ld)sum[d][k%d]); continue; }

            int mnc=t[1]; for(int i=2;i<=T;i++) mnc=min(mnc,t[i]);
            ld ans=val[k];
            ld p=1.0;
            for(int i=1;i<=200;i++)
            {
                int x=k+i*d; if(x>n||i>mnc) break;
                p=p*((ld)(mnc-i+1)/(n-i));
                ans+=p*val[x];
            }
            p=1.0;
            for(int i=1;i<=200;i++)
            {
                int x=k-i*d; if(x<1||i>mnc) break;
                p=p*((ld)(mnc-i+1)/(n-i));
                ans+=p*val[x];
            }
            printf("%.4Lf\n",ans);
        }
    }

    return 0;
}

B:
假设我们不使用跳跃,那么我们从根出发,遍历一遍树再回到根,每条边经过2次,这也是经过次数的上限
我们可以随便画几个跳跃,手玩一下,会发现一次从 x y 的跳跃可以使 x y 路径上的边都少经过一次
因为要求每条边至少经过一次,所以一条边不能被多个跳跃的链同时覆盖,同时可以通过手玩发现,确定了这些互不相交的跳跃后,一定能构造出一组解使得这些链上的边都少经过一次
转化一下发现就是在这棵树上找不超过 k 条互不相交的链使他们覆盖的边权和- C 链的数量最大
做个DP即可

f [ i ] [ j ] [ 0 / 1 ] 表示 i 的子树里有 j 条链, i 向上的父边是否被链占用,最大价值
一个点 x 合并他的孩子时这个0/1的意义要改一下,改成和孩子连边被占用的边数的奇偶

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;

inline void up(int &a,const int &b){if(a<b)a=b;}
const int maxn = 2100;

int n,K,C;
struct edge{int y,c,nex;}a[maxn<<1]; int len,fir[maxn];
inline void ins(const int x,const int y,const int c){a[++len]=(edge){y,c,fir[x]};fir[x]=len;}

int siz[maxn];
int f[maxn][maxn][2],g[maxn][2];
void dp(int x,int fa)
{
    siz[x]=1;
    f[x][0][0]=0,f[x][0][1]=0;
    for(int k=fir[x],y=a[k].y;k;k=a[k].nex,y=a[k].y) if(y!=fa)
    {
        dp(y,x);
        for(int j=0;j<=siz[x]+siz[y]&&j<=K;j++) g[j][0]=g[j][1]=0;
        for(int j=0;j<=siz[x];j++) for(int l=0;l<=siz[y]&&j+l<=K;l++)
        {
            up(g[j+l][0],f[x][j][0]+f[y][l][0]);
            up(g[j+l][1],f[x][j][0]+f[y][l][1]+a[k].c);
            up(g[j+l][1],f[x][j][1]+f[y][l][0]);
            up(g[j+l+1][0],f[x][j][1]+f[y][l][1]+a[k].c-C);
        }
        siz[x]+=siz[y];
        for(int j=0;j<=siz[x]&&j<=K;j++) f[x][j][0]=g[j][0],f[x][j][1]=g[j][1];
    }
}

int main()
{
    freopen("mzz.in","r",stdin);
    freopen("mzz.out","w",stdout);

    while(scanf("%d%d%d",&n,&K,&C)!=EOF)
    {
        memset(fir,0,sizeof fir); len=0;
        int sum=0;
        for(int i=1;i<n;i++)
        {
            int x,y,c; scanf("%d%d%d",&x,&y,&c); x++,y++;
            ins(x,y,c); ins(y,x,c);
            sum+=c<<1;
        }
        for(int i=1;i<=n;i++) for(int j=0;j<=K;j++) f[i][j][0]=f[i][j][1]=0;
        dp(1,0);
        int ans=0;
        for(int i=0;i<=K;i++) up(ans,f[1][i][0]);
        printf("%d\n",sum-ans);
    }

    return 0;
}

C:
不会做
看不懂题解
弃疗
……

猜你喜欢

转载自blog.csdn.net/l_0_forever_lf/article/details/80679288
今日推荐