关闭

匹配 解题报告

标签: 分治特殊数据匹配dp
521人阅读 评论(0) 收藏 举报
分类:

求一棵树的最大匹配以及最大匹配方案数模m。

n1.5106,m109

时间限制:2s
空间限制:32MB
= =这是一道卡内存傻题,一个显然的DP方程是设f(n,0/1)为这个点向其父亲的边选或不选这个子树能得到的最大匹配,然后g(n,0/1)是其方案数,然后随便转移一下就好了。
注意到状态有4*n个,而内存太小了,最多只能开5*n的数组。。我感觉遍历一棵树似乎至少就需要3*n,所以说DP的状态必须要动态地记录。
然后我使用的沙茶方法是一种关于直径的分治,就是每次选出一条直径,然后从下向上DP,不在直径上的就递归处理,这样每一层的空间复杂度都是O(1)。显然,每一层的直径都是严格小于上一层的,直径长度大于n的显然不会超过n个,而直径长度小于n的显然也不会超过n个,所以这玩意儿的层数是n的。空间复杂度O(n),时间复杂度O(n),然而妈蛋常数太大差点T掉。
但是蛋疼的地方就是初始化的时候我默认把叶子节点的g初始化为1,然后对于n=1,m=1的时候跪了。这已经是我第二次因为模的数等于1跪掉了,所以说对于m有范围的情况一定要试m=1的情况!
然而我考完以后才发现这其实只要按链剖一样按size找就可以轻易地把空间复杂度降到O(log2n)的。。而且不知道怎么回事改过的代码一直被卡常数,然后把当年因为怕内存爆被改成bitset的一个长度为n的布尔数组改了回来就A了。。
代码(n沙茶分治):

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<bitset>
using namespace std;
bitset<1500005> flag;
int succ[3000005],ptr[1500005],next[3000005],etot=1;
void in(int &x){
    char c=getchar();
    while(c<'0'||c>'9')c=getchar();
    x=0;
    for(;c>='0'&&c<='9';c=getchar())x=x*10+(c^'0');
}
void addedge(int u,int v){
    next[etot]=ptr[u],ptr[u]=etot,succ[etot++]=v;
}

