【NOIP2016】Day1

本文解析了三道算法题,包括模拟、树上差分和概率DP。通过具体实例介绍了如何利用异或简化代码,使用树上差分解决路径计数问题,并详细解释了概率DP在求解期望值问题中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Solution

T1

模拟,可以用异或简化代码

T2

这里讲一下正解,网上题解也有很多
我们把路径插成,u->lca , lca->v,
我们先看u->lca这一条,若这条路径上的点x能观察到u,那么:
deep[u]deep[x]=T[x]
移项后得: deep[u]=deep[x]+T[x]
发现右边是一个之与x有关的项,我们的目标就是求x的子树中有几个起点u,终点在x以上(含x),且deep[u]=deep[x]+T[x]
这句话让我们想到了什么?树上差分!
我们在u上+1,fa[lca]上-1,就表示了一条链,但这个值怎么搞?开一个桶就行了!
我们对每一个节点建一个类似边表的东西,表示会对那些节点+-1,也可以用vector,然后dfs到u时,遍历一遍这个边表,把他的影响算上,然后+a[T[u]+deep[u]],a是桶。
注意:一个节点可以有多个儿子,但每个儿子只能计数自己的子树内的个数,这样会多次计数,我们可以在进入这个节点前减去答案,最后再加答案,就是加差值,这很重要,也简化了步骤。
往上走也是一样, deep[u]deep[lca]+deep[y]deep[lca]=T[y]
移项后: T[y]deep[y]=deep[u]2deep[lca]

T3

这道题目赛场上写了一个本来能拿80的错误的DP,但是最短路打错了,只剩20.
对于这个期望有两种理解:一种是每条可能路径的长度*概率,
另外一种是:每两节课之间的每条路的长度*概率,
第二种理解的转化是解题的关键,因为这道题目只有相邻的点会影响路径的选择,所以我们要记录前一节课是在哪里上的,或者说是否申请,
http://www.cnblogs.com/ljh2000-jump/p/6189054.html 此人博客暴力写的都很详细
这显然是一道概率DP裸题。考虑f[i][j][0、1]表示前i堂课,已经申请了j次,这次申不申请的最小期望值
d1=dis[c[i-1]][c[i]],d2=dis[c[i-1]][d[i]],d3=dis[d[i-1]][c[i]],d4=dis[d[i-1]][d[i]];
f[i][j][0]=max{f[i-1][j][0]+d1,f[i-1][j][1]+d3*k[i-1]+d1*(1-k[i-1])}
表示i节课不申请,有两种来源:i-1节课不申请,只可用从c[i-1]走到c[i],i-1节课申请,分类,成功,概率k[i-1],价值d1*k[i-1],失败,概率(1-k[i]),价值d3 *(1-k[i-1]),其他同理。

CODE

T1

#include<bits/stdc++.h>
using namespace std;
const int MAXN=100000+10;
int d[MAXN],n,m;
char name[MAXN][15];

int main()
{
    scanf("%d%d",&n,&m);
    for (int i=0;i<n;i++)
        scanf("%d%s",&d[i],&name[i]);
    int now=0,x,y;
    for (int i=0;i<m;i++)
    {
        scanf("%d%d",&x,&y);
        if (d[now]^x) now=(now+y)%n;
        else now=(now-y+n)%n;   
    }
    puts(name[now]);    
    return 0;
}

T2

#include<bits/stdc++.h>
using namespace std;
const int MAXN=300010;
int Head[MAXN],Headq[MAXN],Headup[MAXN],Headdown[MAXN];
int ans[MAXN],father[MAXN],deep[MAXN],a[MAXN],b[MAXN*2];
int s[MAXN],t[MAXN],lca[MAXN],T[MAXN],fa[MAXN];
int n,m,tot,tot1;
bool vis[MAXN];

