191111CSP模拟DAY1

博主分享了CSP模拟考试的体验,指出在星际旅行问题中采用排序与二分查找解决,妖蝶问题通过杨辉三角和转化思路得出解法,而对于八云蓝问题,博主感性理解并详细解释了四种区间类型的统计方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

今天的考试还是不太理想,很多暴力分都没拿到,希望明天能改进。

T1:星际旅行

一道氵题,先排个序,二分查找即可。

代码:

#include<bits/stdc++.h>
#define ll long long
#define db double
#define re register
#define cs const
#define N 200005
using namespace std;
inline int read()
{
	int x=0,f=1;
	char ch;
	while(ch>'9'||ch<'0')
	{
		if(ch=='-')	f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=x*10+ch-'0';
		ch=getchar();
	}
	return f*x;
}
int n,s,l[N],x,ans,m;
int main()
{
	freopen("dwar.in","r",stdin);
	freopen("dwar.out","w",stdout);
	n=read();
	s=read();
	for(re int i=1;i<=n;++i)	l[i]=read();
	sort(l+1,l+1+n);
	l[n+1]=0x3f3f3f3f;
	for(re int i=1;i<n;++i)
	{
		x=s-l[i];
		if(x<=0)	break;
		m=upper_bound(l+1,l+n+1,x)-l-1;
		if(m<=i)	continue;
		ans+=m-i;
	}
	printf("%d",ans);
}

T2:栖息于禅寺的妖蝶

一道推结论的计数问题,这道题竟然用杨辉三角推组合数,还有 n ² n² n²暴力得 70 p t s 70pts 70pts的做法,而我一开始就找错了方向。

正解:由于题目要求在 n n n个数中选出 m m m个数,而且必须是有序的,我们可以做一个巧妙的转化,设 b i = a i + i b_i=a_i+i bi=ai+i,则序列中的每个数都转化成了偶数,则问题就转化成了从 1 1 1 n + m n+m n+m中选出 m m m个偶数,进一步就可转化成从 1 1 1 ⌊ n + m 2 ⌋ \left\lfloor\frac{n+m}{2}\right\rfloor 2n+m选出 m m m个数,则答案就是 ( ⌊ n + m 2 ⌋ m ) \left(\begin{array}{c}{\left\lfloor\frac{n+m}{2}\right\rfloor} \\ { m}\end{array}\right) (2n+mm)

代码:

#include<bits/stdc++.h>
#define int long long
#define db double
#define re register
#define cs const
#define mod 998244353
#define N 1000005 
using namespace std;
inline int read()
{
	int x=0,f=1;
	char ch;
	while(ch>'9'||ch<'0')
	{
		if(ch=='-')	f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=x*10+ch-'0';
		ch=getchar();
	}
	return f*x;
}
int inv[N],pre[N],t,n,m;
void fuck(int x)
{
	inv[0]=1;
	inv[1]=1;
	pre[0]=1;
	for(re int i=2;i<=N;++i)	inv[i]=(long long)inv[mod%i]*(mod-mod/i)%mod;
	for(re int i=1;i<=N;++i)	pre[i]=(long long)pre[i-1]*i%mod;
	for(re int i=1;i<=N;++i)	inv[i]=inv[i-1]*inv[i]%mod;
}
int work(int x,int y)
{
	return (long long)pre[x]*inv[y]%mod*inv[x-y]%mod;
}
signed main()
{
	t=read();
	fuck(1e6);
	while(t--)
	{
		n=read();
		m=read();
		printf("%lld\n",work((n+m)/2,m));
	}
}

T3:八云蓝

我只能感性理解,这里复制粘贴下题解。

设询问区间为[x,y],对于每个线段树区间[l,r],我们统计有多少个[x,y]的子区间会影响到[l,r],

把所有区间分成4类:

1.[l,r]包含[x,y]
2.[l,r]和[x,y]真相交

