2018暑期集训第二期模拟赛DAY1题解

西安铁一中2018暑期集训第二期模拟赛DAY1题解

本次题目来自:hdu5952hdu5955hdu4053.
T1:聚会
  • 题目大意:有n个人和m对关系,现在要选s个人使得这s个人都互相认识,求方案数。 n 100 , m 1000 , s 10 ,且保证每个人认识的不超过 20 人。
  • 题解:考虑到每个人最多认识 20 人,而 C 9 20 = 167960 ,并不是一个太大的数字,因此我们可以考虑暴力选人然后check一下,看上去复杂度好像刚好爆掉了,但如果我们选人时按照编号从小到大选,既不会造成答案重复,也能过减小很大一部分复杂度,因此建边时可以只建由小指向大的边。
  • 代码里因为并没有超时,所以还是存了无向边。
#include<bits/stdc++.h>
#define maxn 100005
#define inf (1<<30)
#define mod 1000000007
using namespace std;
typedef long long LL;
int read()
{
    char c=getchar();int f=1,sum=0;
    while(c<'0' || c>'9'){if(c=='-') f=-1;c=getchar();}
    while(c>='0' && c<='9'){sum=sum*10+c-'0';c=getchar();}
    return sum*f;
}
int n,m,s;
int a[105][105];
int deg[105],sta[105],vis[105],ans;
int head[105],to[2005],nex[2005],cnt;
void add(int u,int v)
{
    to[++cnt]=v;nex[cnt]=head[u];head[u]=cnt;
}
bool check()
{
    for(int i=1;i<s;i++)
    for(int j=i+1;j<s;j++)
    if(!a[sta[i]][sta[j]]) return false;
    return true;
}
void dfs(int x,int num,int l)
{
    if(num==s)
    {
        if(check())
        ans++;
        return;
    }
    for(int i=head[x];i;i=nex[i])
    {
        if(deg[to[i]]<s-1) continue;
        if(to[i]<l) continue;
        if(vis[to[i]]) continue;
        sta[num]=to[i];
        vis[to[i]]=1;
        dfs(x,num+1,to[i]);
        sta[num]=0;
        vis[to[i]]=0;
    }
    return;
}
int main()
{
    freopen("counting.in","r",stdin);
    freopen("counting.out","w",stdout);
    n=read();m=read();s=read();
    for(int i=1;i<=m;i++)
    {
        int u=read(),v=read();
        a[u][v]=a[v][u]=1;
        deg[u]++;deg[v]++;
        add(u,v);add(v,u);
    }
    for(int i=1;i<=n-s+1;i++)
    dfs(i,1,i);
    printf("%d\n",ans);
    return 0;
}
T2:扔骰子
  • 本场最难题
  • 题目大意: n 个人 ( n 10 ) 扔骰子,每个人有一个猜测序列(猜测序列长度 l 10 ),最先扔出猜测序列的人获胜,求每个人获胜概率。
  • 题解:AC自动机+高斯消元。
  • 对于所有序列先建立AC自动机然后进行高斯消元解出答案。
  • 如果一个节点 i 不是结尾的节点,那么对于 i 能转移到的每个节点 j 都有 1 6 的概率转移到,因此对于方程 A [ i ] [ j ] += 1 6 ,然后自己应该在方程的等号右边,直接令 A [ i ] [ i ] += 1
  • 根节点特殊需要特殊处理,因为一开始就以1的概率转移到根节点,所以 A [ 0 ] = 1
  • 放上一张图解:
  • 网上扒的
  • 然后高斯消元即可。
