2015ACM多校对抗赛第五场

1004——MZL's game

当时全场没有队伍通过的一道题,此题状态构造的甚是巧妙

题意:n个人,每次从存活的人中等概率选出一个人去攻击场上其他人,被攻击者存活的概率相等 且由题目给出,选出的人出局。求一个人被攻击k次之后出局(被选出来攻击其他人后出局,受到攻击死亡不算出局)的概率。

思路:出题人的想法是先把这个问题转化为 从1,2...n顺次选取参与者去攻击其他人,如果被选者死亡,则直接选取下一个人即可。我们先处理出1,2...n的人受到 j 次攻击后存活的概率,dp[i][j]表示选取第 i 个人时,i 到 n的人都受到了 j 次攻击的概率,可以得到dp[i][j] = dp[i-1][j-1]*p1(第i-1个人受到j-1次攻击后存活的概率)+dp[i-1][j]*p2(第i-1个人在 j 次攻击内死亡的概率)。因为如果第 i-1 个人存活,那么必然会对i,i+1...n的人都进行一次攻击;只有当第i-1个人在之前的攻击中死亡,其余人才不会受到攻击。

一个人被攻击k次之后出局的概率其实就是dp[1,2...n][k]的和乘以p3(一个人受到k次攻击不死亡的概率)再除以n取平均数。在这里可以将dp[i][j]看作是第i个人受到j次攻击的概率。

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define LL long long
#define MOD 258280327
LL power(LL x,LL k){
    LL res=1;
    while(k){
        if(k&1)res=res*x%MOD;
        x=x*x%MOD;
        k>>=1;
    }
    return res;
}
LL dp[2100][2100];
int main(){
    LL n,x,y,T;scanf("%lld",&T);
    while(T--){
        scanf("%lld%lld%lld",&n,&x,&y);
        LL dead=x*power(y,MOD-2)%MOD;//dead probability of an attack
        LL live=(MOD+1-dead)%MOD;//live probability of an attack
        dp[1][0]=1;
        for(int j=1;j<n;j++)dp[1][j]=0;
        for(int i=2;i<=n;i++){// i th peolivee
            dp[i][0]=0;
            LL tmp=1;
            for(int j=1;j<i;j++){// j times attacked
                //move = i th live (live^(j-1)) + i th dead (1-live^(j))
                dp[i][j]=dp[i-1][j-1]*tmp%MOD+dp[i-1][j]*(MOD+1-tmp*live%MOD)%MOD;
                tmp=tmp*live%MOD;
            }
        }
        for(int k=0;k<n;k++){
            LL tmp=1;
            LL ans=0;
            for(int i=1;i<=n;i++){
                if(i-1<k)tmp=tmp*live%MOD;
                ans=(ans+dp[i][k])%MOD;
            }
            // ans*live^(k)/n
            ans=ans*tmp%MOD*power(n,MOD-2)%MOD;
            printf("%lld%c",ans,k==n-1?'\n':' ');
        }
    }
    return 0;
}

1005——MZL's endless loop

题意:一个具有n个结点,m条无向边的无向图,要求我们规定每条边的方向,使其成为有向图之后每个结点的入度与出度差的绝对值小于1,若无解,则输出-1

解题思路:该题的解法有很多种,有用欧拉路的,也有用搜索的,而我要讲的方法就是深搜

首先我们先看看多校题解报告中的思路

出题人的想法是一张图中除了环就是树,根据左孝凌版离散数学中树的定义“无回路的连通图”可以得出这样一个结论,要么有回路(环),要么没有回路(树),无外乎这么两种情况,所以根据环与树各自的属性特点,此题从每个点开始DFS这张图就好了

下面给出我做此题时的思路

首先暂且先不考虑环还是树,直接进行深搜,对每个点进行出度和入度的判断,如果出度大,就先进行反向的搜索(每搜索一条边u,v就认为这是一条从v到u的有向边),如果入度大,进行正向搜索(每搜到一条边u,v认为这是一条从u到v的有向边),一直搜索到找不到边能继续为止。

注意:

①已经使用过的边为了防止再次被遍历到,可以修改head[u]的值,即head[u] = edge[i].next

