题目大意
有一个无限长的二进制串,初始时它的每一位都为 \(0\)。现在有 \(m\) 个操作,其中第 \(i\) 个操作是将这个二进制串的数值加上 \(2^{a_i}\)。我们称每次操作的代价是这次操作改变的位的数量。
我们以一定概率执行这些操作:第 \(i\) 个操作有 \(p_i\) 的概率执行,否则不执行。
请求出所有执行的操作的代价和的期望。
\(n\leq 100000,m\leq 200000,0\leq a_i\leq n\)
题解
容易发现,如果进行了 \(k\) 次操作且把这个数从 \(0\) 修改成了 \(v\),那么代价就是 \(2k-\operatorname{bitcount}(v)\)。
可以用势能分析相关知识解释随便看看就看出来了。
前半部分就是 \(2\sum_{i=1}^mp_i\)。
后半部分可以每位分开算:计算 \(v\) 的每一位为 \(1\) 的概率。
设 \(f_{i,j}\) 为只考虑 \(a_k\leq i\) 的那些操作,修改完后 \(\lfloor\frac{v}{2^i}\rfloor=j\) 的概率。
\(g_{i,j}\) 为只考虑 \(a_k=i\) 的那些操作,修改完后 \(\lfloor\frac{v}{2^i}\rfloor=j\) 的概率,也就是执行了 \(j\) 个操作的概率。
\(g_{i,j}\) 可以用分治 NTT 求出。
\(f_{i,j}=\sum_{\lfloor\frac{k}{2}\rfloor+l=j}f_{i-1,k}g_{i,l}\),可以用 NTT 优化。
那么答案的第二部分就是 \(\sum_{i\geq 0}\sum_{2\nmid j}f_{i,j}\)
时间复杂度是多少?
记 \(c_i=\sum_{j=1}^m[a_j=i]\)
算 \(g\) 的复杂度显然是 \(O(m\log^2 m)\)
算 \(f\) 的复杂度是
&O(\log m\times \sum_{i=0}^n\sum_{j=0}^{i}\frac{c_j}{2^{i-j}})\\
=&O(\log m\times \sum_{i=0}^nc_i\sum_{j\geq 0}2^{-j})\\
=&O(m\log m)
\end{align}
\]
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<ctime>
#include<utility>
#include<functional>
#include<cmath>
#include<vector>
//using namespace std;
using std::min;
using std::max;
using std::swap;
using std::sort;
using std::reverse;
using std::random_shuffle;
using std::lower_bound;
using std::upper_bound;
using std::unique;
typedef long long ll;
typedef unsigned long long ull;
typedef std::pair<int,int> pii;
typedef std::pair<ll,ll> pll;
void open(const char *s){
#ifndef ONLINE_JUDGE
char str[100];sprintf(str,"%s.in",s);freopen(str,"r",stdin);sprintf(str,"%s.out",s);freopen(str,"w",stdout);
#endif
}
int rd(){int s=0,c,b=0;while(((c=getchar())<'0'||c>'9')&&c!='-');if(c=='-'){c=getchar();b=1;}do{s=s*10+c-'0';}while((c=getchar())>='0'&&c<='9');return b?-s:s;}
void put(int x){if(!x){putchar('0');return;}static int c[20];int t=0;while(x){c[++t]=x%10;x/=10;}while(t)putchar(c[t--]+'0');}
int upmin(int &a,int b){if(b<a){a=b;return 1;}return 0;}
int upmax(int &a,int b){if(b>a){a=b;return 1;}return 0;}
const ll p=998244353;
const int N=600000;
ll fp(ll a,ll b)
{
ll s=1;
for(;b;b>>=1,a=a*a%p)
if(b&1)
s=s*a%p;
return s;
}
ll v[N],x[N],y[N];
int a[N];
int n,m;
ll e[N];
ll *f[N*2];
ll *g[N];
namespace fft
{
const int W=524288;
ll w[N];
int rev[N];
void init()
{
ll s=fp(3,(p-1)/W);
w[0]=1;
for(int i=1;i<W;i++)
w[i]=w[i-1]*s%p;
}
void ntt(ll *a,int n,int t)
{
for(int i=1;i<n;i++)
{
rev[i]=(rev[i>>1]>>1)|(i&1?n>>1:0);
if(rev[i]>i)
swap(a[i],a[rev[i]]);
}
for(int i=2;i<=n;i<<=1)
for(int j=0;j<n;j+=i)
for(int k=0;k<i/2;k++)
{
ll u=a[j+k];
ll v=a[j+k+i/2]*w[W/i*k]%p;
a[j+k]=(u+v)%p;
a[j+k+i/2]=(u-v)%p;
}
if(t==-1)
{
reverse(a+1,a+n);
ll inv=fp(n,p-2);
for(int i=0;i<n;i++)
a[i]=a[i]*inv%p;
}
}
void mul(ll *a,ll *b,ll *c,int n,int m,int l)
{
static ll a1[N],a2[N];
int k=1;
while(k<=n+m)
k<<=1;
for(int i=0;i<=n;i++)
a1[i]=a[i];
for(int i=n+1;i<k;i++)
a1[i]=0;
for(int i=0;i<=m;i++)
a2[i]=b[i];
for(int i=m+1;i<k;i++)
a2[i]=0;
ntt(a1,k,1);
ntt(a2,k,1);
for(int i=0;i<k;i++)
a1[i]=a1[i]*a2[i]%p;
ntt(a1,k,-1);
for(int i=0;i<=l;i++)
c[i]=a1[i];
}
int cnt;
int len[N*2];
void solve(int &now,int l,int r)
{
now=++cnt;
if(l==r)
{
len[now]=1;
f[now]=new ll[len[now]+1];
f[now][0]=1-e[l];
f[now][1]=e[l];
return;
}
int ls;
int rs;
int mid=(l+r)>>1;
solve(ls,l,mid);
solve(rs,mid+1,r);
len[now]=len[ls]+len[rs];
f[now]=new ll[len[now]+1];
mul(f[ls],f[rs],f[now],len[ls],len[rs],len[now]);
}
}
int rt[N];
int len[N];
int len2[N];
ll h[N];
std::vector<ll> c[N];
int main()
{
fft::init();
open("loj565");
scanf("%d%d",&n,&m);
n++;
ll ans=0;
ll s=0;
for(int i=1;i<=m;i++)
{
scanf("%d%lld%lld",&a[i],&x[i],&y[i]);
a[i]++;
v[i]=x[i]*fp(y[i],p-2)%p;
ans+=2*v[i];
c[a[i]].push_back(v[i]);
}
g[0]=new ll[1];
g[0][0]=1;
for(int i=1;i<=n+20;i++)
{
for(auto v:c[i])
e[++len[i]]=v;
if(len[i])
fft::solve(rt[i],1,len[i]);
else
{
rt[i]=++fft::cnt;
f[rt[i]]=new ll[1];
f[rt[i]][0]=1;
}
for(int j=0;j<=len2[i-1]>>1;j++)
h[j]=0;
for(int j=0;j<=len2[i-1];j++)
h[j>>1]=(h[j>>1]+g[i-1][j])%p;
len2[i-1]>>=1;
len2[i]=len2[i-1]+len[i];
g[i]=new ll[len2[i]+1];
fft::mul(h,f[rt[i]],g[i],len2[i-1],len[i],len2[i]);
for(int j=1;j<=len2[i];j+=2)
s=(s+g[i][j])%p;
}
ans=(ans-s)%p;
ans=(ans+p)%p;
printf("%lld\n",ans);
return 0;
}
【LOJ565】【LibreOJ Round #10】mathematican 的二进制 DP 分治FFT的更多相关文章
-
LOJ#565. 「LibreOJ Round #10」mathematican 的二进制 分治,FFT,概率期望
原文链接www.cnblogs.com/zhouzhendong/p/LOJ565.html 前言 标算真是优美可惜这题直接暴力FFT算一算就solved了. 题解 首先,假装没有进位,考虑解决这个问 ...
-
【BZOJ5119】【CTT2017】生成树计数 DP 分治FFT 斯特林数
CTT=清华集训 题目大意 有\(n\)个点,点权为\(a_i\),你要连接一条边,使该图变成一颗树. 对于一种连边方案\(T\),设第\(i\)个点的度数为\(d_i\),那么这棵树的价值为: \[ ...
-
LOJ565. 「LibreOJ Round #10」mathematican 的二进制(NTT)
题目链接 https://loj.ac/problem/565 题解 首先,若进行所有操作之后成功执行的操作数为 \(m\),最终得到的数为 \(w\),那么发生改变的二进制位的数量之和(即代价之和) ...
-
#565. 「LibreOJ Round #10」mathematican 的二进制(期望 + 分治NTT)
题面 戳这里,题意简单易懂. 题解 首先我们发现,操作是可以不考虑顺序的,因为每次操作会加一个 \(1\) ,每次进位会减少一个 \(1\) ,我们就可以考虑最后 \(1\) 的个数(也就是最后的和) ...
-
Codeforces Beta Round #10 D. LCIS(DP&;amp;LCIS)
D. LCIS time limit per test 1 second memory limit per test 256 megabytes input standard input output ...
-
[LOJ#516]「LibreOJ β Round #2」DP 一般看规律
[LOJ#516]「LibreOJ β Round #2」DP 一般看规律 试题描述 给定一个长度为 \(n\) 的序列 \(a\),一共有 \(m\) 个操作. 每次操作的内容为:给定 \(x,y\ ...
-
LibreOJ #516. 「LibreOJ β Round #2」DP 一般看规律
二次联通门 : LibreOJ #516. 「LibreOJ β Round #2」DP 一般看规律 /* LibreOJ #516. 「LibreOJ β Round #2」DP 一般看规律 set ...
-
[LOJ#522]「LibreOJ β Round #3」绯色 IOI(危机)
[LOJ#522]「LibreOJ β Round #3」绯色 IOI(危机) 试题描述 IOI 的比赛开始了.Jsp 和 Rlc 坐在一个角落,这时他们听到了一个异样的声音 …… 接着他们发现自己收 ...
-
【BZOJ1662】[Usaco2006 Nov]Round Numbers 圆环数 数位DP
[BZOJ1662][Usaco2006 Nov]Round Numbers 圆环数 Description 正如你所知,奶牛们没有手指以至于不能玩"石头剪刀布"来任意地决定例如谁 ...
随机推荐
-
自定义延时查询控件---valen
当查询已经成标配 查询是已成为每个应用常用的功能,也正是这样前端后对查询的设计需求也日益增加,本文针对前端(Android端)查询控件做一个例子: 控件设计与逻辑 产品的设计UI图; 要达到如下 1| ...
-
MySQL的varchar定义长度到底是字节还是字符
相信这个问题也会困扰不少人,尤其是使用过其它数据库(如Oracle)的人,之前我也没有太在意这个问题,再加上一些书籍和网上的文章讲的不够细致,又没测试过,导致我一直理解错误.下面通过实例来解释,在开始 ...
-
SQL随着子查询结果更新多个字段
笔者:iamlasong 要求:表格内容需要改变,在临时表中内容的变化,使用SQL官方声明更新表若干领域. 假设更新一个字段,直接用字段名=子查询就能够了,多个字段更新,将字段在括号里并列写出就可以, ...
-
C# 垃圾回收机制(转)
摘要:今天我们漫谈C#中的垃圾回收机制,本文将从垃圾回收机制的原理讲起,希望对大家有所帮助. GC的前世与今生 虽然本文是以.NET作为目标来讲述GC,但是GC的概念并非才诞生不久.早在1958年,由 ...
-
《算法导论》习题2.3-6 改进的InsertSort
InsertSort中有关键的一步是把当前元素A[i]插入到已经排好序的A[1,i-1]的合适的位置上,在原始的InsertSort算法中, 采用的是从后往前一步一步查找的方法,习题2.3-6要求利用 ...
-
IT连创业系列:App产品上线后,运营怎么搞?(中)
等运营篇写完,计划是想写一个IOS系列,把IT连App里用到和遇到的坑都完整的和大伙分享. 不过写IOS系列前,还是要认真把这个运营篇写完,接下来好好码字!!! 上篇说到,我们计划去一次富士康门口,拉 ...
-
PowerDesigner工具将表字段转成java实体
首先将数据库的表导出为SQL文件.下载安装PowerDesigner工具. 下面以图文的形式讲解: 图 (1) 图 (2) 图 (3) 图 (4) 图 (5) 图 (6) 图 ...
-
Android为TV端助力 SharedPreferences 轻量级存储!
首先在当前进程也就是当前的项目里面进行存储 SharedPreferences.Editor editor = mContext.getSharedPreferences("tvplay&q ...
-
在PHP中使用AES加密算法加密数据及解密数据
这个算法可以将数据加密后,储存起来,到需要用的时候,用之前加密的秘钥将之还原. 除了这个之外,还有AES这个算法能够将数据很好的加密起来,在传输过程中不容易被破解. 在PHP中,我们必须先安装好mcr ...
-
.net core WebApi Mutex实现并发同步
Mutex,中文译为互斥体,在.net中也是作为一种线程或进程之间的互斥体存在.即在同一时刻,一个共享资源只允许被某一个线程或进程访问,其他线程或进程需要等待(直至获取互斥锁为止). Mutex的使用 ...