int m,n;
//next[1..n]表示i的儿子们在哪里开始的,succ[1..n]存它的儿子们,ptr[1..n]存它的父亲们。 
//next[n+1+1..n+1+n]表示其儿子们的最大深度,succ[n+1..n+n]存每个点的depth 
struct DS{
    int f[2],g[2];
};
typedef long long LL;
#define inf 1000000000
DS dfs(int node,int depth){
    int i,Maxson;
    while(next[node]!=next[node+1]){
        succ[n+node]=depth;
        Maxson=succ[next[node]];
        for(i=next[node];++i<next[node+1];)
            if(next[n+1+succ[i]]>next[n+1+Maxson])
                Maxson=succ[i];
        node=Maxson;
    }
    succ[n+node]=depth;
    DS ans,sps,Max,now;
    for(;;){
        ans=(DS){{0,1},{1%m,1%m}},Max=(DS){{inf,-inf},{1%m,1%m}};
        for(i=next[node];i<next[node+1];++i){
            if(succ[n+succ[i]])now=sps;
            else now=dfs(succ[i],depth+1);
            if(now.f[1]-now.f[0]>Max.f[1]-Max.f[0]){
                Max=now;
                Max.g[0]=(LL)ans.g[1]*now.g[1]%m;
                if(now.f[1]-now.f[0]==0)Max.g[0]=(Max.g[0]+(LL)ans.g[1]*now.g[0])%m;
            }
            else
                if(now.f[1]-now.f[0]==Max.f[1]-Max.f[0])Max.g[0]=((LL)Max.g[0]*now.g[0]+(LL)ans.g[1]*now.g[1])%m;
                else Max.g[0]=(LL)Max.g[0]*now.g[0]%m;
            ans.f[1]+=now.f[0];
            ans.g[1]=(LL)ans.g[1]*now.g[0]%m;
        }
        if(Max.f[1]-Max.f[0]>=0){
            ans.g[0]=Max.g[0];
            ans.f[0]=ans.f[1]-1+Max.f[1]-Max.f[0];
        }
        else{
            ans.g[0]=ans.g[1];
            ans.f[0]=ans.f[1]-1;
        }
        sps=ans;
        //cout<<node<<":"<<ans.f[0]<<","<<ans.f[1]<<" "<<ans.g[0]<<","<<ans.g[1]<<endl;
        if(succ[n+ptr[node]]==succ[n+node])node=ptr[node];
        else return ans;
    }
}
int main(){
    freopen("match.in","r",stdin);
    freopen("match.out","w",stdout);
    int i,u,v,j;
    in(n);
    for(i=n;--i;){
        in(u),in(v);
        addedge(u,v),addedge(v,u);
    }
    in(m);
    //dfs出父亲来。 
    int node=1,tmp;
    flag[1]=1;
    while(node){
        if(!flag[succ[ptr[node]]]){
            flag[succ[ptr[node]]]=1;
            tmp=node;
            node=succ[ptr[node]];
            ptr[tmp]=next[ptr[tmp]];
        }
        else if(next[ptr[node]])swap(succ[ptr[node]],succ[next[ptr[node]]]);
        else{
            ptr[node]=succ[ptr[node]];
            node=ptr[node];
        }
    }
    //按父亲排序 
    //next[n+2..n+n]作为新next,succ[n+1..n]作为新的表头。 
    //ptr[1..n]是父亲,next[1..n]作为分界点。 
    memset(succ,0,sizeof(succ));
    memset(next,0,sizeof(next));
    for(i=1;i<=n;++i)next[i+n]=succ[n+ptr[i]],succ[n+ptr[i]]=n+i;
    next[1]=1;
    for(i=1;i<=n;++i){
        next[i+1]=next[i];
        for(j=succ[n+i];j;j=next[j])succ[next[i+1]++]=j-n;
    }
    for(i=1;i<=n;++i)next[n+2+i]=0;
    //求最大深度 
    flag.reset();
    for(i=n;i;--i)succ[n+i]=next[i];
    node=1;
    while(node){
        if(succ[n+node]==next[node+1]){
            ++next[n+1+node];
            flag[node]=1;
            node=ptr[node]; 
        }
        else
            if(!flag[succ[succ[n+node]]])node=succ[succ[n+node]];
            else next[n+1+node]=max(next[n+1+node],next[n+1+succ[succ[n+node]++]]);
    }
    for(i=n;i;--i)succ[n+i]=0;
    //dp
    DS ans=dfs(1,1);
    printf("%d\n%d\n",ans.f[0],ans.g[0]);
}

代码(链剖):

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<iostream>
#include<bitset>
using namespace std;
bool flag[1500005];
int succ[3000005],ptr[1500005],next[3000005],etot=1;
void in(int &x){
    char c=getchar();
    while(c<'0'||c>'9')c=getchar();
    x=0;
    for(;c>='0'&&c<='9';c=getchar())x=x*10+(c^'0');
}
void addedge(int u,int v){
    next[etot]=ptr[u],ptr[u]=etot,succ[etot++]=v;
}

