比较数学的一场,难度稍大。

Task 1:数数

【问题描述】

 fadbec 很善于数数,⽐如他会数将a 个红球,b 个黄球,c 个蓝球,d个绿球排成⼀列,求出任意相邻不同⾊的方案数⽬。

 现在R 君不知道fadbec 数的对不对,想让你也算⼀算。

 由于数字⽐较⼤,所以请输出除以109 + 7 的余数。

【输入格式】

 ⼀⾏四个正整数a,b,c,d。

【输出格式】

 输出包含⼀个整数,表⽰答案。

【样例输入1】

1 1 1 2

【样例输出1】

36

【数据规模及约定】

 对于前30% 的数据,1 <=a; b; c; d <= 3。

 对于前100% 的数据,1 <= a; b; c; d <= 30。

 直观想法:暴力搜索,判断不同,有30分。

 稍加改造:只有四种球,取s个数的时候对应的有多种不同状态,按状态转移进行DP,注意空间和时间优化,100pts

 比较简单,不做过多解释。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ns s&1
#define ls ~s&1
#define lint long long
using namespace std;
const lint modd=1000000007;
lint a,b,c,d,f[2][31][31][31][5];
inline lint mod(lint x){
return x%modd;
}
inline lint upd(lint s,lint i,lint j,lint k,lint p){
lint sum=0;
for(int pp=0;pp<=4;++pp){
if(pp==p)continue;
sum=mod(sum+f[s][i][j][k][pp]);
}
return mod(sum);
}
int main(){
freopen("count.in","r",stdin);
freopen("count.out","w",stdout);
scanf("%lld%lld%lld%lld",&a,&b,&c,&d);
f[0][0][0][0][0]=1;
lint sum=a+b+c+d;
for(int s=1;s<=sum;++s){
memset(f[ns],0,sizeof(f[ns]));
lint maxi=min(a,sum);
for(int i=0;i<=maxi;++i){
lint maxj=min(b,sum-i);
for(int j=0;j<=maxj;++j){
lint maxk=min(c,sum-i-j);
for(int k=0;k<=maxk;++k){
lint l=s-i-j-k;
if(l<0||l>d)continue;
// for(int p=1;p<=4;++p){
if(i>0)f[ns][i][j][k][1]=mod(f[ns][i][j][k][1]+upd(ls,i-1,j,k,1));
if(j>0)f[ns][i][j][k][2]=mod(f[ns][i][j][k][2]+upd(ls,i,j-1,k,2));
if(k>0)f[ns][i][j][k][3]=mod(f[ns][i][j][k][3]+upd(ls,i,j,k-1,3));
if(l>0)f[ns][i][j][k][4]=mod(f[ns][i][j][k][4]+upd(ls,i,j,k-0,4));
// }
// printf("s=%lld ns=%lld a=%lld b=%lld c=%lld %lld %lld %lld %lld\n",s,ns,i,j,k,f[ns][i][j][k][1],f[ns][i][j][k][2],f[ns][i][j][k][3],f[ns][i][j][k][4]);
}
}
}
}
lint ans=mod(mod(f[sum&1][a][b][c][1]+f[sum&1][a][b][c][2])+mod(f[sum&1][a][b][c][3]+f[sum&1][a][b][c][4]));
printf("%lld\n",ans);
return 0;
}

Task 2:数组

【问题描述】

 fabdec 有⼀个长度为n 的数组a[](下标1-n), 初始时都是0。

 fabdec 随机了⼀个1 到n 的随机数x,并且把a[x]++。

 fabdec 重复了m 次这样的操作,然后数了⼀下数组⾥⼀共有k 个位置为奇数。

  fabdec 现在想问执⾏m 次操作,总共能⽣成多少种不同的数组使得恰好有k 个位置是奇数?(两个数组不同当且仅当两个数组存在某个位置数组的值不相同)

 因为这个数字会很⼤,所以只需输出这个答案除以109 + 7 的余数。

【输入格式】

 ⼀⾏三个整数,n,m,k。

