tc-srm704-div1-1000 解题报告

题意:构造一个点数不超过20无重边无自环的有向图,节点编号从0到n-1,使得从0到n-1的哈密顿路径数量恰好为k。

感觉完全没有思路。。不知道怎么构造。
去看了下别人的代码,原来可以做成1<-2<-…<-18一条路径,然后1-18都向编号比它大的节点连边。这样的话往回走就只有唯一的路径,所以假如说我当前在i,走到了j,j>i,那么就需要立刻从j到j-1直到i+1,然后再往后走。这样就相当于每个节点有选或不选两种状态,如果选的话就是从前面第一个比它小的选的节点跳过来,不选的话就是从i+1过来。所以如果有0->i,就相当于贡献了 218i11i<18) (因为18是必选的,19不能回到18)。

既然不会做的话,那就乱搞一下吧~
假如说随便确定了一个图,怎么求路径数量呢?那就需要 O(220202) 的状压dp。不过我们可以先不求0->n-1的哈密顿路径数量,可以先求 i>19,1i<19 ,然后再连从0出去的边,这样的话就相当于是问是否存在一个子集和恰好为k。
我们知道 (189) 是一个非常大的数了,所以如果i->19的路径数量是在 k9 附近随机,那么就很容易一不小心就凑出个k来!所以我们可以二分边的总数,直到找到一个边的总数在那里随机 i>19 的路径数量的平均值会离 k9 比较近,然后就在那里一直随机。
这样的话时间复杂度是 O(2nn2logn)109 的。所以dp的常数比较要用心卡一下才行。。。我一开始算错了时间复杂度以为是 O(2nnlogn) 的就没管,结果样例都过不去。。。

代码:

#include
using namespace std;
const int n=20,N=20;
typedef long long LL;
class HamiltonianConstruction
{
    public:
        pair<int,int> edge[N*N];
        int etot;
        LL f[1<1<int prev[N];
        int bit[1<inline void cal(int mid)
        {
            random_shuffle(edge,edge+etot);
            memset(prev,0,sizeof(prev));
            for(int i=mid;i--;)prev[edge[i].second-1]|=1<1;
            memset(f,0,sizeof(f));
            f[1<2][n-2]=1;
            for(int i=1;i<1<1;++i)
                for(int j=i,x;j;j^=1<if(f[i][x=bit[j&-j]])
                        for(int k=prev[x]&~i,y;k;k^=y)
                        {
                            y=k&-k;
                            f[i|y][bit[y]]+=f[i][x];
                        }
            for(int i=n-2;i--;)s[1<1<1)-1][i];
            for(int i=1;i<1<2;++i)s[i]=s[i^i&-i]+s[i&-i];
        }
        inline vector<string> construct(int k)
        {
            for(int i=0;i1<for(int i=n-1;--i;)
                for(int j=n;--j;)
                    if(i!=j)
                        edge[etot++]=make_pair(i,j);
            vector<string> ans(N,string(N,'N'));
            int l=0,r=100;
            while(r-l>1)
            {
                int mid=l+r>>1;
                cal(mid);
                for(int i=1<2;i--;)
                    if(s[i]==k)
                    {
                        for(int j=mid;j--;)ans[edge[j].first][edge[j].second]='Y';
                        for(int j=n-2;j--;)
                            if(i>>j&1)
                                ans[0][j+1]='Y';
                        return ans;
                    }
                LL sum=0;
                for(int i=n-2;i--;)sum+=s[1<if(sum>=k<<1)r=mid;
                else l=mid;
            }
            for(;;)
            {
                cal(r);
                for(int i=1<2;i--;)
                    if(s[i]==k)
                    {
                        for(int j=r;j--;)ans[edge[j].first][edge[j].second]='Y';
                        for(int j=n-2;j--;)
                            if(i>>j&1)
                                ans[0][j+1]='Y';
                        return ans;
                    }
            }
        }
};

总结:
①构造的时候可以考虑一些特殊情况,或者根据题目中的特殊性质。比如说构造一个图,就可以先考虑一条链、完全图、树。
②如果没有构造的思路,不妨试试随机化!
③一定要算好时间复杂度!

你可能感兴趣的:(图论,状压dp,构造,随机化,DP)