前言

好像每个题目背景所描述的人都是某部番里的角色,热切好像都挺惨的(情感上的惨)。

然后我只知道 T1 的莓,确实挺惨。。。

T1 莓良心

解题思路

首先答案只与 \(w\) 的和有关系,于是问题就变成了对于一个点求出每一个所在组的大小以及对应的方案数。

考场上想的是枚举组的大小,然后预处理一下 \(n\) 个数字划分为 \(m\) 个非空集合的方案数。

一开始想的是隔板法,显然不对,然后就尝试 DP 计算,就有了 \(f_{i,j}=f_{i-1,j}\times j+f_{i-1,j-1}\)

一直想矩阵快速幂,看了半天才发现这 TM 两维,我优化个鬼!!

考完之后才知道这个是第二类斯特林数,然后才发现这玩意可以 NTT 暴算求出来一行或者一列的值,是我浅薄了。。。

官方题解的做法非常不一样,对于一对数 \(u,v\) 当两者被分到一个组中就会多出来 \(w_u+w_v\) 的贡献。

然后答案就是 \((\sum\limits_{i=1}^n w_i)\times(\;{n \brace k}+(n-1)\times {n-1 \brace k}\;)\)

发现可以直接容斥去求:

\[\displaystyle{n \brace k}=\frac{1}{k!}\sum_{i=0}^k(-1)^i \binom{k}{i}(k-i)^n
\]

然后 \(\mathcal{O}(n)\) 的线性筛或者 \(\mathcal{O}(nlogn)\) 的快速幂均可。

code

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"RP++"<<endl
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int N=1e6+10,M=2e3+10,mod=998244353;
int n,m,ans,base,fac[N],ifac[N] ;
int power(int x,int y,int p=mod)
{
int temp=1;
while(y)
{
if(y&1) temp=temp*x%p;
x=x*x%p; y>>=1;
}
return temp;
}
int C(int x,int y){return fac[x]*ifac[y]%mod*ifac[x-y]%mod;}
int STL(int x,int y)
{
int temp=0;
for(int i=0,bas=1;i<=y;i++,bas=-bas) temp=(temp+bas*C(y,i)*power(y-i,x)%mod+mod)%mod;
return temp*ifac[y]%mod;
}
#undef int
int main()
{
#define int long long
freopen("ichigo.in","r",stdin); freopen("ichigo.out","w",stdout);
n=read(); m=read(); fac[0]=ifac[0]=1;
for(int i=1;i<=n;i++) base=(base+read())%mod,fac[i]=fac[i-1]*i%mod;
ifac[n]=power(fac[n],mod-2); for(int i=n-1;i>=1;i--) ifac[i]=ifac[i+1]*(i+1)%mod;
printf("%lld",base*(STL(n,m)%mod+STL(n-1,m)%mod*(n-1)%mod)%mod);
return 0;
}

T2 尽梨了

解题思路

比较直接的一个 DP 就是 \(f_{i,j}\) 前 \(i\) 个商店买 \(j\) 个物品的最短时间。

发现在 \(a_i\) 不为 0 时,增长是指数级别的,于是每次转移只要 \(log\) 次就够了。

然后就是对于 \(a_i\) 为 0 的情况进行处理了,直接维护一个前缀和二分即可。

代码实现上有一些小细节。