【输出格式】

  输出包含⼀个整数,表⽰答案。

【样例输入】

2 3 1

【样例输出】

4

【数据规模及约定】

 对于前20% 的数据,1 <= n;m <= 4。

 对于前50% 的数据,1 <= n;m <= 2000。

 对于前100% 的数据,1 <= n;m <= 100000, 0 <= k <= n。

 考虑方法:

 直接硬搞搜索,20pts

 直接DP,50pts

 正解的想法比较巧妙:

 奇数的本质其实就是二进制位下最低位为1,所以k位位奇数,那就在k位上添加上一个1,选择方法有C(n,k)种。剩下的数拆分成多个2的形式,随意分配到每一位上,计算分配类型总数即可。

 由于数学不好,我通过打表得到结论:对于ss=(m-k)/2个2可重复地填到n位上,选择方法有C(n+ss-1,ss)种。

  •  根据乘法原理,答案就是C(n,k)*C(n+ss-1,ss),注意要大力取模。
  •  考虑组合数的计算:因为模数是质数,而且n范围比较大,考虑线性求阶乘逆元。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define MAXN 400010
#define lint long long
using namespace std;
const lint mod=1000000007;
lint n,m,k,fac[MAXN],inv[MAXN];
//put k into n positions;
inline lint __pow(lint x,lint y){//x^y
lint s=1;
while(y!=0){
if(y&1){
y^=1;
s=x*s%mod;
}
y>>=1;
x=x*x%mod;
}
return s;
}
void get_fac(lint maxn){
fac[0]=1;
for(lint i=1;i<=maxn;++i){
fac[i]=i*fac[i-1]%mod;
}
inv[maxn]=__pow(fac[maxn],mod-2);
// printf("inv=%lld\n",inv[n+k]);
for(lint i=maxn;i>=2;--i){
inv[i-1]=inv[i]*i%mod;
}
}
inline lint C(lint x,lint y){
return ((fac[x]*inv[y])%mod)*inv[x-y]%mod;
}
int main(){
freopen("array.in","r",stdin);
freopen("array.out","w",stdout);
scanf("%lld%lld%lld",&n,&m,&k);
if((m-k)&1){
puts("0");
return 0;
}
lint ss=(m-k)>>1;
lint maxn=max(max(n+ss-1,ss),max(n,k));
get_fac(maxn);
// for(int i=1;i<=n+k;++i)printf("%d ",fac[i]);puts("");
lint ans=C(n+ss-1,ss)*C(n,k)%mod;
printf("%lld",ans);
return 0;
}

Task 3:子集【问题描述】

 R 君得到了⼀个集合,⾥⾯⼀共有n 个正整数,R 君对这个集合很感兴趣,通过努⼒钻研,发现了这个集合⼀共有2n 个⼦集。

 现在R 君又对这个集合的⼦集很感兴趣。

 定义⼀个集合的权值是这个集合内所有数字的和的话,那么R 君想问问你,这个集合的权值第K ⼩⼦集是多⼤。

 ps. 涉及到较少数字的long long 输⼊输出,建议使用cin/cout。

【输入格式】

 第⼀⾏两个正整数n,k。

 接下来⼀⾏n 个正整数,表⽰集合内元素。

【输出格式】

 输出⼀个数字,表⽰集合的权值第K ⼩⼦集的权值。

【样例输入】

2 3
1 2

【样例输出】

2
6

【数据规模及约定】

  • 对于前20% 的数据,1 <= n <= 15。
  • 对于前40% 的数据,1 <= n <= 22。
  • 对于前100% 的数据,1 <= n <=35, 1 <= k <= 2^n,1 <= 集合元素<=1000000000

 朴素算法直接dfs构造所有子集,排序求解即可,40pts。

 正解需要使用meet-in-the-middle的思想。正向搜索搜索树的大小是235的,是不可接受的。如果两端同时开始搜索,就可以让搜索树大小变成216+215。但是对于中间合并的时候要仔细考虑,避免复杂度退化成216^2。所以想到双向搜索后,本题的核心问题就成为了怎么把分开的两个小集合最终合并成一个大集合。

 这里我们考虑二分答案,二分第k大集合的数值,判断该数值不小于的集合数与k的比较。其中判断函数中的方法类似于“悬线法”的思想,把子集排序后用两个指针来记录答案总数。

 顺带一提,预处理拆分的子集构造还有一种更容易写的方法-使用lowbit构造。但是因为本蒟蒻不会+难理解+实测更慢,在这里本蒟蒻选择搜索构造。