int m,n;
//next[1..n]表示i的儿子们在哪里开始的,succ[1..n]存它的儿子们,ptr[1..n]存它的父亲们。 
//next[n+1+1..n+1+n]表示其儿子们的最大深度,succ[n+1..n+n]存每个点的depth 
struct DS{
    int f[2];
    long long g[2];
};
#define inf 1000000000
DS dfs(int node,int depth){
    int i,Maxson;
    while(next[node]!=next[node+1]){
        succ[n+node]=depth;
        Maxson=succ[next[node]];
        for(i=next[node];++i<next[node+1];)
            if(next[n+1+succ[i]]>next[n+1+Maxson])
                Maxson=succ[i];
        node=Maxson;
    }
    succ[n+node]=depth;
    DS ans,sps,Max,now;
    for(;;){
        ans=(DS){{0,1},{1%m,1%m}},Max=(DS){{inf,-inf},{1%m,1%m}};
        for(i=next[node];i<next[node+1];++i){
            if(succ[n+succ[i]])now=sps;
            else now=dfs(succ[i],depth+1);
            if(now.f[1]-now.f[0]>Max.f[1]-Max.f[0]){
                Max=now;
                Max.g[0]=ans.g[1]*now.g[1]%m;
                if(now.f[1]-now.f[0]==0)Max.g[0]=(Max.g[0]+ans.g[1]*now.g[0])%m;
            }
            else
                if(now.f[1]-now.f[0]==Max.f[1]-Max.f[0])Max.g[0]=(Max.g[0]*now.g[0]+ans.g[1]*now.g[1])%m;
                else Max.g[0]=Max.g[0]*now.g[0]%m;
            ans.f[1]+=now.f[0];
            ans.g[1]=ans.g[1]*now.g[0]%m;
        }
        if(Max.f[1]-Max.f[0]>=0){
            ans.g[0]=Max.g[0];
            ans.f[0]=ans.f[1]-1+Max.f[1]-Max.f[0];
        }
        else{
            ans.g[0]=ans.g[1];
            ans.f[0]=ans.f[1]-1;
        }
        sps=ans;
        //cout<<node<<":"<<ans.f[0]<<","<<ans.f[1]<<" "<<ans.g[0]<<","<<ans.g[1]<<endl;
        if(succ[n+ptr[node]]==succ[n+node])node=ptr[node];
        else return ans;
    }
}
int main(){
    freopen("match.in","r",stdin);
    freopen("match.out","w",stdout);
    int i,u,v,j;
    in(n);
    for(i=n;--i;){
        in(u),in(v);
        addedge(u,v),addedge(v,u);
    }
    in(m);
    //dfs出父亲来。 
    int node=1,tmp;
    flag[1]=1;
    //printf("%f\n",(double)clock()/CLOCKS_PER_SEC);
    while(node){
        if(!flag[succ[ptr[node]]]){
            flag[succ[ptr[node]]]=1;
            tmp=node;
            node=succ[ptr[node]];
            ptr[tmp]=next[ptr[tmp]];
        }
        else if(next[ptr[node]])swap(succ[ptr[node]],succ[next[ptr[node]]]);
        else{
            ptr[node]=succ[ptr[node]];
            node=ptr[node];
        }
    }
    //printf("%f\n",(double)clock()/CLOCKS_PER_SEC);
    //按父亲排序 
    //next[n+2..n+n]作为新next,succ[n+1..n]作为新的表头。 
    //ptr[1..n]是父亲,next[1..n]作为分界点。 
    memset(succ,0,sizeof(succ));
    memset(next,0,sizeof(next));
    for(i=1;i<=n;++i)next[i+n]=succ[n+ptr[i]],succ[n+ptr[i]]=n+i;
    next[1]=1;
    for(i=1;i<=n;++i){
        next[i+1]=next[i];
        for(j=succ[n+i];j;j=next[j])succ[next[i+1]++]=j-n;
    }
    memset(next+n+3,0,sizeof(int)*n);
    //printf("%f\n",(double)clock()/CLOCKS_PER_SEC);
    //求size 
    memset(flag,0,sizeof(flag));
    for(i=n;i;--i)succ[n+i]=next[i];
    node=1;
    while(node){
        if(succ[n+node]==next[node+1]){
            ++next[n+1+node];
            flag[node]=1;
            node=ptr[node]; 
        }
        else
            if(!flag[succ[succ[n+node]]])node=succ[succ[n+node]];
            else next[n+1+node]+=next[n+1+succ[succ[n+node]++]];
    }
    memset(succ+n+1,0,sizeof(int)*n);
    //printf("%f\n",(double)clock()/CLOCKS_PER_SEC);
    //dp
    DS ans=dfs(1,1);
    //printf("%f\n",(double)clock()/CLOCKS_PER_SEC);
    printf("%d\n%I64d\n",ans.f[0],ans.g[0]);
}