struct Edge{
    int v,next,x;
}edge[MAXN*2];
struct Ques{
    int v,next,id;
}que[MAXN*2];
struct node{
    int next,x,y;
}up[MAXN*2],down[MAXN*2];
void adde(int x,int y)
{
    edge[++tot]=(Edge){y,Head[x],0};
    Head[x]=tot;
}
void addq(int x,int y,int id)
{
    que[++tot]=(Ques){y,Headq[x],id};
    Headq[x]=tot;
}
void addup(int u,int x,int y)
{
    up[++tot]=(node){Headup[u],x,y};
    Headup[u]=tot;
}
void adddown(int u,int x,int y)
{
    down[++tot1]=(node){Headdown[u],x,y};
    Headdown[u]=tot1;
}
int getfa(int x)
{
    return fa[x]==x?fa[x]:fa[x]=getfa(fa[x]);
}
void Tarjan_LCA(int u,int pre)
{
    father[u]=pre; fa[u]=u; deep[u]=deep[pre]+1;
    for (int i=Head[u];i;i=edge[i].next)
    {
        int v=edge[i].v;
        if (v==pre) continue;
        Tarjan_LCA(v,u);
        fa[v]=u;
    }
    vis[u]=true;
    for (int i=Headq[u];i;i=que[i].next)
        if (vis[que[i].v]) lca[que[i].id]=getfa(que[i].v);
}
void dfs(int u,int pre)
{
    ans[u]-=a[T[u]+deep[u]]+b[T[u]-deep[u]+n];
    for (int i=Head[u];i;i=edge[i].next)
        if (edge[i].v!=pre) dfs(edge[i].v,u);
    for (int i=Headup[u];i;i=up[i].next) a[up[i].x]+=up[i].y;
    for (int i=Headdown[u];i;i=down[i].next) b[down[i].x+n]+=down[i].y;
    ans[u]+=a[T[u]+deep[u]]+b[T[u]-deep[u]+n];
}
int main()
{
    scanf("%d%d",&n,&m);tot=0;
    for (int i=1;i<n;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        adde(x,y); adde(y,x);
    }
    tot=0;
    for (int i=1;i<=n;i++) scanf("%d",&T[i]);
    for (int i=1;i<=m;i++)
    {
        scanf("%d%d",&s[i],&t[i]);
        addq(s[i],t[i],i);
        addq(t[i],s[i],i);
    }
    Tarjan_LCA(1,0);
    tot=tot1=0;
    for (int i=1;i<=m;i++)
    {
        addup(s[i],deep[s[i]],1);
        addup(father[lca[i]],deep[s[i]],-1);
        adddown(t[i],deep[s[i]]-2*deep[lca[i]],1);
        adddown(lca[i],deep[s[i]]-2*deep[lca[i]],-1);
    }
    dfs(1,0);
    for (int i=1;i<n;i++) printf("%d ",ans[i]);
    printf("%d\n",ans[n]);
    system("pause");
    return 0;
}

T3

#include<bits/stdc++.h>
using namespace std;
const int MAXN=2000+10;
int f[MAXN][MAXN],c[MAXN],d[MAXN],n,m,v,e;
double k[MAXN],k_[MAXN],s[MAXN][MAXN][2],ans;

void init()
{
    memset(f,0x3f,sizeof(f));
    scanf("%d%d%d%d",&n,&m,&v,&e);
    memset(s,0x43,sizeof(s));
    ans=1e16;
    for (int i=1;i<=n;i++) scanf("%d",&c[i]);
    for (int i=1;i<=n;i++) scanf("%d",&d[i]);
    for (int i=1;i<=n;i++) scanf("%lf",&k[i]),k_[i]=1.0-k[i];
    for (int i=1;i<=e;i++)
    {
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        if (f[x][y]>z) f[x][y]=f[y][x]=z;
    }
    for (int i=1;i<=v;i++) f[i][i]=0;
    for (int kk=1;kk<=v;kk++)
        for (int i=1;i<=v;i++)
            for (int j=1;j<=v;j++)
                f[i][j]=min(f[i][j],f[i][kk]+f[kk][j]);
}
int main()
{
    init();
    memset(s[1],0,sizeof(s[1]));
    s[1][0][1]=1e16;
    if (n==1) ans=0;
    for (int i=2;i<=n;i++)
        for (int j=0;j<=m;j++)
        {
            int d1=f[c[i-1]][c[i]],d2=f[c[i-1]][d[i]];
            int d3=f[d[i-1]][c[i]],d4=f[d[i-1]][d[i]];
            s[i][j][0]=min(s[i-1][j][0]+d1,s[i-1][j][1]+k[i-1]*d3+k_[i-1]*d1);
            if (j) s[i][j][1]=min(s[i-1][j-1][0]+k[i]*d2+k_[i]*d1,s[i-1][j-1][1]+
                   k_[i-1]*k_[i]*d1+k_[i-1]*k[i]*d2+k[i-1]*k_[i]*d3+k[i-1]*k[i]*d4);
            if (i==n) ans=min(ans,min(s[i][j][0],s[i][j][1]));
        }
    printf("%.2lf\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值