删数方案数(regex)

[题目描述]

给出一个正整数序列 a,长度为 n,cyb 不喜欢完美,他要删掉一些数(也可以不删,即删掉0个),但是他不会乱删,他希望删去以后,能将 a 分成 2 个集合,使得两个非空集合的数的和相同,现在他希望你能帮他算出删数的方案数。

[输入文件]

第一行 n 个正整数

以下有 n行,每行1个

正整数表示整数序列a

[输出文件]

一个整数表示答案

[输入样例]

4

1 2 3 4

[输出样例]

3

[数据范围]

30%:n<=5

100%:n<=20

100%:a 中每个元素<=100000000

题解:

对于前半部分和后半部分dfs枚举

将前半部分得到的值包括状态存进hash(去重),在把后半部分的所有状态去重在hash中查找

本来是用的三进制数表示存进子集A,存进子集B,删去3种状态,后面发现没有必要

因为是“删数的方案”,也就是除删去的数,AB集合间如何分配并不关心,所以直接二进制就行

注意hash不能只存值,还要保存二进制数以判重,保存二进制数的数组不能太小也不能大

hash的大小70000够了,状态数组zt[70000][700]正好AC,500则90分,100则75分

博客里上传了数据,在管理里的文件

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 using namespace std;
 6 struct Node
 7 {
 8     long long s;
 9     int p;
10 }f[70001];
11 int n,zt[70001][701],len,h[1200001];
12 long long ans,has[70001],sp[70001],inf,a[21];
13 bool cmp(Node a,Node b)
14 {
15     return (a.s<b.s||(a.s==b.s&&a.p<b.p));
16 }
17 void push_hash(long long x,int i)
18 {int j;
19     long long p=(x+1000000000)%70000;
20      while (has[p]!=inf&&has[p]!=x)
21      {
22             p++;
23             if (p>70000) p=1;
24      }
25      if (has[p]==inf)
26      {
27         has[p]=x;
28         sp[p]=1;
29         zt[p][sp[p]]=i;
30      }
31      else if (has[p]==x)
32      {
33         for (j=1;j<=sp[p];j++)
34          if (i==zt[p][j]) return;
35          sp[p]++;
36          zt[p][sp[p]]=i;
37      }
38 }
39 void ask_hash(long long x,int i)
40 {int j;
41     long long p=(x+1000000000)%70000;
42      while (has[p]!=inf&&has[p]!=x)
43      {
44             p++;
45             if (p>70000) p=1;
46      }
47      if (has[p]==x)
48      {
49         for (j=1;j<=sp[p];j++)
50         h[zt[p][j]|i]=1;
51      }
52 }
53 void dfs1(int x,long long s,int p)
54 {int i;
55     if (x>n/2) push_hash(s,p);
56     else
57     for (i=-1;i<=1;i++)
58     dfs1(x+1,s+i*a[x],p|((i!=0)<<(x-1)));
59 }
60 void dfs2(int x,long long s,int p)
61 {int i;
62     if (x>n) f[++len]=(Node){s,p};
63     else
64     for (i=-1;i<=1;i++)
65     dfs2(x+1,s+i*a[x],p|((i!=0)<<(x-1)));
66 }
67 int main()
68 {int i,j;
69 long long s;
70 freopen("regex.in","r",stdin);
71 freopen("regex.out","w",stdout);
72     cin>>n;
73     memset(has,-127,sizeof(has));
74     inf=has[0];
75     for (i=1;i<=n;i++)
76      scanf("%lld",&a[i]);
77     dfs1(1,0,0);dfs2(n/2+1,0,0);
78     sort(f+1,f+len+1,cmp);
79      for (i=1;i<len;i++)
80       if (f[i].s==f[i+1].s&&f[i].p==f[i+1].p) f[i].s=(1<<30);
81     sort(f+1,f+len+1,cmp);
82     while (f[len].s==(1<<30)) len--;
83     for (i=1;i<=len;i++)
84     ask_hash(-f[i].s,f[i].p);
85     for (i=1;i<=(1<<n)-1;i++) ans+=h[i];
86     //cout<<h[i]<<endl;
87     cout<<ans;
88 }
时间: 2024-09-30 21:17:57

删数方案数(regex)的相关文章

[来源不详]删数方案数

Description 给出一个正整数序列 a,长度为 n,cyb 不喜欢完美,他要删掉一些数(也可以不删,即删掉0个),但是他不会乱删,他希望删去以后,能将 a 分成 2 个集合,使得两个非空集合的数的和相同,现在他希望你能帮他算出删数的方案数. Input 第一行 n 个正整数 以下有 n行,每行1个 正整数表示整数序列a Output 一个整数表示答案 Sample Input 4 1 2 3 4 Sample Output 3 Hint 30%:n<=5 100%:n<=20 100%

LuoGu 1108 低价购买 LIS方案数