总结:
①一定要试极限数据啊!!!尤其是模的数有范围的情况!
②如果不是为了位运算还是不要用bitset的好,直接布尔数组快多了。。
③不要用什么沙茶n直径分治了,被链剖log2n虐爆了有木有!

0
0
查看评论

GROUP BY 排序的问题

 我希望每次用 GROUP BY 排序完都加 相应的合计该怎么做? 如:我用select A,B,C from Table group by A,B,C查出结果为   A  B  C  VALUE   a1  b1  c...
  • lsd123
  • lsd123
  • 2008-07-22 11:24
  • 948

poj解题报告——2313

开始假设b[i] = a[i](1 Mid(b[i - 1], a[i], b[i + 1]) (2 <= i <= n - 1), mid(x, y, z)表示x, y, z中数值居中间的数}这个画下数轴就知道了,则b[i]=mid(b[i - 1], a[i], b[i + 1...
  • Lingfu74
  • Lingfu74
  • 2015-07-28 13:36
  • 555

NOIP2016提高组解题报告

D1T1玩具谜题   模拟 D1T2天天爱跑步   LCA,树链剖分 D1T3换教室  DP,数学期望,最短路 D2T1组合数问题   数学 D2T2蚯蚓   单调队列,模拟 D2T3愤怒的小鸟...
  • qwerty1125
  • qwerty1125
  • 2017-09-10 20:42
  • 276

算法分析与设计解题报告

解题报告格式:         ①原题中文大意; ②算法思想及解题用到的主要数据结构; ③详细解题思路; ④逐步求精算法描述(含过程及变量说明); ⑤程序注释清单(重要过程的说明); ⑥测试数据(5-10组...
  • u011394079
  • u011394079
  • 2014-09-24 11:42
  • 258

HDU 1254 推箱子 解题报告

HDU 1254的解题报告
  • lawk97
  • lawk97
  • 2016-09-05 10:17
  • 392

C语言解题报告模板

问题及代码:/* Copyright (c) 2014, 烟台大学计算机学院 All rights reserved. 文件名称:test.cpp 作 者:贺利坚 完成日期:2016年 10 月 x 日 版 本 号:v1.0 题目描述  计算并输出它们串联电阻值 输入 ...
  • sxhelijian
  • sxhelijian
  • 2016-09-17 11:10
  • 784

杭电ACM1800解题报告

Flying to the Mars Time Limit: 5000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 1730...
  • Aqin556
  • Aqin556
  • 2016-07-27 11:12
  • 517

noi2016解题报告

D1T1: 首先转化成统计AA型字符串有几种。 st[i]表示从i位置开始的AA型字符串有几个,ed[i]表示到i结束的有几个。 ans=∑st[i]*ed[i-1] 然后枚举A的长度L(AA长度的一半),i=k*L,j=(k+1)*L 观察x=lcp(i,j)和y=lcs(i-1,j-1...
  • wzf_2000
  • wzf_2000
  • 2017-05-21 19:38
  • 524

NOIP2017普及组题解

1【分析】 直接输出a∗0.3+b∗0.2+c∗0.5a*0.3+b*0.2+c*0.5即可,代码不给了。2【分析】 有2种做法,第一种是求出10x10^x,然后把每个书的号码模上一下,判断和y是否相等;第二种是用字符串,copy出后x个字符和y比较。3【分析】
  • qq_31640513
  • qq_31640513
  • 2017-11-11 19:02
  • 1895

NOIP解题报告

NOIP解题报告 day1 T1  题目链接 其实就是一个简单的模拟,只要分清方向,然后取模的时候细心一点就可以啦。 #include #include #include #include #include #define N 110003 using namespace std; s...
  • clover_hxy
  • clover_hxy
  • 2016-12-01 18:37
  • 246
    个人资料
    • 访问:174530次
    • 积分:3634
    • 等级:
    • 排名:第10687名
    • 原创:187篇
    • 转载:1篇
    • 译文:0篇
    • 评论:25条
    最新评论