#include<bits/stdc++.h>
#define maxn 100005
#define inf (1<<30)
#define eps 1e-6
using namespace std;
typedef long long LL;
int read()
{
    char c=getchar();int f=1,sum=0;
    while(c<'0' || c>'9'){if(c=='-') f=-1;c=getchar();}
    while(c>='0' && c<='9'){sum=sum*10+c-'0';c=getchar();}
    return sum*f;
}
int T;
int n,l;
int siz;
int son[505][7],fail[5005],vis[5005],q[5005];
int ed[15],s[15];
double ans[201][201];
void insert(int x)
{
    int now=0;
    for(int i=1;i<=l;i++)
    {
        int t=s[i];
        if(son[now][t]) now=son[now][t];
        else now=son[now][t]=++siz;
    }
    vis[now]++;
    ed[x]=now;
}
void build()
{
    queue<int> q;
    q.push(0);
    while(!q.empty())
    {
        int now=q.front();q.pop();
        for(int i=1;i<=6;i++)
        {
            int nex=son[now][i];
            if(!nex)
                son[now][i]=son[fail[now]][i];
            else
            {
                if(now)
                fail[nex]=son[fail[now]][i];
                q.push(nex);
            }
        }
    } 
}
void solve()
{
    for(int i=0;i<=siz;i++)
    {
        if(vis[i]) continue;
        for(int j=1;j<=6;j++)
        ans[son[i][j]][i]+=1.0/6.0;
    }
    for(int i=0;i<=siz;i++)
        ans[i][i]+=-1.0;
    ans[0][siz+1]=-1.0;
}
void gauss(int x)
{
    int now=0,to;
    double t;
    for(int i=0;i<=x;i++)
    {
        for(to=now;to<=x;to++)
        if(fabs(ans[to][i])>eps)
        break;
        if(to>x) continue;
        if(to!=now)
        {
            for(int j=0;j<=x+1;j++)
            swap(ans[to][j],ans[now][j]);
        }
        t=ans[now][i];
        for(int j=0;j<=x+1;j++)
        ans[now][j]/=t;
        for(int j=0;j<=x;j++)
        if(j!=now)
        {
            t=ans[j][i];
            for(int k=0;k<=x+1;k++)
            ans[j][k]-=t*ans[now][k];
        }
        now++;
    }
}
void init()
{
    siz=0;
    memset(son,0,sizeof(son));
    memset(ans,0.0,sizeof(ans));
    memset(vis,0,sizeof(vis));
    memset(fail,0,sizeof(fail));
}
int main()
{
    T=read();
    while(T--)
    {
        init();
        n=read();l=read();
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=l;j++) s[j]=read();
            insert(i);
        }
        build();
        solve();
        gauss(siz);
        double sum=0.0;
        for(int i=1;i<=n;i++) sum+=ans[ed[i]][siz+1];
        for(int i=1;i<n;i++) printf("%.6lf ",ans[ed[i]][siz+1]/sum);
        printf("%.6lf\n",ans[ed[n]][siz+1]/sum);
    }
    return 0;
}
T3:开灯
  • 题意:在一条数轴上有 n 个灯,第 i 盏灯在 d [ i ] 处,按下开关 t [ i ] 秒后熄灭,求一个合法序列使得能够按亮所有的灯,在两盏灯间的移动时间是两个灯的坐标之差。
  • 题解:区间dp。用 d p [ l ] [ r ] [ 0 / 1 ] 表示按亮 [ l , r ] 之间的灯,并且停留在左端点还是右端点的最短时间。
  • 转移方程为:
  • d p [ l ] [ r ] [ 0 ] = m i n ( d p [ l + 1 ] [ r ] [ 0 ] + d [ l + 1 ] d [ l ] , d p [ l + 1 ] [ r ] [ 1 ] + d [ r ] d [ l ] )
  • d p [ l ] [ r ] [ 1 ] = m i n ( d p [ l ] [ r 1 ] [ 0 ] + d [ r ] d [ l ] , d p [ l ] [ r 1 ] [ 1 ] + d [ r ] d [ r 1 ] )
  • 记录路径时只要记录当前这个状态是往左还是往右走即可。
#include<bits/stdc++.h>
#define maxn 100005
#define inf (1<<30)
#define mod 1000000007
using namespace std;
typedef long long LL;
int read()
{
    char c=getchar();int f=1,sum=0;
    while(c<'0' || c>'9'){if(c=='-') f=-1;c=getchar();}
    while(c>='0' && c<='9'){sum=sum*10+c-'0';c=getchar();}
    return sum*f;
}
int n;
int t[205],d[205];
int dp[205][205][2],pre[205][205][2];
int main()
{
    while(n=read())
    {
        memset(dp,0,sizeof(dp));
        for(int i=1;i<=n;i++) t[i]=read();
        for(int i=1;i<=n;i++) d[i]=read();
        for(int l=2;l<=n;l++)
        {
            for(int i=1;i<=n-l+1;i++)
            {
                int j=i+l-1;
                int li=dp[i+1][j][0]+d[i+1]-d[i];
                int ri=dp[i+1][j][1]+d[j]-d[i];
                if(li<=ri) dp[i][j][0]=li,pre[i][j][0]=0;
                else dp[i][j][0]=ri,pre[i][j][0]=1;
                if(t[i]<=dp[i][j][0])
                dp[i][j][0]=inf;
                li=dp[i][j-1][0]+d[j]-d[i];
                ri=dp[i][j-1][1]+d[j]-d[j-1];
                if(li<=ri) dp[i][j][1]=li,pre[i][j][1]=0;
                else dp[i][j][1]=ri,pre[i][j][1]=1;
                if(t[j]<=dp[i][j][1])
                dp[i][j][1]=inf;
            }
        }
        int l,r;
        int dir;
        if(dp[1][n][0]<inf)
        {
            dir=pre[1][n][0];
            printf("1");
            l=2,r=n;
        }
        else if(dp[1][n][1]<inf)
        {
            dir=pre[1][n][1];
            printf("%d",n);
            l=1,r=n-1;
        }
        else
        {
            puts("Mission Impossible");
            continue;
        }
        while(l<=r)
        {
            if(dir==0)
            {
                dir=pre[l][r][0];
                printf(" %d",l++);
            }
            else
            {
                dir=pre[l][r][1];
                printf(" %d",r--);
            }
        }
        printf("\n");
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_39791208/article/details/81748773