1 条题解

  • 0
    @ 2026-8-27 10:13:23

    首先这道题有一个加强版,该讲的也都在那边的题解有了。

    双倍经验P3230 [HNOI2013]比赛

    这个题最直接的想法就是暴搜,先搜1v2,1v3……1vN,然后2v3……搜到结尾

    很显然这样做只能拿部分分(好吧我不知道这样究竟能拿多少部分分)

    剪枝:

    1.注意到我们把1队的所有比赛搜完后才开始搜的2,然而此时如果1队的总分已经不与期望得分相同了,那么就可以直接返回0了(88pts)

    2.知道总分和总场数,可以求出所有比赛中胜场和平场。搜索时超过这个值则返回0(92pts)

    3.记忆化搜索,搜完某个人的所有场次后,如果后面所有队剩余分数从小到大排成的序列已经搜过了,那么可以直接返回上次搜到的值,这可以用Hash处理(100pts)

    当然还有一些别的剪枝。比如说,如果某人接下来每场都赢也不能获得期望的分数,那么返回0

    我本来以为前两个剪枝就可以AC这个题然而没有。所以最后这两个题都评上了紫题

    附上一个两边都能过的AC代码

    #include<bits/stdc++.h>
    typedef long long LL;
    const LL maxn=12;
    const LL mod=1e9+7;
    const LL base=28;
    using namespace std;
    
    LL n,ans;
    LL a[maxn],tmp[maxn],cz[maxn];
    LL sx,sy,s; ///sx是总胜场,sy是总平场
    map<LL,LL> M;
    
    LL cmp(LL x,LL y)
    {
        return x>y;
    }
    
    LL dfs(LL x,LL y)
    {
        LL now=0;
        if(x>=n) return 1;
        if(y>n)
        {
            if(tmp[x]!=a[x]) return 0;
            ///处理Hash
            for(LL i=x+1;i<=n;i++) cz[i]=a[i]-tmp[i];
            sort(cz+x+1,cz+n+1);
            LL hsh=0;
            for(LL i=x+1;i<=n;i++) hsh=hsh*base+cz[i];
            if(M.find(hsh)!=M.end()) return M[hsh];
            else return M[hsh]=dfs(x+1,x+2);
        }
        if(tmp[x]+3<=a[x] && sx)
        tmp[x]+=3,sx--,now+=dfs(x,y+1),tmp[x]-=3,sx++;
        if(tmp[y]+3<=a[y] && sx)
        tmp[y]+=3,sx--,now+=dfs(x,y+1),tmp[y]-=3,sx++;
        if(tmp[x]+1<=a[x] && tmp[y]+1<=a[y] && sy)
        tmp[x]+=1,tmp[y]+=1,sy--,now+=dfs(x,y+1),tmp[x]-=1,tmp[y]-=1,sy++;
        return now%mod;
    }
    
    int main()
    {
        scanf("%lld",&n);
        for(LL i=1;i<=n;i++) scanf("%lld",&a[i]),s+=a[i];
        sx=s-n*n+n;sy=(n*n-n)/2-sx; ///这个在纸上解方程
        sort(a+1,a+n+1,cmp); ///啊,从大到小也是很重要的
        printf("%lld",dfs(1,2));
        return 0;
    }
    
    • 1

    信息

    ID
    2959
    时间
    800ms
    内存
    128MiB
    难度
    10
    标签
    递交数
    1
    已通过
    1
    上传者