②注意自环,因为能否继续向下层搜索是根据v的入度和出度大小来决定的,但是如果是自环的话可能会影响继续搜索。

另外,此题不存在无解的可能

反证法证明:假设存在一个结点v,满足该结点的入度比出度多2,那么,当有一条边搜索到v后找不到后继,导致v的入度比出度多1,这时又有一条边搜索到v,导致v的入度比出度多2,但是这样的话就会与第一条找不到后继的情况冲突,所以不会出现入度比出度多2的情况,其余情况亦是如此,所以不会有无解的结果。

代码:

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<queue>
#include<math.h>
#include<vector>
#include<map>
#include<set>
#include<cmath>
#include<string>
#include<algorithm>
#include<iostream>
#define exp 1e-10
using namespace std;
const int N = 100005;
const int inf = 1000000000;
struct node
{
    int to,next;
}edge[6*N];
int k,head[N],num[N],in[N],out[N],n,ans[N*3];
bool vis[N*6];
void add_edge(int from,int to)
{
    edge[k].to=to;
    edge[k].next=head[from];
    head[from]=k++;
}
void DFS1(int u)
{
    int i,v;
    for(i=head[u];i+1;i=edge[i].next)
    {
        if(vis[i])
        {
            head[u]=edge[i].next;
            continue;
        }
        v=edge[i].to;
        if(u!=v&&in[v]>out[v])
            continue;
        out[u]++;
        in[v]++;
        vis[i]=vis[i^1]=true;
        ans[i/2]=i%2?0:1;
        head[u]=edge[i].next;
        DFS1(v);
        break;
    }
}
void DFS2(int u)
{
    int i,v;
    for(i=head[u];i+1;i=edge[i].next)
    {
        if(vis[i])
        {
            head[u]=edge[i].next;
            continue;
        }
        v=edge[i].to;
        if(u!=v&&in[v]<out[v])
            continue;
        out[v]++;
        in[u]++;
        vis[i]=vis[i^1]=true;
        ans[i/2]=i%2?1:0;
        head[u]=edge[i].next;
        DFS2(v);
        break;
    }
}
int main()
{
    int t,m,i,u,v;
    scanf("%d",&t);
    while(t--)
    {
        k=0;
        memset(head,-1,sizeof(head));
        memset(num,0,sizeof(num));
        memset(in,0,sizeof(in));
        memset(out,0,sizeof(out));
        memset(vis,false,sizeof(vis));
        scanf("%d%d",&n,&m);
        for(i=0;i<m;i++)
        {
            scanf("%d%d",&u,&v);
            add_edge(u,v);
            add_edge(v,u);
            num[u]++;
            num[v]++;
        }
        for(i=1;i<=n;i++)
            while(in[i]+out[i]<num[i])
                if(in[i]>=out[i])
                    DFS1(i);
                else
                    DFS2(i);
        for(i=0;i<m;i++)
            printf("%d\n",ans[i]);
    }
    return 0;
}

1006——MZL's simple problem

1 x : add number x to set
2 : delete the minimum number (if the set is empty now,then ignore it)
3 : query the maximum number (if the set is empty now,the answer is 0)

就是在set里进行简单操作。注意是to set,说的很明白了

代码:

#include<bits/stdc++.h>
using namespace std;
set<long long >::iterator it;
set<long long > s;

int main()
{
    long long n, a, x;
    scanf("%lld",&n);
    s.clear();
    while(n--)
    {
        scanf("%lld",&a);
        if(a == 1)
        {
            scanf("%lld",&x);
            s.insert(x);
        }
        else if(a == 2)
        {
            if(!s.empty())
               s.erase(s.begin());
        }
        else if(a == 3)
        {
            if(s.empty())
                puts("0");
            else
            {
                it=s.end();
                it--;
                printf("%lld\n",*it);
            }
        }
    }
    return 0;
}

本文参考:

https://blog.csdn.net/queuelovestack/article/details/47303363

https://blog.csdn.net/leidaibudong/article/details/47311735

猜你喜欢

转载自blog.csdn.net/xianpingping/article/details/81295773