code

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"RP++"<<endl
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int N=2e5+10;
int n,m,ans,pos,lim,f[N],pre[N];
struct Node{int a,b;}s[N];
bool comp(Node x,Node y){x.b++;y.b++;if(x.b*y.a!=y.b*x.a)return x.b*y.a<y.b*x.a;return x.b<y.b;}
#undef int
int main()
{
#define int long long
freopen("eriri.in","r",stdin); freopen("eriri.out","w",stdout);
n=read(); m=read(); lim=(int)log2(m)+1; memset(f,0x3f,sizeof(f));
for(int i=1;i<=n;i++) s[i].a=read(),s[i].b=read();
f[0]=0; sort(s+1,s+n+1,comp); pos=n+1;
for(int i=1;i<=n;i++) if(!s[i].a){pos=i;break;}
for(int i=1;i<=n;i++) s[i].b+=s[i].a+1,s[i].a++;
for(int i=pos;i<=n;i++) pre[i]=pre[i-1]+s[i].b;
for(int i=1;i<pos;i++)
for(int j=min(i,lim);j>=1;j--)
if(f[j-1]<=m) f[j]=min(f[j],f[j-1]*s[i].a+s[i].b);
for(int i=0;i<=min(n,lim);i++)
{
if(f[i]>m) continue;
int l=pos,r=n,temp=-1;
while(l<=r)
{
int mid=(l+r)>>1;
if(pre[mid]<=m-f[i]) temp=mid,l=mid+1;
else r=mid-1;
}
ans=max(ans,i+((~temp)?temp-pos+1:0));
}
printf("%lld",ans);
return 0;
}

T3 团不过

解题思路

很妙的一个题。

设 \(p(i)\) 表示 \(i\) 堆石子的方案数 \(p(i)=(2^n-1)^{\underline{i}}\) 。

设 \(f(n)\) 表示 \(n\) 堆石子先手必败的方案数,转移考虑在 \(i-1\) 堆石子后再添加一堆与 \(i-1\) 堆异或和相等的石子堆。

也就是 \(p(n-1)\) ,但是如果 \(i-1\) 堆石子异或和已经是 0 那么显然是不合法的,需要减去。

还有一种情况就是 \(i-2\) 堆石子的异或和为 0 新加入了两堆相同的石子,显然也是不可以的,也需要减去。

因此就有了:

\[f(i)=p(i-1)-f(i-1)-f(i-2)\times(i-1)\times(2^n-i+1)
\]

直接递推,然后拿总方案数减去就好了。

code

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"RP++"<<endl
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int N=1e7+10,mod=1e9+7;
int n,p[N],p2[N],f[N];
#undef int
int main()
{
#define int long long
freopen("yui.in","r",stdin); freopen("yui.out","w",stdout);
n=read(); p2[0]=1; for(int i=1;i<=n;i++) p2[i]=p2[i-1]*2%mod;
p[0]=1; for(int i=1;i<=n;i++) p[i]=p[i-1]*(p2[n]-i)%mod;
for(int i=3;i<=n;i++) f[i]=(p[i-1]-f[i-1]-(i-1)*f[i-2]%mod*(p2[n]-i+1)%mod+2*mod)%mod;
printf("%lld",(p[n]-f[n]+mod)%mod);
return 0;
}

T4 七负我

解题思路

最优的策略就是我们把所有的值都平均分配到一个完全图中,可以用 调整法 来证明。

然后直接枚举是 \(2^n\) 的无法接受,考虑 \(meet\;in\;the\;middle\)

于是枚举前 \(\frac{n}{2}\) 里的完全图,然后看他对于后 \(\frac{n}{2}\) 个点的连边。

再预处理出后 \(\frac{n}{2}\) 个点所有点集的所包含的完全图的个数。

然后枚举前 \(\frac{n}{2}\) 个点的点集,然后看他们对于后 \(\frac{n}{2}\) 连边的并集合并即可。

代码里好像有一个细节错了,但是错的那个点小于20,我直接。。。组合拳??

