895C - Square Subsets 状压DP + 离散化

C. Square Subsets
time limit per test
4 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output

Petya was late for the lesson too. The teacher gave him an additional task. For some array a Petya should find the number of different ways to select non-empty subset of elements from it in such a way that their product is equal to a square of some integer.

Two ways are considered different if sets of indexes of elements chosen by these ways are different.

Since the answer can be very large, you should find the answer modulo 109 + 7.

Input

First line contains one integer n (1 ≤ n ≤ 105) — the number of elements in the array.

Second line contains n integers ai (1 ≤ ai ≤ 70) — the elements of the array.

Output

Print one integer — the number of different ways to choose some elements so that their product is a square of a certain integer modulo 109 + 7.

Examples
input
4
1 1 1 1
output
15
input
4
2 2 2 2
output
7
input
5
1 2 4 5 8
output
7
Note

In first sample product of elements chosen by any way is 1 and 1 = 12. So the answer is 24 - 1 = 15.

In second sample there are six different ways to choose elements so that their product is 4, and only one way so that their product is 16. So the answer is 6 + 1 = 7.






因为其给的a的数据范围较小,70  ,又其是求平方数,所以考虑质数分解.

其质数只有19个,考虑状压DP

由于n为100000,复杂度太大,考虑离散化


#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const int N=1e5+5;
const int MOD=1e9+7;
int a[N];
int cnt[75];//离散化

ll dp[2][(1<<19)+1];
ll er[N];

int s[75];
int prime[19]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67};

int main()
{
//    freopen("data.txt","r",stdin);
    ios::sync_with_stdio(false);
    for(int i=1;i<=70;i++)
    {
        int t=i;
        for(int j=0;j<19;j++)
        {
            while(t%prime[j]==0)t/=prime[j],s[i]^=(1<<j);
        }
    }

    er[0]=1;
    for(int i=1;i<N;i++)er[i]=(er[i-1]*2)%MOD;

    int n;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
        cnt[a[i]]++;
    }

    dp[0&1][0]=1;
    for(int i=1;i<=70;i++)
    {
        if(!cnt[i])
        {
            for(int j=0;j<(1<<19);j++)
            {
                dp[i&1][j]=dp[(i+1)&1][j];
            }
        }
        else
        {
            for(int j=0;j<(1<<19);j++)
            {
                dp[i&1][j]=0;
            }
            for(int j=0;j<(1<<19);j++)
            {
                dp[i&1][j^s[i]]=(dp[i&1][j^s[i]]+er[cnt[i]-1]*dp[(i+1)&1][j])%MOD;//从cnt[i]个数个选奇数个,C(n,1)+C(n,3)+...=2^(n-1)
                dp[i&1][j]=(dp[i&1][j]+er[cnt[i]-1]*dp[(i+1)&1][j])%MOD;//从cnt[i]个数个选偶数个,C(n,0)+C(n,2)*C(n,4)+...=2^(n-1)
            }
        }
    }

    cout<<(dp[70&1][0]-1)%MOD<<endl;//减去0的情况
    return 0;
}










### C语言实现最小生成树算法 (Prim 和 Kruska) 以下是基于C语言编写的最小生成树算法的示例代码,分别实现了 **Prim** 和 **Kruskal** 算法。这些代码满足题目中的基本要求,并提供了详细的注释以便理解。 #### Prim 算法 Prim 算法的核心是从某个起点出发逐步构建一棵最小生成树,每次选择连接已访问节点和未访问节点之间的最短边[^2]。 ```c #include <stdio.h> #define MAX 100 #define INF 999999 // 辅助函数:寻找下一个加入MST的顶点 int findMinKey(int key[], int mstSet[], int V) { int min = INF, min_index; for (int v = 0; v < V; ++v) if (!mstSet[v] && key[v] < min) min = key[v], min_index = v; return min_index; } void primMST(int graph[][MAX], int V) { int parent[V]; // 存储构造的MST int key[V]; // 关键值用于挑选最小权重边 int mstSet[V]; // 记录是否已经加入了MST // 初始化所有的key为INF,parent为-1 for (int i = 0; i < V; ++i) key[i] = INF, mstSet[i] = 0; key[0] = 0; // 设置第一个顶点的关键值为0 parent[0] = -1; // 第一个节点无父节点 for (int count = 0; count < V - 1; ++count) { int u = findMinKey(key, mstSet, V); mstSet[u] = 1; // 更新相邻顶点的key值 for (int v = 0; v < V; ++v) if (graph[u][v] && !mstSet[v] && graph[u][v] < key[v]) parent[v] = u, key[v] = graph[u][v]; } printf("Edge \tWeight\n"); for (int i = 1; i < V; ++i) printf("%d - %d \t%d \n", parent[i], i, graph[parent[i]][i]); } ``` --- #### Kruskal 算法 Kruskal 算法通过不断选取全局范围内权值最小的边并将其加入到生成树中,直到形成一颗连通的树为止[^1]。 ```c #include <stdio.h> #include <stdlib.h> typedef struct Edge { int src, dest, weight; } Edge; typedef struct Graph { int V, E; Edge* edge; } Graph; Graph* createGraph(int V, int E) { Graph* graph = malloc(sizeof(Graph)); graph->V = V; graph->E = E; graph->edge = malloc(E * sizeof(Edge)); return graph; } // 查找集合代表元 int find_set(int subsets[], int i) { if (subsets[i] == -1) return i; return find_set(subsets, subsets[i]); } // 合并两个子集 void union_set(int subsets[], int x, int y) { int xset = find_set(subsets, x); int yset = find_set(subsets, y); subsets[xset] = yset; } // 主体逻辑 void kruskalMST(Graph* graph) { int V = graph->V; Edge result[V]; int e = 0; int i = 0; qsort(graph->edge, graph->E, sizeof(graph->edge[0]), compare); int subset[V]; for (int v = 0; v < V; ++v) subset[v] = -1; while (e < V - 1 && i < graph->E) { Edge next_edge = graph->edge[i++]; int x = find_set(subset, next_edge.src); int y = find_set(subset, next_edge.dest); if (x != y) { result[e++] = next_edge; union_set(subset, x, y); } } printf("Following are the edges in the constructed MST:\n"); long total_weight = 0; for (i = 0; i < e; ++i){ printf("%d -- %d == %d\n", result[i].src, result[i].dest, result[i].weight); total_weight += result[i].weight; } printf("\nThe Total Weight of Minimum Spanning Tree is :%ld\n",total_weight); } // 排序辅助函数 int compare(const void* a, const void* b) { Edge* a1 = (Edge*)a; Edge* b1 = (Edge*)b; return a1->weight > b1->weight ? 1 : -1; } ``` --- ### 综合程序设计 为了使两种算法能够在一个程序中共存,可以编写如下菜单驱动的功能模块: ```c #include <stdio.h> void menu() { printf("\nMenu Options:"); printf("\n1. Run Prim's Algorithm."); printf("\n2. Run Kruskal's Algorithm."); printf("\n3. Exit.\nYour choice: "); } int main() { int option; do { menu(); scanf("%d", &option); switch(option) { case 1: // Call function to run Prim algorithm. break; case 2: // Call function to run Kruskal algorithm. break; case 3: printf("Exiting program...\n"); break; default: printf("Invalid Option! Please try again.\n"); } } while (option != 3); return 0; } ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值