#include<cstdio>
#include<iostream>
#include<algorithm>
#define lint long long
using namespace std;
const int MAXN=(1<<18)+5;
lint n,k,sum,arr[40],cnt_1,cnt_2,s1[MAXN],s2[MAXN];
void dfs_1(lint pos,lint val,lint ed){
s1[++cnt_1]=val;
for(lint i=pos;i<=ed;++i){
dfs_1(i+1,val+arr[i],ed);
}
}
void dfs_2(lint pos,lint val,lint ed){
s2[++cnt_2]=val;
for(lint i=pos;i<=ed;++i){
dfs_2(i+1,val+arr[i],ed);
}
}
inline bool judge(lint x){
lint p1=1,p2=1,ans=0;
while(s1[p1+1]+s2[p2]<=x&&p1+1<=cnt_1)p1++;
// printf("p1=%lld p2=%lld\n",p1,p2);
while(p2<=cnt_2){
while(s2[p2]+s1[p1]>x&&p1>=0)p1--;
if(p1<0)break;
// printf("ans+=%d\n",p1);
ans+=p1;
p2++;
}
// printf("x=%lld ans=%lld\n",x,ans);
return ans>=k;
}
int main(){
freopen("subset.in","r",stdin);
freopen("subset.out","w",stdout);
cin>>n>>k;
for(int i=1;i<=n;++i){
cin>>arr[i];
sum+=arr[i];
}
lint bg_1=1,bg_2=n/2+1;
lint ed_1=n/2,ed_2=n;
dfs_1(bg_1,0,ed_1);
dfs_2(bg_2,0,ed_2);
/*
思路:
- 把集合分成两半
- 预处理这两个集合的所有子集
- 二分答案
*/
sort(s1+1,s1+1+cnt_1);
sort(s2+1,s2+1+cnt_2);
lint l=1,r=sum;
while(r>l+1){
lint mid=(l+r)>>1;
if(judge(mid)){
r=mid;
}else{
l=mid;
}
}
printf("%lld",r);
}