code

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"RP++"<<endl
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int N=50;
int n,m,m1,m2,all,maxn,e[N],f[1<<20];
#undef int
int main()
{
#define int long long
freopen("nanami.in","r",stdin); freopen("nanami.out","w",stdout);
n=read(); m=read(); all=read(); m1=n>>1; m2=n-m1; if(n<=20) m1=n;
for(int i=1,x,y;i<=m;i++) x=read(),y=read(),e[x]|=1ll<<y-1,e[y]|=1ll<<x-1;
for(int sta=1;sta<(1ll<<m1);sta++)
{
int sum=__builtin_popcount(sta); if(sum<=maxn) continue;
for(int i=1;i<=m1;i++) if((sta>>i-1)&1) if((e[i]&sta)!=(sta^(1ll<<i-1))) goto X;
maxn=max(maxn,sum);X:;
}
if(n<=20) printf("%.6lf",(1.0*(maxn*(maxn-1)/2))*(1.0*all)/(1.0*maxn)*(1.0*all)/(1.0*maxn)),exit(0);
for(int sta=1;sta<(1ll<<m2);sta++)
{
int sum=__builtin_popcount(sta);
for(int i=1;i<=m2;i++) if((sta>>i-1)&1) if(((e[i+m1]>>m1)&sta)!=(sta^(1ll<<i-1))) goto Y;
f[sta]=sta; maxn=max(maxn,sum); Y:;
}
for(int sta=0;sta<(1ll<<m2);sta++)
{
int U=(1ll<<m2)-1,sum=__builtin_popcount(sta);
for(int i=1;i<=m2;i++) if((sta>>i-1)&1) U&=e[i+m1]>>m1;
if(!U&&sum!=1) continue;
for(int i=1;i<=m2;i++)
if((((sta>>i-1)&1)^1))
if((((e[i+m1]>>m1)&sta)==sta&&(U&(1ll<<i-1)))){if(__builtin_popcount(f[sta|(1ll<<i-1)])<sum+1) f[sta|(1ll<<i-1)]=f[sta]|(1ll<<i-1);}
else if(__builtin_popcount(f[sta|(1ll<<i-1)])<sum) f[sta|(1ll<<i-1)]=f[sta];
}
for(int sta=1;sta<(1ll<<m1);sta++)
{
int U=(1<<m2)-1,sum=__builtin_popcount(sta);
for(int i=1;i<=m1;i++) if((sta>>i-1)&1) if((e[i]&sta)!=(sta^(1ll<<i-1))) goto Z;
for(int i=1;i<=m1;i++) if((sta>>i-1)&1) U&=e[i]>>m1;
maxn=max(maxn,sum+__builtin_popcount(f[U])); Z:;
}
printf("%.6lf",(1.0*(maxn*(maxn-1)/2))*(1.0*all)/(1.0*maxn)*(1.0*all)/(1.0*maxn));
return 0;
}

NOIP模拟85(多校18)的更多相关文章

  1. NOIP模拟83(多校16)

    前言 CSP之后第一次模拟赛,感觉考的一般. 不得不吐槽多校联测 OJ 上的评测机是真的慢... T1 树上的数 解题思路 感觉自己思维有些固化了,一看题目就感觉是线段树. 考完之后才想起来这玩意直接 ...

  2. Noip模拟80 2021.10.18

    预计得分:5 实际得分:140?????????????? T1 邻面合并 我考场上没切掉的大水题....(证明我旁边的cty切掉了,并觉得很水) 然而贪心拿了六十,离谱,成功做到上一篇博客说的有勇气 ...

  3. Noip模拟43 2021.8.18

    T1 地一体 可以树形$dp$,但考场没写出来,只打了没正确性的贪心水了$30$ 然后讲题的时候B哥讲了如何正确的贪心,喜出望外的学习了一下 不难发现 每次士兵都会直接冲到叶子节点 从深的点再返回到另 ...

  4. NOIP模拟92(多校25)

    前言 所以说这次是 HZOI 多校联测巅峰????(题目,数据过水??) T1 石子合并 解题思路 签到题. 发现我们可以给每个数字附一个正负号,每个数字的贡献就是它本身乘上这个符号. 发现至少应该有 ...

  5. NOIP模拟84(多校17)

    T1 宝藏 解题思路 考场上一眼出 \(nlog^2\) 做法,然后没看见是 1s 3e5 的数据,我竟然以为自己切了?? 考完之后尝试着把二分改为指针的移动,然后就过了??或许是数据水吧,感觉自己的 ...

  6. NOIP模拟86(多校19)

    T1 特殊字符串 解题思路 \(f_{i,j}\) 表示前 \(i\) 个字符中结尾为 \(j\) 的最大贡献. 转移枚举当前位置于之前位置结尾的组合加上贡献即可. 对于边界问题,容易发现选择 1 一 ...

  7. NOIP模拟88(多校21)

    前言 对于这套题的总体感觉就是难,然后就是自己很菜... 对于 T1 考试时只会一个最垃圾的背包,考完之后对于思路这一块也不是很顺利,大概这就是薄弱的地方吧. 然后 T2 是比较简单的一道题了,但是考 ...

  8. NOIP模拟96(多校29)

    T1 子集和 解题思路 大概是一个退背包的大白板,然而我考场上想复杂了,竟然还用到了组合数. 但是大概意思是一样的,有数的最小值一定是一个在 \(a\) 数组中存在的数字. 那么我们想办法除去它对应的 ...

  9. NOIP模拟99(多校31)

    T1 法阵 解题思路 原题3100,张口放 T1(出题人原话) 思维题,合法的情况其实就是上下两个梯形拼起来的样子. 他们的边界都是在 \(i\) 轴上面,但是不能相交. 于是我们可以尝试两者相交的纵 ...

