zoukankan      html  css  js  c++  java
  • 省选模拟(41-45)

    省选模拟41#

    1.要换换名字##

    bfs自动机(误)+最大流
    用霍尔定理或者感性理解可以发现,一个人最多只需要n个名字就一定会有合法解.
    所以不需要(2^{100})记录所有的子序列,找到最小的n个就可以了.
    具体找法:维护(nxt[i][j][k])为第i个串j位置后第一个k字符的出现位置,可以倒推.
    然后bfs找到字典序最小的n个串.
    然后二分最大的串长,网络流判断是否满流就行.
    构建方案的话如果网络流用的是trie上的节点就很方便,只需要再维护一下这个点的字符代表以及父节点.

    2.动态半平面交##

    可持久化线段树+树上数颜色
    考场看到这个题目凉了一大半...
    维护深度棵的下标为dfs序的可持久化线段树.
    查询时只需要查询(dep[u]+d)深度的线段树上(u)(dfs)序区间.
    考虑怎么处理点权的lcm问题.
    发现点权的lcm实际就是对每个质因子的幂次取(max).
    换句话说,假如(p^1,p^2,p^3)的贡献都是(p),其实把(p^3)分解成这3种颜色是一样的.
    那么问题变成了求树上每种颜色的出现了.
    我们用一种树链的并做法.
    之前我们提到过.用set维护的dfs序可以求出所有路径*2的长度,等于把set上的点组成一个环每两点间的距离之和.
    这里再次得到一种做法.
    总的路径等于dfs序上每个点到自己与前一个点的lca的距离.

    把路径求并就可以做到不重不漏了.
    然后相当于差分,x处乘,lca处除掉.
    查询子树积就是自己的值.

    #include<cstdio>
    #include<queue>
    #include<vector>
    #include<map>
    #include<set>
    #define ll long long
    using namespace std;
    const int N=2e5+50;
    const int mod=998244353;
    inline ll rd(register ll x=0,register char ch=getchar(),register int f=0){
    	while(ch<'0'||ch>'9') f=ch=='-',ch=getchar();
    	while(ch>='0'&&ch<='9') x=(x<<1ll)+(x<<3ll)+ch-48,ch=getchar();
    	return f?-x:x;
    }
    ll mgml(ll a,ll b,ll ans=1){for(;b;b>>=1,a=a*a%mod)if(b&1)ans=ans*a%mod;return ans;}
    int K,n,tot,B,ptr,MAX;
    int a[N],head[N],to[N<<1],nxt[N<<1],dep[N];
    int dfn[N],r[N],idfn[N],Euler[N<<1],f[25][N<<1],Log[N<<1],fir[N];
    int mul[N*300],ls[N*300],rs[N*300],rt[N];
    map<int,int>ma;int V[N],W[N];
    vector<pair<int,int> >v[N];
    vector<int>D[N];
    set<int>se[N];
    int LCA(int x,int y){
    	if(!x||!y)return 0;
    	x=fir[idfn[x]],y=fir[idfn[y]];
    	if(x>y)swap(x,y);
    	int Logg=Log[y-x+1];
    	return dep[f[Logg][x]]<dep[f[Logg][y-(1<<Logg)+1]]?dfn[f[Logg][x]]:dfn[f[Logg][y-(1<<Logg)+1]];
    }
    void dfs(int x,int prt){
    	dfn[x]=++dfn[0];idfn[dfn[0]]=x;Euler[++Euler[0]]=x;fir[x]=Euler[0];
    	dep[x]=dep[prt]+1;D[dep[x]].push_back(x);
    	for(int i=head[x];i;i=nxt[i]) if(to[i]!=prt) dfs(to[i],x),Euler[++Euler[0]]=x;
    	r[x]=dfn[0];
    }
    void lnk(int x,int y){
    	to[++B]=y,nxt[B]=head[x],head[x]=B;
    	to[++B]=x,nxt[B]=head[y],head[y]=B;
    }
    int NEW(int OLD){
    	++ptr;mul[ptr]=mul[OLD],ls[ptr]=ls[OLD],rs[ptr]=rs[OLD];return ptr;
    }
    int query(int x,int l,int r,int L,int R){
    	if(!x) return mul[x];
    	if(L<=l&&r<=R) return mul[x];
    	int mid=(l+r)>>1,Mul=1;
    	if(L<=mid) Mul=1LL*Mul*query(ls[x],l,mid,L,R)%mod;
    	if(R>mid) Mul=1LL*Mul*query(rs[x],mid+1,r,L,R)%mod;
    	return Mul;
    }
    void add(int &x,int l,int r,int p,int v){
    	if(!p)return;x=NEW(x);if(l==r) return mul[x]=1LL*mul[x]*v%mod,void();
    	int mid=(l+r)>>1;p<=mid?add(ls[x],l,mid,p,v):add(rs[x],mid+1,r,p,v);
    	mul[x]=1LL*mul[ls[x]]*mul[rs[x]]%mod;
    }
    void update(int &x,int p,int v){
    	//printf("x:%d p:%d v:%d
    ",x,p,V[v]);
    	if(se[v].size()==0)return se[v].insert(p),add(x,1,n,p,V[v]),void();
    	set<int>::iterator nxt=se[v].upper_bound(p);
    	int Nxt=nxt==se[v].end()?0:*nxt,Pre=nxt==se[v].begin()?0:*--nxt;
    	return se[v].insert(p),add(x,1,n,LCA(Pre,Nxt),V[v]),add(x,1,n,p,V[v]),add(x,1,n,LCA(Pre,p),W[v]),add(x,1,n,LCA(p,Nxt),W[v]),void();
    }
    int main(){
    	//freopen("text.in","r",stdin);
    	K=rd(),n=rd();
    	for(int i=1;i<=n;++i) a[i]=rd(),MAX=max(MAX,a[i]);
    	for(int i=1;i<=n;++i){
    		int t=a[i];
    		for(int j=2;j*j<=t;++j)if(t%j==0){
    			int c=0;while(t%j==0)++c,t/=j;
    			if(!ma[j]){
    				ma[j]=++tot;V[tot]=j;W[tot]=mgml(j,mod-2);
    				for(ll k=j*j;k<=MAX;k=k*j)++tot,V[tot]=j,W[tot]=mgml(j,mod-2);
    			}
    			v[i].push_back(make_pair(ma[j],c));
    		}
    		if(t!=1){
    			if(!ma[t]){
    				ma[t]=++tot;V[tot]=t;W[tot]=mgml(t,mod-2);
    				for(ll k=t*t;k<=MAX;k=k*t)++tot,V[tot]=t,W[tot]=mgml(t,mod-2);
    			}
    			v[i].push_back(make_pair(ma[t],1));
    		}
    	}
    	for(int i=1;i<n;++i) lnk(rd(),rd());
    	
    	dfs(1,0);
    	Log[0]=-1;for(int i=1;i<=Euler[0];++i) Log[i]=Log[i>>1]+1,f[0][i]=Euler[i];
    	for(int i=1;i<=Log[Euler[0]];++i)for(int j=1;j+(1<<i)-1<=Euler[0];++j)f[i][j]=dep[f[i-1][j]]<dep[f[i-1][j+(1<<i-1)]]?f[i-1][j]:f[i-1][j+(1<<i-1)];
    	
    	mul[0]=1;
    	for(int i=1;i<=n;++i){
    		rt[i]=NEW(rt[i-1]);
    		for(int j=0;j<(int)D[i].size();++j)for(int k=0;k<(int)v[D[i][j]].size();++k) for(int d=0;d<v[D[i][j]][k].second;++d) update(rt[i],dfn[D[i][j]],v[D[i][j]][k].first+d);
    	}
    	int q=rd(),lastans=0;
    	while(q--){
    		int u=rd()^(K*lastans),v=rd()^(K*lastans);
    		printf("%d
    ",lastans=query(rt[min(n,dep[u]+v)],1,n,dfn[u],r[u]));
    	}
    	return 0;
    }
    

    3.获取名额##

    ?
    首先x和a都除掉(maxa)保证不用高精度了.
    计算无法获取省选名额的概率.

    [1-prod_{i=l}^r(1-frac{a_i}{x}) ]

    [1-exp(sum_{i=l}^r(ln(1-frac{a_i}{x}))) ]

    [ln(1-x)=sum_{i=1}^{+∞}-frac{x^i}{i!} ]

    所以需要处理(a)的幂次的前缀和.
    用泰勒展开的话20次左右就可以了.
    考虑如果说(frac{a}{x})很大的话泰勒展开就会不精准.
    所以准备一个阈值0.5.
    预处理区间a的最大值.
    如果说(frac{max a}{x}>0.5)直接((1-frac{a}{x}))计算,并继续递归左右部分.
    否则直接泰勒展开这个区间.
    因为每次分治((1-frac{a}{x})<0.5),所以每次精度翻倍,递归次数不会超过log次.

    省选模拟42#

    1.coin##

    期望dp
    最爱期望,最恨期望.
    首先可以发现期望的线性性.
    从而可以转为求出每种假币被拿走的期望人数.
    记 f(i,j) 表示恰好有 j 个人最喜欢的假币种类是 i 的概率,可以用 dp 计算。
    设 h(i,j,k) 表示前 k 个人恰好有 j 个喜爱的假币种类是 i 的概率
    那么 (h(i,j,k) = h(i,j−1,k−1)∗ pij/1000 + h(i,j −1,k)∗(1− pij/1000),f(i,j) = h(i,j,n)。)
    记 g(i,j) 表示第 i 种假币携带 j 枚,会被拿走的期望个数。
    (g(i,j) = ∑_{0≤k≤m}min(k,j)∗f(i,k) = [∑ k≤j k∗f(i,k)] + j ∗ ∑ j<k≤m f(i,k))
    假设确定了每枚假币的携带数量 wi,那么 E =∑g(i,wi),现在就是要让 E 最大。
    考虑记 dp[i][j] 表示决策前 i 种假币中共选取了 j 枚作礼物,所收获的最大期望。
    (dp[i][j] = max_k {dp[i−1][j −k] + g(i,k)}) 复杂度 (O(n^2m))

    分析一下 (∇g(i,j) = g(i,j)−g(i,j −1)) 的特性,(∇g(i,j) =∑_{j ≤ k ≤ m}f(i,j))
    你会发现∇ g(i,j) 首先是非负的,其次是单调不升的
    也就是说针对第 i 种假钞而言,拿 x + 1 枚一定比 x 收益大
    但是从 x + 1 枚变成 x + 2 枚增加的收益比从 x 枚增加到 x + 1 枚是要少的。
    摒弃第二部分的 dp,先假设所有假钞都选了 0 份,接着贪心得选择一种假钞,将其数量 +1 使得收益增幅最大,重复这个操作 n 次。
    计算 g 的复杂度也是 (O(n^2m)),注意到如果第 i 种假钞当前只选了 a 份,那么 (g(i,a + 2),g(i,a + 3)..) 是不用计算的
    g(i,x) 需要用到时从 g(i,x−1) 可以 O(n) 推得,即只有 O(n) 个 g(i,j) 需要计算.
    总时间复杂度 (O(nm + n^2))

    #include<bits/stdc++.h>
    using namespace std;
    int n,m,P[3333][333],al[3333];double p[3333][333],ans;
    priority_queue<pair<double,int> >q;map<int,double>M[303][3003];
    double f(int k,int c,int n){
    	while(!P[n][k]&&n)n--;
    	if(c==0||!n)return 0;
    	if(M[k][c].find(n)!=M[k][c].end())return M[k][c][n];
    	return M[k][c][n]=(1+f(k,c-1,n-1))*p[n][k]+f(k,c,n-1)*(1-p[n][k]);
    }
    int main(){
    	cin>>n>>m;
    	for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)scanf("%d",&P[i][j]),p[i][j]=P[i][j]/1000.;
    	for(int i=1;i<=m;++i)q.push(make_pair(f(i,1,n),i));
    	for(int i=1;i<=n;++i){
    		ans+=q.top().first;int k=q.top().second;q.pop();
    		al[k]++;q.push(make_pair(f(k,al[k]+1,n)-f(k,al[k],n),k));
    	}printf("%.10lf
    ",ans);
    }
    

    2.B##

    矩阵树定理
    发现进行不超过k次操作等价于是完全图的生成树中非原树边(<=k)个.
    变元矩阵树定理,高消或者拉格朗日插值插出i条新边的生成树个数就行.

    3.battery##

    (2-sat)
    1.每种炮台只有横向/竖向两种形态.
    2.每个空地最多被横着竖着各一个炮台打中.
    所以是2-sat.
    两个炮台的关系是(a|b).
    p.s.dfs的时候记得从下边来的方向是向上的!

    省选模拟43#

    1.选拔赛##

    dp
    (T(L,R))表示前k个(>=L)且后n-k个(<=R)的方案数.
    那么我们离散化所有和后答案就是所有的(T(x,x)-T(x+1,x)).
    考虑怎么求出(T(L,R))来.
    字太多了直接粘了

    2.跳跃##

    倍增
    首先处理出来(L[i][j] R[i][j])表示j位置走(2^i)步的最向左右的位置.
    这是相互可以转移的.
    然后是比较奇妙的操作了.
    二分答案mid.用(mid-1 check).
    枚举左端点x,从x跳mid步的位置都要染色.
    但是每次取极值并不一定是最优解.
    所以要每次让一个区间去跳.
    所以要维护倍增的倍增数组.
    然后跳到了最远的地方后.
    考虑如果最优答案(>=mid),那么就一定有(y>max place)且y也不能到达x.
    所以处理后面每个点向左跳的最小下标,然后后缀取(min),每个左端点判断自己的右边界+1的后缀min是否小于自己,若是就有一组合法解.

    3.切蛋糕##

    半平面交
    圆不好整.
    用多边形拟合圆.
    直接半平面交.
    交出的多边形拟合的边就是弧长,其他的就是弦长.

    模拟测试44#

    1.跑步##

    BIT+单调指针
    (O(n^2))的dp直接搞就完事了.
    考虑每次修改的区域是(x,y)->(n,n).
    不妨再缩小一点就是一个(x,y)开始的连续的位置.
    再缩小一点发现每行的区间l,r单调不降.
    所以用单调指针维护这个东西.
    每行区间修改,单点查询.
    BIT维护.

    2.算术##

    ?
    什么剩余都不是.
    (m^k=n)那么在取模意义下仍相等.
    那么构造几个微妙的质数满足(p=ak+1)
    (x^{ak}equiv 1)
    如果说(m^k=n)(m^{ak}equiv 1).
    那么(n^aequiv 1)才行.
    前提是(n\% p!=0).
    然后这个东西异常的精准,模拟个20次左右就行了.

    3.求和##

    只会一种90分的暴力.
    线段树,维护堆.
    这个区间的堆里的元素表示的是([l,r])作为左端点同时满足的右端点.
    然后就可以区间的(max)+堆的(top).
    然后上传答案到根更新.

    模拟测试45#

    1.matrix##

    trie合并 set合并
    一种很新奇的思路.
    把每一行当作一个字符串看待.
    先讨论(l=1)的所有子矩阵本质不同的行个数.
    换种方法,考虑每一行被统计进本质不同的子矩阵个数.
    就是((i-pre)*(n-nxt)).
    然后考虑怎么做使得l+1.
    就是把trie上根的儿子合并成新根,然后set也启发式合并,map也启发式合并.
    考虑怎么更新答案.
    发现答案可以从增量角度来计算.
    这里一种复杂度不对的打法就是合并x,y时(delta=ret[x]+ret[y]-ret[x']).
    这样不对的原因是这样需要把合并后的x'的set统计一遍答案,每个元素照旧会被算n次.
    正确的姿势应该是减去ret[y]但是把y中元素加入x的贡献加入.
    (O(nlog^2))

    #include<cstdio>
    #include<set>
    #include<map>
    #define ll long long
    using namespace std;
    const int NM=5e5+50;
    inline int rd(register int x=0,register char ch=getchar(),register int f=0){
    	while(ch<'0'||ch>'9') f=ch=='-',ch=getchar();
    	while(ch>='0'&&ch<='9') x=(x<<1)+(x<<3)+ch-48,ch=getchar();
    	return f?-x:x;
    }
    int n,m,ptr;
    set<int>s[NM];
    map<int,int>ch[NM];
    ll ans[NM],now,ret[NM];
    void bl(int rt){
    	int lst=0;
    	for(set<int>::iterator it=s[rt].begin();it!=s[rt].end();++it) ret[rt]+=1LL*(*it-lst)*(n-*it+1),lst=*it;
    	for(map<int,int>::iterator it=ch[rt].begin();it!=ch[rt].end();++it)bl(it->second);
    }
    set<int>::iterator Pre(set<int>::iterator it){return --it;}
    int merge(int x,int y){
    	if(!x||!y) return x+y;
    	if(ch[x].size()<ch[y].size())swap(x,y);
    	for(map<int,int>::iterator it=ch[y].begin();it!=ch[y].end();++it) 
    		ch[x][it->first]=merge(ch[x][it->first],it->second);
    	
    	if(s[x].size()<s[y].size())swap(s[x],s[y]),swap(ret[x],ret[y]);
    	ans[now]-=ret[y];
    	for(set<int>::iterator it=s[y].begin();it!=s[y].end();++it) if(s[x].find(*it)==s[x].end()){
    		set<int>::iterator t=s[x].lower_bound(*it);
    		int l=*it,r=n-*it+1;
    		if(t!=s[x].begin())l=*it-*Pre(t);
    		if(t!=s[x].end())r=*t-*it;
    		s[x].insert(*it);
    		
    		ret[x]+=1LL*l*r;
    		ans[now]+=1LL*l*r;
    	}
    	
    	return x;
    }
    int main(){
    	n=rd();m=rd();ptr=1;
    	for(int i=1,rt;rt=1,i<=n;++i)for(int j=1,c;j<=m;++j){
    		c=rd();
    		if(!ch[rt][c]) ch[rt][c]=++ptr;
    		rt=ch[rt][c]; s[rt].insert(i);
    	}
    	bl(1);for(int i=1;i<=ptr;++i) ans[0]+=ret[i];
    	for(int o=1,rt=1;o<m;++o){
    		int c=0; now=o;
    		for(map<int,int>::iterator it=ch[rt].begin();it!=ch[rt].end();++it) c=merge(c,it->second);
    		rt=c;
    		ans[o]-=ret[rt];
    		ans[o]+=ans[o-1];
    	}
    	ll sum=0;for(int i=0;i<m;++i) sum+=ans[i];
    	printf("%lld
    ",sum);
    	return 0;
    }
    

    2.sequence##

    线段树
    离线扫描线.
    维护每个点作为右端点时候的线段树.
    左端点位置的线段树值就是答案.
    发现一个后缀和的&值最多改变30次.
    所以直接新添入一个元素该合并的合并.
    &值模k得零的区间+1就好了.
    区间修改,单点查询.
    可以标记永久化.

    #include<cstdio>
    #include<algorithm>
    #define lch k<<1
    #define rch k<<1|1
    #define ll long long
    using namespace std;
    const int N=1e5+50;
    inline int rd(register int x=0,register char ch=getchar(),register int f=0){
    	while(ch<'0'||ch>'9') f=ch=='-',ch=getchar();
    	while(ch>='0'&&ch<='9') x=(x<<1)+(x<<3)+ch-48,ch=getchar();
    	return f?-x:x;
    }
    int n,Q,K;
    int a[N],nxt[N],tag[N<<2];
    ll sum[N<<2],ans[N*5];
    struct node{
    	int l,r,id;
    	friend bool operator < (node a,node b){return a.r<b.r;}
    }q[N*5];
    void down(int k,int l,int r){
    	if(!tag[k])return;int mid=(l+r)>>1;
    	sum[lch]+=1LL*tag[k]*(mid-l+1);tag[lch]+=tag[k];
    	sum[rch]+=1LL*tag[k]*(r-mid);tag[rch]+=tag[k];
    	tag[k]=0;
    }
    void add(int k,int l,int r,int L,int R){
    	if(L<=l&&r<=R)return sum[k]+=r-l+1,tag[k]++,void();
    	int mid=(l+r)>>1;down(k,l,r);
    	if(L<=mid)add(lch,l,mid,L,R);
    	if(R>mid)add(rch,mid+1,r,L,R);
    	sum[k]=sum[lch]+sum[rch];
    }
    ll ask(int k,int l,int r,int L,int R){
    	if(L<=l&&r<=R)return sum[k];
    	int mid=(l+r)>>1;down(k,l,r);
    	if(R<=mid)return ask(lch,l,mid,L,R);
    	if(L>mid)return ask(rch,mid+1,r,L,R);
    	return ask(lch,l,mid,L,R)+ask(rch,mid+1,r,L,R);
    }
    int main(){
    	n=rd();Q=rd();K=rd();
    	for(int i=1;i<=n;++i) a[i]=rd();
    	for(int i=1;i<=Q;++i) q[i].l=rd(),q[i].r=rd(),q[i].id=i;
    	sort(q+1,q+Q+1);
    	for(int i=1,j=1;i<=n&&j<=Q;++i){
    		int val=a[i]; nxt[i]=i-1;
    		for(int x=i;x;x=nxt[x]){
    			int y=nxt[x];
    			while(y)if((val&a[y])==val)y=nxt[y];else break;
    			nxt[x]=y;
    			if(val%K==0) add(1,1,n,nxt[x]+1,x);
    			val&=a[nxt[x]];
    		}
    		while(q[j].r==i) ans[q[j].id]=ask(1,1,n,q[j].l,q[j].r),++j;
    	}
    	for(int i=1;i<=Q;++i) printf("%lld
    ",ans[i]);
    	return 0;
    }
    

    3.permutation##

    第一类斯特林数+NTT
    (ans(n,a,b)=sum_{i=1}^{n}f(i-1,a-1)*f(n-i,b-1)*C(n-1,i-1))
    含义是枚举最大值的位置求得方案
    f的求法:(f[i][j]=f[i-1][j]*(i-1)+f[i-1][j-1])
    含义是考虑当前元素的排名.
    发现(f[i][j]=s1(i,j))
    再次考虑ans,等价于在n-1个元素组成a+b-2个环,然后分a-1,b-1个给左右.
    那么等价于(s1(n-1,a+b-2)*C(a+b-2,a-1))
    需要(O(nlog))求出第一类斯特林数.
    二项式推一推就好了.

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    #include<cmath>
    #define ll long long
    using namespace std;
    const int mod=998244353;
    const int N=1e6+50; 
    inline int rd(register int x=0,register char ch=getchar(),register int f=0){
    	while(ch<'0'||ch>'9') f=ch=='-',ch=getchar();
    	while(ch>='0'&&ch<='9') x=(x<<1)+(x<<3)+ch-48,ch=getchar();
    	return f?-x:x;
    }
    int n,A,B;
    int BIT[N],rev[N],a[N],b[N],c[N],fac[N],inv[N],finv[N];
    ll mgml(ll a,ll b,ll ans=1){for(;b;b>>=1,a=a*a%mod)if(b&1)ans=ans*a%mod;return ans;}
    ll C(ll x,ll y){return x<y||y<0?0:1LL*fac[x]*finv[y]%mod*finv[x-y]%mod;}
    int mo(int a){return a>=mod?a-mod:a;}
    void NTT(int *a,int len,int opt){
    	for(int i=0;i<len;++i){
    		rev[i]=(rev[i>>1]>>1)|((i&1)<<BIT[len]-1);
    		if(i<rev[i]) swap(a[i],a[rev[i]]);
    	}
    	for(int i=1;i<len;i<<=1)for(int j=0,wn=mgml(3,(mod-1)/(i<<1));j<len;j+=i<<1)for(int k=0,w=1,x,y;k<i;++k,w=1LL*w*wn%mod)x=a[j+k],y=1LL*w*a[j+k+i]%mod,a[j+k]=mo(x+y),a[j+k+i]=mo(x-y+mod);
    	if(opt==1)return; reverse(a+1,a+len);
    	for(int i=0,inv=mgml(len,mod-2);i<len;++i)a[i]=1LL*a[i]*inv%mod;
    }
    void solve(int l,int r){
    	if(l==r) return a[0]=l,a[1]=1,void();
    	int mid=(l+r)>>1;if((r-l+1)&1)mid--;solve(l,mid);
    	int len=1<<((int)(log2(mid-l+2)+1));
    	for(int i=0,pw=1;i<len;++i,pw=1LL*pw*(mid+1)%mod)b[i]=1LL*fac[i]*a[i]%mod,c[i]=1LL*pw*finv[i]%mod;
    	for(int i=len;i<len<<1;++i) b[i]=c[i]=0;
    	reverse(b,b+len);NTT(b,len<<1,1);NTT(c,len<<1,1);for(int i=0;i<len<<1;++i)b[i]=1LL*b[i]*c[i]%mod;NTT(b,len<<1,-1);reverse(b,b+len);
    	for(int i=0;i<len;++i)b[i]=1LL*b[i]*finv[i]%mod;
    	if((r-l+1)&1) for(int i=len-1;~i;--i) b[i]=mo(1LL*b[i]*r%mod+(i==0?0:b[i-1]));
    	for(int i=len;i<len<<1;++i) b[i]=a[i]=0;
    	len<<=1;
    	NTT(a,len,1);NTT(b,len,1);for(int i=0;i<len;++i)a[i]=1LL*a[i]*b[i]%mod;NTT(a,len,-1);
    }
    int main(){
    	fac[0]=fac[1]=inv[0]=inv[1]=finv[0]=finv[1]=1;
    	for(int i=2;i<N;++i) fac[i]=1LL*fac[i-1]*i%mod,inv[i]=1LL*inv[mod%i]*(mod-mod/i)%mod,finv[i]=1LL*finv[i-1]*inv[i]%mod;
    	for(int i=1,j=0;i<N;++j,i<<=1) BIT[i]=j;
    	n=rd();A=rd(),B=rd();
    	if(A==1&&B==1)return !printf("%d
    ",n==1?1:0);
    	solve(0,n-2);
    	printf("%lld
    ",1LL*C(A+B-2,A-1)*a[A+B-2]%mod);
    	return 0;
    }
    
  • 相关阅读:
    MySQL--单表查询
    python库--pandas--Series.str--字符串处理
    如何 grep tab & 如何grep 减号(dash)
    png压缩
    如何无密码登陆远程机器?
    ssh中运行awk
    PHP 时区
    sublime使用
    nginx 50x故障分析
    nginx反向代理异常
  • 原文地址:https://www.cnblogs.com/hzoi2018-xuefeng/p/12506825.html
Copyright © 2011-2022 走看看