zoukankan      html  css  js  c++  java
  • BZOJ2327: [HNOI2011]勾股定理

    BZOJ2327: [HNOI2011]勾股定理

    Description


    题解Here!

    这是一道神题。。。

    我一开始把题目看错了,我以为是在$n$根木棒中选两个$i,j$满足$gcd(i,j)==1$,并且存在$k$使得$i^2+j^2==k^2$。

    我说这不是网络流的沙茶题嘛?

    然后数据范围$n<=10^6$。。。

    怎么这么大?就算建图建完了,$Dinic$好像也会$TLE$,预流推进可能都会$TLE$。。。

    然后回头看题——$MD$,我就是个$zz$。。。

    选取若干个。。。

    于是这题就比较好做了。

    这里有一篇$YDC$巨佬的博客讲的比我好:链接

    首先要预处理出$10^6$以内的勾股数对。

    根据初中的经验,我们有:$$(m^2-n^2)^2+(2mn)^2=(m^2+n^2)^2$$

    这个应该都会证吧,全部拆开就好了。

    于是一对互质勾股数$(a,b)$与一对$(i,j)$应满足$$a=j^2-i^2,b=2 imes i imes j,c=j^2+i^2,gcd(i,j)==1,i<j, ext{i和j不同奇偶}$$

    我们枚举$i,j$,由于$i imes j<=frac{10000000}{2}$,并且$gcd$的复杂度上限是$log_2n$,一般不会达到,所以就是$500000 imes log_2 500000<500000 imes 20=10^7$的复杂度。

    我们对于互质勾股数$(a,b)$,在$a,b$间连边。

    我们假设他们构成了一片森林,那么题目变成在森林中选点且互不相邻了,跟P1352 没有上司的舞会很像,经典树形$DP$。

    但是自从我发现这个不是基环无向树就不想做了。

    设$dp[i][0]$表示在$i$这棵子树上不选$i$的方案数,$dp[i][1]$表示在$i$这颗子树上选$i$的方案数。

    状态转移长这样:
    $$dp[i][0]=(dp[v_1][0]+F[v_1][1]) imes (dp[v_2][0]+dp[v_2][1]) imes ...$$
    $$dp[i][1]=(2^{num[i]}-1) imes dp[v_1][0] imes dp[v_2][0] imes ...$$

    $num[i]$表示$i$出现了几次。

    我们发现我们建的图将构成一片森林。

    假设森林里每颗树的根是$rt_1,rt_2,...rt_k$,那么答案就是:
    $$(dp[rt_1][0]+dp[rt_1][1]) imes(dp[rt_2][0]+dp[rt_2][1]) imes ... ext{之后再减1,即去掉空集}$$

    $BUT$!这么做是$30$分。

    我当时懵的一批啊。。。感觉这方法行得通吗???

    然后我发现一个智障一样的问题——图并不一定是树,可能有环。。。

    靠!这个坑。。。

    接下来就开始乱搞了。。。

    对于一条回边$(u,v)$,就像$tarjan$求强连通中的$dfn[v]<dfn[u]$一样,我们把$u,v$标记一下加入点集$stack$,把这条边删掉。

    之后$2^{|stack|}$枚举每个点的选择情况,在合法的情况下套用树形$DP$。

    这个时候由于是同一棵树,所以用加法原理而不是乘法原理,即每次统计的答案用个变量累加,最后乘以这个变量。

    这题就做完了

    吗?

    没有!

    但是因为数据水的原因,出题人强行把$NP$的题出成了普通题。。。

    所以这题就当过了吧。。。

    HN的出题人真是不负责任。。。

    附代码:

    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    #include<vector>
    #define MAXN 1000010
    #define MOD 1000000007
    using namespace std;
    vector<int> stack;
    int n,c=1,d=1,T;
    int head[MAXN],deep[MAXN],vis[MAXN],num[MAXN];
    long long bit[MAXN],dp[MAXN][2];
    bool used[MAXN],choose[MAXN];
    struct Edge{
        int next,to;
    }a[MAXN];
    inline int read(){
        int date=0,w=1;char c=0;
        while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
        while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
        return date*w;
    }
    inline long long sqr(long long x){return x*x;}
    int gcd(int x,int y){
        if(!y)return x;
        return gcd(y,x%y);
    }
    inline void add(int x,int y){
        a[c].to=y;a[c].next=head[x];head[x]=c++;
        a[c].to=x;a[c].next=head[y];head[y]=c++;
    }
    void build(){
        int m=MAXN-10;
        for(int i=1;i<=m/2;i++)
        for(int j=i+1;j<=m/2/i&&sqr(j)-sqr(i)<=m;j++)
        if(((i&1)!=(j&1))&&gcd(i,j)==1)add(2LL*i*j,sqr(j)-sqr(i));
        bit[0]=1;
        for(int i=1;i<=m;i++)bit[i]=(bit[i-1]<<1)%MOD;
    }
    inline void insert(int x){
        if(!used[x]){
            used[x]=true;
            stack.push_back(x);
        }
    }
    bool check(int u,int f){
        vis[u]=T;
        for(int i=head[u];i;i=a[i].next){
            int v=a[i].to;
            if(v==f||!num[v])continue;
            if(choose[u]&&choose[v])return false;
            if(vis[v]!=T)if(!check(v,u))return false;
        }
        return true;
    }
    void dfs1(int x,int f){
        deep[x]=d++;
        for(int i=head[x];i;i=a[i].next){
            int v=a[i].to;
            if(!num[v]||v==f)continue;
            if(!deep[v])dfs1(v,x);
            else if(deep[v]<deep[x]){insert(x);insert(v);}
        }
    }
    void dfs2(int x,int f){
        vis[x]=T;
        dp[x][0]=1;
        dp[x][1]=(bit[num[x]]-1+MOD)%MOD;
        if(used[x]){
            if(choose[x])dp[x][0]=0;
            else dp[x][1]=0;
        }
        for(int i=head[x];i;i=a[i].next){
            int v=a[i].to;
            if(v==f||!num[v]||vis[v]==T)continue;
            if(vis[v]!=T)dfs2(v,x);
            dp[x][0]=dp[x][0]*(dp[v][0]+dp[v][1])%MOD;
            dp[x][1]=dp[x][1]*dp[v][0]%MOD;
        }
    }
    void dfs3(int i,int m,int x,long long &ans){
        if(i==m){
            T++;
            if(check(x,-1)){
                T++;
                dfs2(x,-1);
                ans=(ans+dp[x][0]+dp[x][1])%MOD;
            }
            return;
        }
        choose[stack[i]]=false;
        dfs3(i+1,m,x,ans);
        choose[stack[i]]=true;
        dfs3(i+1,m,x,ans);
    }
    long long solve(int x){
        stack.clear();
        dfs1(x,-1);
        int m=stack.size();
        long long ans=0;
        dfs3(0,m,x,ans);
        return ans;
    }
    void work(){
        long long ans=1;
        for(int i=1;i<=MAXN-10;i++)if(num[i]&&!deep[i])ans=ans*solve(i)%MOD;
        printf("%lld
    ",(ans-1+MOD)%MOD);
    }
    void init(){
        n=read();
        for(int i=1;i<=n;i++){
            int x=read();
            num[x]++;
        }
    }
    int main(){
        build();
        init();
        work();
        return 0;
    }
    
  • 相关阅读:
    Hdu1711 Number Sequence--Kmp模板题
    Trie入门--Poj3630 Phone List,查单词,HDU1251 统计前缀,PKU2503 Babelfish
    高次幂的组合数表示形式
    BZOJ1697 [Usaco2007 Feb] Cow Sorting牛排序
    1025 [SCOI2009]游戏(置换群,DP)
    Poj1721 Cards
    [Poi2003]Shuffle
    poj 3128 Leonardo's Notebook(置换的幂)
    POJ3734 Block母函数入门
    重心拉格朗日插值法
  • 原文地址:https://www.cnblogs.com/Yangrui-Blog/p/9488125.html
Copyright © 2011-2022 走看看