2017年9月2日 机房模拟赛

显然貌似是某其他学校考过的,而我们的题竟然其他学校也在考,233,教练们是什么py(thon)交易


显然作为NOIP选手考这样一套NOIP难度的题目,考成这样确实很差,尤其是t2的思想错误,竟然认为最小树形图和最小生成树是一样的,我竟然tarjan之后在用最小生成树来做???真是羞耻啊…t1根本不会,期望以前根本没做过,现在总算是有一些起色,t3写的n方暴力,那么现在我们就来看题吧


这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述


t1是一个期望dp,我们可以从后面往前推,现状压一下,表示现在是这个集合到买满所有的东西的集合的期望(平均)次数。显然我们可以得到dp[(1<<(n-1))-1]是0,因为此时本来就已经买完了,不需要再买了。
然后现在我们来推dp转移方程

f[s]=f[s]P(s>s)+f[s]P(s>s)+1

f[s](1P(s>s))=f[s]P(s>s)+1

f[s]=(f[s]P[s>s]+1)/(1P(s>s));

解释一下上面的推导,其实已经写得非常清楚啦,就是说从所有可以转移到的新的集合转移过来,让他们的期望全部乘以概率,然后再加上自己rand到已经有的数的概率乘期望,然后自然要+1,因为我们无论是如何转移,从哪里转移,最后自己都还要走一步走到那个位置去。
然后s->s’表示转移到新集合的概率,s->s表示转移到自己的概率,然后dp是不能有环的,是一个DAG转移,所以我们移项,得到第二步的式子,最后变成第三步的式子,就可以做了,愉快地转移,看代码瞬间懂。


代码:

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std ;
const int MAXN=2000000+10;
double dp[MAXN],P[MAXN];
int n,m;
long long ans;
int main(){
    freopen("gift.in","r",stdin);
    freopen("gift.out","w",stdout);
    int tmp;
    scanf("%d",&n);
    for(register int i=1;i<=n;i++){
        scanf("%lf%d",&P[i],&tmp);
        if(P[i]>0) ans+=tmp;
    }
    m=(1<<n)-1;dp[m]=0;
    for(register int s=m-1;s>=0;s--){
        double sum=0;
        for(int j=0;j<n;j++){
            if((s&(1<<j))==0){
                sum+=P[j+1];
                int ss=s|(1<<j);
                dp[s]+=P[j+1]*dp[ss];
            }
        }
        dp[s]++;
        dp[s]=dp[s]/sum;
    } 
    printf("%I64d\n%.3lf\n",ans,dp[0]);
    return 0;
} 

t2的话,显然直接tarjan一下然后贪心就可以了,贪心策略是对于每一个scc,我们选取其入边里面权值最小的边来进行连接,因为0号节点已经是树根了,所以直接这样贪心策略完全没有问题。
代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN=100000*2;
int dfn[MAXN],head[MAXN],tail,low[MAXN],sta[MAXN],top,timer,scc[MAXN],cnt,fa[MAXN],N,M,ff,tt,fl;
int minn[MAXN];
bool vis[MAXN];
struct Line{
    int from,to,nxt,flow;
}line[MAXN];
struct Edge{
    int from,to,flow;
}edge[MAXN];
void add_line(int from,int to,int flow){
    line[++tail].from=from;
    line[tail].nxt=head[from];
    line[tail].to=to;
    line[tail].flow=flow;
    head[from]=tail;
}
void add_edge(int from,int to,int flow){
    edge[++tail].from=from;
    edge[tail].to=to;
    edge[tail].flow=flow;
}
bool cmp(const Edge& A,const Edge& B){return A.flow<B.flow;}
void tarjan(int u){
    dfn[u]=++timer;
    low[u]=dfn[u];
    vis[u]=true;
    sta[++top]=u;
    for(register int i=head[u];i;i=line[i].nxt){
        int v=line[i].to;
        if(dfn[v]==0){
            tarjan(v);low[u]=min(low[u],low[v]);
        }else if(vis[v]==true){
            low[u]=min(low[u],dfn[v]);
        }
    }
    if(dfn[u]==low[u]){
        scc[u]=++cnt;
        vis[u]=false;
        while(sta[top]!=u){
            scc[sta[top]]=cnt;
            vis[sta[top]]=false;
            top--;
        }
        top--;
    }
}
int main(){
    freopen("message.in","r",stdin);
    freopen("message.out","w",stdout); 
    while(scanf("%d%d",&N,&M)!=EOF&&(!(N==0&&M==0))){
        memset(head,0,sizeof(head));tail=0;
        memset(dfn,0,sizeof(dfn));cnt=0;
        memset(minn,63,sizeof(minn));
        for(register int i=1;i<=M;i++){
            scanf("%d%d%d",&ff,&tt,&fl);ff++;tt++;
            add_line(ff,tt,fl);
        }
        for(register int i=1;i<=N;i++){
            if(dfn[i]==0) tarjan(i);
        }
        tail=0;
        for(register int i=1;i<=M;i++){
            int u=line[i].from,v=line[i].to;
            if(scc[u]==scc[v]) continue;
            add_edge(scc[u],scc[v],line[i].flow);
        }
        for(register int i=1;i<=tail;i++){
            int u=edge[i].from,v=edge[i].to;
            minn[v]=min(minn[v],edge[i].flow);
        }
        int rt=scc[1];int ass=0;
        for(register int i=1;i<=cnt;i++){
            if(i==rt) continue;
            ass+=minn[i];
        }
        printf("%d\n",ass);
    }
    return 0;
} 

t3不说了,现在都还不会,会了补上,我贴个跑了一百秒的n方大暴力


代码:

#include<bits/stdc++.h>
using namespace std;
const int MAXN=100000+10;
struct Point{int x,y;}p[MAXN];
int n,start,end,maxn[20][100000],minn[20][100000];
bool cmp(const Point& A,const Point& B){return A.x<B.x;}
void build(){
    for(register int i=1;i<=n;i++) maxn[0][i]=minn[0][i]=p[i].y;
    for(register int j=1;(1<<j)<=n;j++)
        for(register int i=1;i+(1<<j)-1<=n;i++)
            maxn[j][i]=max(maxn[j-1][i],maxn[j-1][i+(1<<(j-1))]),minn[j][i]=min(minn[j-1][i],minn[j-1][i+(1<<(j-1))]);
}
int query_max(int L,int R,int k){
    return max(maxn[k][L],maxn[k][R-(1<<k)+1]);
}
int query_min(int L,int R,int k){
    return min(minn[k][L],minn[k][R-(1<<k)+1]);
}
int main(){
    freopen("raid.in","r",stdin);
    freopen("raid.out","w",stdout);
    scanf("%d",&n);
    for(register int i=1;i<=n;i++){scanf("%d%d",&p[i].x,&p[i].y);}
    sort(p+1,p+n+1,cmp);
    build();int cnt=0;
    for(register int k=2;k<=n;k++){
        int kk=0;
        while((1<<(kk+1))<=k) kk++;
        for(register int i=1;i<=n;i++){
            if(i+k-1>n) break;
            int maks=query_max(i,i+k-1,kk),miks=query_min(i,i+k-1,kk); 
            if(maks==miks+k-1) cnt++;
        }
    }
    printf("%d\n",cnt+n);
    return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值