2019-11-11 CSP-S模拟测

T1

在这里插入图片描述
一句话题意:有一个数值为 1 n 1-n 且递增的序列,任取 m m 个数得到一个新序列,使每个新序列上的数的数值与其对应的位置数奇偶性相同,问有多少种取法。
思路:由奇偶性相同不难发现,假如将新序列中每个数所对应的位置和数值相加,得到的一定是一个偶数,且新序列中的得到的偶数必定两两互不相同。于是令序列 b i = a i + i b_i=a_i+i ,新序列中的 b b 与原序列中的 a a 一一对应。
则问题转化为:从 2 2 ~ n + m n+m 中选择 m m 个偶数,问有多少种方案。等价于:从 1 n + m 2 1\to\lfloor {\frac {n+m}{2}}\rfloor 中选择 m m 个数有多少种方案。
预处理出组合数及其逆元即可

#include <bits/stdc++.h> 
#define int long long 
using namespace std;
const int Mod=998244353;
const int N=1e6+5;
int n,m;
int fac[N],inv[N];
inline int read(){
	int cnt=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-')f=-f;c=getchar();}
	while(isdigit(c)){cnt=(cnt<<3)+(cnt<<1)+(c^48);c=getchar();}
	return cnt*f;
}
void write(int x){
	if(!x){puts("0");return;}
	char ch[20];int tot=0;
	for(;x;x/=10){ch[++tot]=x%10+'0';}
	for(int i=tot;i>=1;i--){putchar(ch[i]);}
	puts("");
}
int qpow(int a,int b){
	int ans=1;
	for(;b;b>>=1){if(b&1)ans=(ans*a)%Mod;a=(a*a)%Mod;}
	return ans%Mod;
}
void pre_w(int N){
	fac[0]=1;for(int i=1;i<=N;i++){fac[i]=fac[i-1]*i%Mod;}
	inv[N]=qpow(fac[N],Mod-2);for(int i=N-1;i>=0;i--){inv[i]=inv[i+1]*(i+1)%Mod;}
}
int C(int m,int n){return fac[m]*inv[m-n]%Mod*inv[n]%Mod;}
signed main(){
	int T;T=read();pre_w(1e6);
	while(T--){
		n=read(),m=read();
		write(C((n+m)/2,m)%Mod);
	}
	return 0;
}

T2

T2本来暴力30pts愣是因为语文问题爆零,不过赛后总结大概也是对线段树本质了解的还不清楚吧TAT,前路漫漫,还要继续努力吖
在这里插入图片描述
f r o m   s o l u t i o n from\ solution:
在这里插入图片描述
注意: [ l , r ] ( r l + 1 ) ( r l + 2 ) 2 [l,r]之间的总区间数是\frac{(r-l+1)*(r-l+2)}{2} ,有交集的地方交集相交处是不能取的
代码:为了避免计算过程中出现分数,先统一 2 *2 再最后 / 2 /2

#include<bits/stdc++.h>
#define mid ((l+r)>>1)
using namespace std;
inline long long read(){
	long long res=0,f=1;char ch=getchar();
	while(!isdigit(ch)) {if(ch=='-') f=-f;ch=getchar();}
	while(isdigit(ch)) {res=(res<<1)+(res<<3)+(ch^48);ch=getchar();}
	return res*f;
}
const int N=2e6+5;
int siz[N];
long long cnt[N],lmx[N],rmx[N],cur[N],sum;
inline void pushup(int k){
	siz[k]+=siz[k<<1]+siz[k<<1|1];
	cnt[k]+=cnt[k<<1]+cnt[k<<1|1];cur[k]+=cur[k<<1]+cur[k<<1|1];
	lmx[k]+=lmx[k<<1]+lmx[k<<1|1];rmx[k]+=rmx[k<<1]+rmx[k<<1|1];
}
void build(int k,int l,int r){//是对每个节点都要处理一次系数 
	siz[k]=1;lmx[k]=(2*l+1);rmx[k]=(2*r-1);cnt[k]-=(long long)l*(l+1);cnt[k]-=(long long)r*(r-1);//lmx:ql项的系数,rmx:qr项的系数,cnt:ql*qr项的系数
	if(l==r)	return;
	cnt[k]+=(long long)4*(l+1)*(r-1);cur[k]=4;lmx[k]-=4*(r-1);rmx[k]-=4*(l+1);
	build(k<<1,l,mid);build(k<<1|1,mid+1,r);
	pushup(k);
}
long long query(int k,int l,int r,int ql,int qr){
	if(ql<=l&&r<=qr){//查询区间完全包含线段树节点区间  
		long long tmp=cnt[k];
		tmp+=(sum-(long long)ql*ql-(long long)qr*qr)*siz[k];tmp+=lmx[k]*ql;tmp+=rmx[k]*qr;tmp+=cur[k]*ql*qr;//ql*ql,qr*qr的系数都是-1
		return tmp;
	}
	long long tmp=sum;
	//不完全包含的情况
	//注意:端点是不能取到的,减去的区间数是从(ql+1)~l,qr~(r+1)
	if(ql<=l)	tmp-=(long long)(l-ql)*(l-ql+1);
	if(r<=qr)	tmp-=(long long)(qr-r)*(qr-r+1);
	if(ql<=mid)	tmp+=query(k<<1,l,mid,ql,qr);
	if(qr>mid)	tmp+=query(k<<1|1,mid+1,r,ql,qr);
	return tmp;
}
int main(){
	int n=read(),q=read(),op=read();
	build(1,1,n);
	long long ans=0;
	while(q--){
		long long l=((ans*op)^read())%n+1,r=((ans*op)^read())%n+1;
		if(l>r) swap(l,r);
		sum=(long long)(r-l+1)*(r-l+2);//先计算出总区间数,后面直接用总个数减去对其没有贡献的个数
		ans=query(1,1,n,l,r)/2;
		printf("%lld\n",ans);
	}
	return 0;
}