随机推荐

  1. [ Skill ] Cadence Skill 语言入门

    https://www.cnblogs.com/yeungchie/ 写个大笔记,低速更新中 ... Cadence Skill Cadence 提供二次开发的 SKILL 语言,它是一种基于通用人工 ...

  2. HTML+CSS设计个人主页

    在个人主页的设计中,我采用了圣代布局和div分块.效果图如下: <!DOCTYPE html> <html lang="en"> <head> ...

  3. EditPlus配置C/C++运行环境

    1.安装MinGW和EditPlus 2.打开EditPlus ctrl+1 编译 ctrl+2 运行

  4. leetcode 盛水最多的容器 解析

    采用双指针法: 主要思想:定义头尾两个指针,分别向中间遍历,当相遇时结束循环.存储每一次遍历容器盛水的最大容量,并不断更新. 盛水的最大容量为 左右指针高度的最小值 乘以 左右指针的距离即宽度. 则可 ...

  5. Docker入门系列之一:什么是Docker?

    原文作者:Jeff Hale 原文地址:https://towardsdatascience.com/learn-enough-docker-to-be-useful-b7ba70caeb4b 翻译: ...

  6. mysql事务干货详解

    说明: mysql是现在行业中流行的关系型数据库,它的核心是存储引擎.mysql的存储引擎有很多种我们可以通过命令查看如下 SHOW ENGINES 不同版本得到的数据不一样,我们今天说的事务是在 M ...

  7. 学习PHP中统计扩展函数的使用

    做统计相关系统的朋友一定都会学习过什么正态分布.方差.标准差之类的概念,在 PHP 中,也有相应的扩展函数是专门为这些统计相关的功能所开发的.我们今天要学习的 stats 扩展函数库就是这类操作函数. ...

  8. 手把手教你调试SpringBoot启动 IoC容器初始化源码,spring如何解决循环依赖

    授人以鱼不如授人以渔,首先声明这篇文章并没有过多的总结和结论,主要内容是教大家如何一步一步自己手动debug调试源码,然后总结spring如何解决的循环依赖,最后,操作很简单,有手就行. 本次调试 是 ...

  9. Jmeter系列(23)- 常用逻辑控制器(2) | 事务控制器Transaction Controller

    事务控制器(Transaction Controller) 作用 选择一些请求,作为事务,放在该控制器下 比如:我有三个请求,注册.登录.下单.这三个请求其实就是一个下单完成过程,可以作为一个下单事务 ...

  10. Shell系列(33) - 多分支if语句简介及计算器例子

    多分支if条件语句 if [ 条件判断式1 ] then 当条件判断式1成立时,执行程序1 elif [ 条件判断式2 ] then 当条件判断式2成立时,执行程序2 ...省略更多条件... els ...