3.[x,y]包含[l,r]
4.[x,y]和[l,r]没有交集

第4类对答案没有贡献不考虑

第1类显然[x,y]的所有子区间都会影响[l,r]

第2类其实分成左相交和右相交两类,不过没有本质区别

贡献可以用总区间数-和[l,r]不相交的区间数

第3类稍微麻烦一点,贡献是总区间数-和[l,r]不相交的区间数-完

全包含[l,r]父亲的区间数

第1,2类区间总共只有O(log n)个,可以暴力计算

考虑第3类区间,在线段树上是𝑂(log 𝑛)个子树,考虑预处理

写出贡献式子可以发现对于某个区间[l,r],若[x,y]包含[l,r],对答案的贡献可以写成 A x y + B x + C y + D Axy+Bx+Cy+D Axy+Bx+Cy+D的形式

直接对于每个点预处理出所有的系数然后求子树和

代码:

#include<bits/stdc++.h>
#define db double
#define re register
#define cs const
#define int long long
#define N 2000005
#define mid (l+r)/2
using namespace std;
char nc(){
	static char buf[100000],*p1=buf,*p2=buf;
	return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int read()
{
	int x=0,f=1;
	char ch;
	while(ch>'9'||ch<'0')
	{
		if(ch=='-')	f=-1;
		ch=nc();
	}
	while(ch>='0'&&ch<='9')
	{
		x=x*10+ch-'0';
		ch=nc();
	}
	return f*x;
}
int sx[N],sy[N],sxy[N],b[N];
int n,q,opt,last,x,y;
void build(int k,int l,int r)
{
	int ll=l+1;
	int rr=r-1;
	sx[k]+=rr;
	sy[k]+=ll;
	sxy[k]--;
	b[k]-=ll*rr;
	if(l==r)	return;
	build(k<<1,l,mid);
	build(k<<1|1,mid+1,r);
	sx[k]+=sx[k<<1]+sx[k<<1|1];
	sy[k]+=sy[k<<1]+sy[k<<1|1];
	sxy[k]+=sxy[k<<1]+sxy[k<<1|1];
	b[k]+=b[k<<1]+b[k<<1|1];
	sx[k]-=r-l;
	sy[k]+=r-l;
	sx[k]-=2*rr;
	sy[k]-=2*ll;
	sxy[k]+=2;
	b[k]+=(r-l+1)*(r-l+2)/2-1;
	b[k]+=2*ll*rr;
	b[k]+=l*(r-l);
	b[k]-=r*(r-l);
}
int query(int k,int l,int r,int x,int y)
{
	if(x<=l&&r<=y)
	{
		int ans=0;
		ans+=x*sx[k]+y*sy[k]+sxy[k]*x*y+b[k];
		return ans;
	}
	int ans=0;
	if(l<=x&&y<=r)
	{
		ans=(y-x+1)*(y-x+2)/2;
		if(x<=mid)	ans+=query(k<<1,l,mid,x,y);
		if(y>mid)	ans+=query(k<<1|1,mid+1,r,x,y);
		return ans;
	}
	if(x<l)	ans+=(y-l+1)*(y-x+1+l-x+1)/2;
	if(y>r)	ans+=(r-x+1)*(y-x+1+y-r+1)/2;
	if(x<=mid)	ans+=query(k<<1,l,mid,x,y);
	if(y>mid)	ans+=query(k<<1|1,mid+1,r,x,y);
	return ans;
}
void print(int n){
	if(n>9) print(n/10);
	putchar(n%10+48);
}
signed main()
{
	n=read();
	q=read();
	opt=read();
	build(1,1,n);
	while(q--)
	{
		x=read();
		y=read();
		x=(x^(last*opt))%n+1;
		y=(y^(last*opt))%n+1;
		if(x>y)	swap(x,y);
		last=query(1,1,n,x,y);
		print(last);
		putchar('\n');
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值