T3

在这里插入图片描述
CF878E Numbers on the blackboard
计数类问题:主要在于讨论每一个数对于答案的贡献
o v e r f l o w overflow 时直接赋为 M o d   o r   I n f Mod\ or\ Inf 即可
在这里插入图片描述

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
inline int read(){
	int cnt=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-')f=-f;c=getchar();}
	while(isdigit(c)){cnt=(cnt<<3)+(cnt<<1)+(c^48);c=getchar();}
	return cnt*f;
}
const int N=5e5+5,Mo=1e9+7;
int n,q,a[N],fa[N],pre[N];
ll sum[N],suf[N],pw[N],S[N],an[N];
struct Que{int l,r,id;}ask[N];
bool cmp(Que a,Que b) {return a.r<b.r;}//询问离线按右端点进行排序  
int get(int x) {return fa[x]?fa[x]=get(fa[x]):x;}//并查集维护连通  
void merge(int x,int y) {//合并两个块  
	fa[x]=y;pre[y]=pre[x];
	int len=x-pre[x];//len当前块的长度 -> k值  sum当前块的值 
	if ((len>30&&sum[y]>0)||(sum[x]+(sum[y]<<len))>Mo) sum[y]=Mo;//直接赋为最大值  
	else sum[y]=sum[x]+(sum[y]<<len);
}
ll query(int l,int r) {return (suf[l]-suf[r+1]*pw[r-l+1]%Mo+Mo)%Mo;}//[l,块末尾]:1,2,4,8,16...  [r+1,块末尾]:1,2,4... *当前pw的值,相当于乘一个系数,使后面的都被减掉  
int main() {
	n=read();q=read();
	for(int i=1;i<=n;i++) a[i]=read();
	for(int i=1;i<=q;i++) ask[i].l=read(),ask[i].r=read(),ask[i].id=i;//处理每个询问,使其离线  
	sort(ask+1,ask+q+1,cmp);//询问区间按右端点排序  
	for(int i=1;i<=n;i++) pre[i]=i-1,fa[i]=0,sum[i]=a[i];//赋初值    
	for(int i=n;i>=1;i--) suf[i]=((suf[i+1]<<1)+a[i]+Mo)%Mo;//处理每个后缀  
	pw[0]=1;for(int i=1;i<=n;i++) pw[i]=(pw[i-1]<<1)%Mo;//预处理每个k值  
	int j=0;
	for(int i=1;i<=n;i++) {
		while (pre[i]&&sum[i]>=0) merge(pre[i],i);//发现让块连通更优,于是合并这两个块  
		S[i]=(S[pre[i]]+(query(pre[i]+1,i)<<1))%Mo;//S 前缀和  
		while (ask[j+1].r==i) {//重新开始记录  
			int x=get(ask[++j].l);
			an[ask[j].id]=(S[i]-S[x]+query(ask[j].l,x)+Mo)%Mo;
		} 
	}
	for(int i=1;i<=q;i++) printf("%lld\n",an[i]);
	return 0;
}
发布了37 篇原创文章 · 获赞 11 · 访问量 1924

猜你喜欢

转载自blog.csdn.net/weixin_42750325/article/details/103021697