【清北学堂2018-刷题冲刺】Contest 3的更多相关文章

  1. 2017 清北济南考前刷题Day 7 afternoon

    期望得分:100+100+30=230 实际得分:100+100+30=230 1. 三向城 题目描述 三向城是一个巨大的城市,之所以叫这个名字,是因为城市中遍布着数不尽的三岔路口.(来自取名力为0的 ...

  2. 2017 清北济南考前刷题Day 1 afternoon

    期望得分:80+30+70=180 实际得分:10+30+70=110 T1 水题(water) Time Limit:1000ms   Memory Limit:128MB 题目描述 LYK出了道水 ...

  3. 2017 清北济南考前刷题Day 3 morning

    实际得分:100+0+0=100 T1 右上角是必败态,然后推下去 发现同行全是必胜态或全是必败态,不同行必胜必败交叉 列同行 所以n,m 只要有一个是偶数,先手必胜 #include<cstd ...

  4. 2017 清北济南考前刷题Day 3 afternoon

    期望得分:100+40+100=240 实际得分:100+40+100=240 将每个联通块的贡献乘起来就是答案 如果一个联通块的边数>点数 ,那么无解 如果边数=点数,那么贡献是 2 如果边数 ...

  5. 2017 清北济南考前刷题Day 4 afternoon

    期望得分:30+50+30=110 实际得分:40+0+0=40 并查集合并再次写炸... 模拟更相减损术的过程 更相减损术,差一定比被减数小,当被减数=减数时,停止 对于同一个减数来说,会被减 第1 ...

  6. 2017 清北济南考前刷题Day 7 morning

    期望得分:100+50+20=170 实际得分:10+50+20=80 1. 纸牌 题目描述 在桌面上放着n张纸牌,每张纸牌有两面,每面都写着一个非负整数.你的邪王真眼可以看到所有牌朝上的一面和朝下的 ...

  7. 2017 清北济南考前刷题Day 6 afternoon

    期望得分:100+100+30=230 实际得分: 正解: 枚举最高的位,这一位m是1但实际用了0 然后剩余的低位肯定是 正数就用1,负数用0 考场思路:数位DP #include<cstdio ...

  8. 2017 清北济南考前刷题Day 6 morning

    T1 贪心 10 元先找5元 20元 先找10+5,再找3张5 #include<cstdio> using namespace std; int m5,m10,m20; int main ...

  9. 2017 清北济南考前刷题Day 5 afternoon

    期望得分:100+100+30=230 实际得分:0+0+0=30 T1 直接模拟 #include<cstdio> #include<iostream> using name ...

  10. 2017 清北济南考前刷题Day 5 morning

    期望得分:100+100+0=200 实际得分: 坐标的每一位不是0就是1,所以答案就是 C(n,k) #include<cstdio> #include<iostream> ...

随机推荐

  1. BZOJ1398Vijos1382寻找主人 Necklace——最小表示法

    题目描述 给定两个项链的表示,判断他们是否可能是一条项链. 输入 输入文件只有两行,每行一个由0至9组成的字符串,描述一个项链的表示(保证项链的长度是相等的). 输出 如果两条项链不可能同构,那么输出 ...

  2. UESTC482-Charitable Exchange-bfs优先队列

    #include <cstring> #include <algorithm> #include <iostream> #include <queue> ...

  3. 【XSY2745】装饰地板 状压DP 特征多项式

    题目大意 你有\(s_1\)种\(1\times 2\)的地砖,\(s_2\)种\(2\times 1\)的地砖. 记铺满\(m\times n\)的地板的方案数为\(f(m,n)\). 给你\(m, ...

  4. apache 与 tomcat、PHP 关系

    Apache:web网络服务器,只支持静态网页,如HTML,C语言开发的 Tomcat:web网络服务器,是apache的扩展,且是个java代码解释器,可脱离apache独立使用,Servlet.J ...

  5. SCOI2016幸运数字(树剖/倍增/点分治+线性基)

    题目链接 loj luogu 题意 求树上路径最大点权异或和 自然想到(维护树上路径)+ (维护最大异或和) 那么有三种方法可以选择 1.树剖+线性基 2.倍增+线性基 3.点分治+线性基 至于线性基 ...

  6. BZOJ4671 异或图(容斥+线性基)

    题意 定义两个结点数相同的图 \(G_1\) 与图 \(G_2\) 的异或为一个新的图 \(G\) ,其中如果 \((u, v)\) 在 \(G_1\) 与 \(G_2\) 中的出现次数之和为 \(1 ...

  7. 用Python爬取"王者农药"英雄皮肤 原

    padding: 10px; border-bottom: 1px solid #d3d3d3; background-color: #2e8b57; } .second-menu-item { pa ...

  8. 自学华为IoT物联网_03 公共事业物联网常见问题及解决方案

    点击返回自学华为IoT物流网 自学华为IoT物联网_03 公共事业物联网常见问题及解决方案 本文从以下六项公共事业,看看物联网的解决方案: 停车问题 路灯管理问题 消防栓管理问题 井盖管理问题 水表管 ...

  9. Nifi flow 备份恢复

      1. 备份  copy 下面的文件到本地: conf/flow.xml.gz   2. 恢复 新安装一个nifi的环境,把这个文件copy到conf文件夹下 运行nifi, 在界面上create ...

  10. virtio,macvtap,sriov

    本文转自http://xiaoli110.blog.51cto.com/1724/1558984 一个完整的数据包从虚拟机到物理机的路径是: 虚拟机--QEMU虚拟网卡--虚拟化层--内核网桥--物理 ...