一句话题意:两个序列的LIS的长度和方案数 首先这里n方的应该是要比nlog的好的,因为还涉及到一个求方案 主要考虑下第二问, 不同的方案数应该怎么求 实际上 对于一个长为len的子序列, 它的方案数即为所有长为len-1的,且可以转移到它的子序列 (都是满足题意的)的方案数之和 这样就比较好办了,直接在原本的求LIS的转移里搞搞就行了. 那么答案就是所有长度为LIS的方案数之和了. 但是还有一个问题,这样的话根本没考虑方案是否重复. 那么怎么样把相同的去掉呢?? 我们把长度相等,当前对应位上的

UVa 11137 (完全背包方案数) Ingenuous Cubrency

题意:用13.23……k3这些数加起来组成n,输出总方案数 d(i, j)表示前i个数构成j的方案数则有 d(i, j) = d(i-1, j) + d(i, j - i3) 可以像01背包那样用滚动数组来实现 1 //#define LOCAL 2 #include <iostream> 3 #include <cstdio> 4 #include <cstring> 5 using namespace std; 6 7 const int maxn = 10; 8

洛谷P1108 低价购买[DP | LIS方案数]

题目描述 “低价购买”这条建议是在奶牛股票市场取得成功的一半规则.要想被认为是伟大的投资者,你必须遵循以下的问题建议:“低价购买:再低价购买”.每次你购买一支股票,你必须用低于你上次购买它的价格购买它.买的次数越多越好!你的目标是在遵循以上建议的前提下,求你最多能购买股票的次数.你将被给出一段时间内一支股票每天的出售价(2^16范围内的正整数),你可以选择在哪些天购买这支股票.每次购买都必须遵循“低价购买:再低价购买”的原则.写一个程序计算最大购买次数. 这里是某支股票的价格清单: 日期 1 2

CF GYM100548 (相邻格子颜色不同的方案数 2014西安区域赛F题 容斥原理)

n个格子排成一行,有m种颜色,问用恰好k种颜色进行染色,使得相邻格子颜色不同的方案数. integers n, m, k (1 ≤n, m ≤ 10^9, 1 ≤ k ≤ 10^6, k ≤ n, m). m种颜色取k种 C(m, k) 这个可以放最后乘 那么问题就变成只用k种颜色第一个格子有k种涂法 第二个有k-1种 第三个也是k-1种 一共就是k*(k-1)^(n-1) 这种算法仅保证了相邻颜色不同,总颜色数不超过k种,并没有保证恰好出现k种颜色 也就是多算了恰好出现2种 恰好出现3种...

UESTC 900 方老师炸弹 --Tarjan求割点及删点后连通分量数

Tarjan算法. 1.若u为根,且度大于1,则为割点 2.若u不为根,如果low[v]>=dfn[u],则u为割点(出现重边时可能导致等号,要判重边) 3.若low[v]>dfn[u],则边(u,v)为桥(封死在子树内),不操作. 求割点时,枚举所有与当前点u相连的点v: 1.是重边: 忽略 2.是树边: Tarjan(v),更新low[u]=min(low[u],low[v]); 子树个数cnt+1.如果low[v] >= dfn[u],说明是割点,割点数+1 3.是回边: 更新lo

NOIP2012pj摆花[DP 多重背包方案数]

题目描述 小明的花店新开张,为了吸引顾客,他想在花店的门口摆上一排花,共m盆.通过调查顾客的喜好,小明列出了顾客最喜欢的n种花,从1到n标号.为了在门口展出更多种花,规定第i种花不能超过ai盆,摆花时同一种花放在一起,且不同种类的花需按标号的从小到大的顺序依次摆列. 试编程计算,一共有多少种不同的摆花方案. 输入输出格式 输入格式: 第一行包含两个正整数n和m,中间用一个空格隔开. 第二行有n个整数,每两个整数之间用一个空格隔开,依次表示a1.a2.……an. 输出格式: 输出只有一行,一个整数

hdu 2157 从a点走到b点刚好k步的方案数是多少 (矩阵快速幂)

n个点 m条路 询问T次 从a点走到b点刚好k步的方案数是多少 给定一个有向图,问从A点恰好走k步(允许重复经过边)到达B点的方案数mod p的值把 给定的图转为邻接矩阵,即A(i,j)=1当且仅当存在一条边i->j.令C=A*A,那么C(i,j)=ΣA(i,k)*A(k,j),实际上就 等于从点i到点j恰好经过2条边的路径数(枚举k为中转点).类似地,C*A的第i行第j列就表示从i到j经过3条边的路径数 Sample Input4 4 // n m0 10 21 32 32 //T0 3 2

bzoj1708[Usaco2007 Oct]Money奶牛的硬币(背包方案数dp)

1708: [Usaco2007 Oct]Money奶牛的硬币 Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 763  Solved: 511[Submit][Status][Discuss] Description 在创立了她们自己的政权之后,奶牛们决定推广新的货币系统.在强烈的叛逆心理的驱使下,她们准备使用奇怪的面值.在传统的货币系统中,硬币的面值通常是1,5,10,20或25,50,以及100单位的货币,有时为了更方便地交易,会发行面值为2单位