zoukankan      html  css  js  c++  java
  • HGOI 20181103 题解

    problem:把一个可重集分成两个互异的不为空集合,两个集合里面的数相乘的gcd为1(将集合中所有元素的质因数没有交集)

    solution:显然本题并不是那么容易啊!考场上想了好久。。

    其实转化为上面的题意就简单多了,对于每一个元素分解质因数,就是在筛质数的时候记下每一个合数的最小质因子low[x],然后每一次不停的除low[x]

    得到一个合数x',然后继续除low[x']即可,然后我们统计出含有每一个质因子的数有哪些(用一个二维vector),然后具有同种质因子的数必须放在同一个集合里面,

    也就是说他们可以合并在一起,考虑并查集处理,就把每一个质因子把对应的数全部合并在一起,然后最后统计剩下的没有交集的最终不能再合并的互异块的个数tot

    考虑用tot分成两个不同的非空集合,显然是2 tot -2,快速幂处理即可。

    复杂度O(n log n)

    code:

    # include <bits/stdc++.h>
    # define int long long
    # define pow Pow
    using namespace std;
    const int M=1e6+10;
    const int mo=1e9+7;
    bool prime[M];
    int low[M],a[M],f[M],n;
    vector<int>r[M];
    inline int read()
    {
        int X=0,w=0; char c=0;
        while (!(c>='0'&&c<='9')) w|=c=='-',c=getchar();
        while (c>='0'&&c<='9') X=(X<<1)+(X<<3)+(c^48),c=getchar();
        return w?-X:X;
    }
    void getprime(int limit)
    {
        memset(low,0,sizeof(low));
        memset(prime,true,sizeof(prime));
        for (int i=2;i<=limit;i++) {
            if (!prime[i]) continue;
            for (int j=i+i;j<=limit;j+=i) {
                if (low[j]==0) low[j]=i;
                prime[j]=false;
            }
        }
    }
    int father(int x)
    {
        if (f[x]==x) return x;
        f[x]=father(f[x]);
        return f[x];
    }
    int calc(int x,int y)
    {
        int fx=father(x),fy=father(y);
        f[fx]=fy;
    }
    void solve(int id)
    {
        int num=a[id];
        while (low[num]) {
            r[low[num]].push_back(id);
            int tmp=low[num];
            while (num%tmp==0) num/=tmp;
        }
        r[num].push_back(id);   
    }
    int pow(int x,int n)
    {
        int ans=1;
        while (n) {
            if (n&1) ans=ans*x%mo;
            x=x*x%mo;
            n>>=1;
        }
        return ans%mo;
    }
    signed main()
    {
        int T=read();
        while (T--) {
            n=read();
            int MAX=0;
            for (int i=1;i<=n;i++) {
                a[i]=read(); f[i]=i;
                MAX=max(MAX,a[i]);
            }
            getprime(MAX);
            for (int i=1;i<=M;i++) r[i].clear();
            for (int i=1;i<=n;i++) solve(i);
            for (int i=2;i<=M;i++) {
                if (r[i].size()==0) continue;
                int k=r[i][0];
                for (int j=1;j<r[i].size();j++)  calc(k,r[i][j]);
            }
            int ret=0;
            for (int i=1;i<=n;i++) if (f[i]==i) ret++;
            printf("%lld
    ",(pow(2,ret)%mo-2+mo)%mo); 
        }
        return 0;
    }

    sol:分成两组然后每一组的人都互相认识,显然想到对每一个联通块01染色分成不同的集合

    对于每一个联通块统计出0的块的个数1的块的个数,显然0或者1的个数为n/2最好才会使 calc(i)+calc(n-i)最小

    其中calc(x)=x(x-1)/2,

    由于每一个块我们只能有1或者0,那么设f[i][j]表示前i个块,选择0或者1的块为j是否可能

    转移的话就是

    f[i][j]|=f[i-1][j-a[i].cnt0]|f[i-1][j-a[i].cnt1]

    然后j从n/2向左枚举然后找到若f[n][j]合法

    那么最小化 clac(j)+calc(n-j)即可

    code:

    # include <bits/stdc++.h>
    # define int long long
    using namespace std;
    const int MAXN=1005;
    int mp[MAXN][MAXN],n,m,col[MAXN];
    bool f[MAXN][MAXN];
    int cnt0,cnt1;
    int o;
    struct rec{ int cnt0,cnt1;}b[MAXN];
    void dfs(int u,int fa,int c)
    {
        col[u]=c;
        //printf("%d ; col=%d
    ",u,col[u]);
        if (c==0) cnt0++; else cnt1++;
        for (int v=1;v<=n;v++) {
            if (mp[u][v]==0) continue;
            if (col[v]!=-1) {
                if (col[v]!=c^1) { puts("-1"); exit(0);} 
                else continue;
             }  
             dfs(v,u,c^1);  
        }
    }
    int calc(int x){ return x*(x-1)/2;}
    signed main()
    {
        scanf("%lld%lld",&n,&m);
        for (int i=1;i<=n;i++)
         for (int j=1;j<=n;j++) 
          if (i==j) mp[i][j]=false;
          else mp[i][j]=true;
        while (m--) {
            int u,v; scanf("%lld%lld",&u,&v);
            mp[u][v]=mp[v][u]=false;
        }   
        memset(col,-1,sizeof(col));
        for (int i=1;i<=n;i++) 
         if (col[i]==-1) {
            cnt0=cnt1=0;
            dfs(i,-1,1);
            b[++o].cnt1=cnt1;
            b[++o].cnt0=cnt0;
        }
        //f[i][j]前i个集合,到达A中有j个是否成立
        //f[i][j]|=f[i-1][j-b[i].cnt0]|f[i-1][j-b[i].cnt1]
        memset(f,false,sizeof(f));
        f[1][b[1].cnt1]=f[1][b[1].cnt0]=true;
        for (int i=2;i<=n;i++) 
         for (int j=1;j<=n;j++) 
          f[i][j]=f[i][j]|f[i-1][j-b[i].cnt0]|f[i-1][j-b[i].cnt1];
        int Ans=n*n*2;
        for (int i=0;i<=n;i++)
         if (f[n][i]) Ans=min(Ans,calc(i)+calc(n-i));
        cout<<Ans<<endl; 
        return 0;
    }
  • 相关阅读:
    CocoaPods初试用(1)
    iOS界面调试利器DCIntrospect
    iOS开发中sqlite3基本应用
    禁用iOS7系统自带的pop手势
    iOS7开发~Xcode5制作framework
    完美兼容iOS7自定义的AlertView
    iOS float类型比较大小
    iOS开发应用更新时,须注意事项
    Tesseract 文字识别 运用于ios7 xcode5 任意工程
    oc学习之路----内存管理
  • 原文地址:https://www.cnblogs.com/ljc20020730/p/9899524.html
Copyright © 2011-2022 走看看