T1

一道。。。很水的题。。感觉是个人都会做吧QAQ 随便upper_bound 一下就好了

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxn=300000;
const int inf=1e9;
int read(){
	int x=0,f=1;char ch=getchar();
	while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
int n,a[maxn+10],limit;
long long ans=0;
int main(){
	freopen("dwar.in","r",stdin);
	freopen("dwar.out","w",stdout);
	n=read();limit=read();
	for(int i=1;i<=n;i++) a[i]=read();
	sort(a+1,a+1+n);a[++n]=inf;
	for(int i=1,pos;i<=n-1;i++){
		if(limit>=a[i]*2){
			pos=upper_bound(a+i,a+1+n,limit-a[i])-a;
			if(pos>i){
				ans=ans+(long long)pos-(long long)i-1;
			}
		}
	}
	printf("%lld",ans);
}

T2

这道题不太适合我这种推结论很弱的人。其实就算我会推结论,我也算不来组合数QAQ

首先讲一下\(70pts\)的算法 这个数据显然可以\(o(n^2)\)过 因此考虑二维\(dp\)\(dp_{i,j}\)表示当前到了第\(i\)位,并且包括当前位(当前位必选)总共已经选了\(j\)个数了。 显然只有当\(i≡j(mod2)\)时,\(dp_{i,j}\) 才会有值

通过仔细的思考 发现\(dp[i][j]=\sum_{k=0}^{i>2k}dp[i-2k-1][j-1]\) 然而这需要三个\(for\)循环,会挂。然后又惊奇的发现\(dp[i-2][j]=\sum_{k=0}^{i-2>2k}dp[i-2k-3][j-1]\) 因此\(dp[i][j]=dp[i-1][j-1]+dp[i-2][j]\)

这样就可以得\(70pts\)

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxn=2010;
const int mod=998244353;
int read(){
	int x=0,f=1;char ch=getchar();
	while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
int dp[maxn][maxn],n,m,t,ans;
int main(){
	freopen("temple.in","r",stdin);
	freopen("temple.out","w",stdout);
	for(int i=0;i<=2000;i++)
		dp[i][0]=1;
	dp[1][1]=1;
	for(int i=2;i<=2000;i++){
		for(int j=1;j<=i;j++){
			if(j%2==i%2){
				dp[i][j]=(dp[i-1][j-1]+dp[i-2][j])%mod;
			}
			else{
				dp[i][j]=0;
			}
		}
	}
	t=read();
	while(t--){
		n=read();m=read();
		if(n%2==m%2)
			ans=dp[n][m];
		else ans=dp[n-1][m];
		printf("%d\n",(ans%mod+mod)%mod);
	}
}

现在是满分算法: 用出题人的思路来说就是 显然\(a_i+i\)一定为偶数。\(1<=a_i+i<=n+m\) 就相当于在\([1,n+m]\)里面选m个偶数 ?? 所以说这个\(a_i+i\) 是怎么想到的QAQ

然后就相当于答案就是\(\displaystyle\binom {\lfloor \frac {n+m}{2}\rfloor}{m}\)\(\displaystyle x=\lfloor \frac {n+m}{2}\rfloor,y=m\) 即为\(\displaystyle \frac{x!}{y!(x-y)!}\% mod\) 除一个数等于乘上他的逆元,这里相当于求\(y!,(x-y)!\) 的逆元 然后大致就可以做了。。。吗?

这道题有多个询问,每一次都求逆元显然亲妈爆炸,所以我们考虑预处理。。线性求逆元QAQ

\(a*a^{-1}≡b*b^{-1}\(mod p)\) 移项得到\(a^{-1}≡b^{-1}*(\frac {b}{a})\) 在这道题中,若\(b=i!,a=(i-1)!\) 那么\((i-1)!^{-1}=i!^{-1}*i\) 然后将\(1e6!^{-1}\)用费马小定理随便乱搞一下就好了。

另外注意:线性求逆元显然不是这样推导的,因为关于如果\(a,b\)不整除,我这种推导就GG了,线性求逆元的正确推导是这样的:

\(a÷b=q……r\)

\(q⋅b+r≡0(moda)\) 左右同时乘上\(b^{-1}\cdot r^{-1}\)

\(q⋅r^{−1}+b^{−1}≡0(modp)\)

\(b^{-1}≡−q⋅r{−1}(modp)\)

\(b^{−1}≡−⌊\frac a b⌋⋅(amodb)^{−1}(moda)\)

\(b^{−1}≡a-⌊\frac a b⌋⋅(amodb)^{−1}(moda)\)

。。显然这个推导在这道题中也是对的 但是好像用之前的那个式子更方便吧

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#define LL long long 
using namespace std;
const int maxn=1e6;
const int mod=998244353;
int read(){
	int x=0,f=1;char ch=getchar();
	while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
int factorial[maxn+10],inv[maxn+10],t;
LL ksm(int x,int y){
	LL sum=1;
	while(y){
		if(y&1) sum=(long long)sum*x%mod;
		y>>=1;x=(long long)x*(long long)x%mod;
	}

	return sum%mod;
}
void prepare_work(int x){
	factorial[0]=1;
	for(int i=1;i<=maxn;i++) factorial[i]=(long long)factorial[i-1]*i%mod;
	inv[maxn]=(int)ksm(factorial[maxn],mod-2);
	for(int i=maxn-1;i>=0;i--){
		inv[i]=(long long)inv[i+1]*(i+1)%mod;
	}
}
int n,m;
long long C(int x,int y){
	return (long long)factorial[y]*inv[x]%mod*inv[y-x]%mod;
}
int main(){
	prepare_work(maxn);
	t=read();
	while(t--){
		n=read();m=read();
		printf("%lld\n",C(m,(n+m)/2));
	}
	return 0;
}

T3

我们令询问\([x,y]\)为询问区间,当前节点\([l,r]\)为当前区间 那么总共分为四种情况:

1.当前区间完全包含询问区间:显然询问区间的所有子区间都对当前区间有贡献

2.当前区间与询问区间真相交:应该也很好算,用等差数列求和公式就可以算了,在代码里面应该很明了

3.询问区间完全包含当前区间:等下讨论

4.询问区间与当前区间交集为空:没有贡献

那么现在来讨论第三种情况:

这个考虑预处理,不然会遍历到线段树底部(即有可能退化成\(O(n^2)\) )

对于当前节点,它对答案的贡献有两种:它作为经过节点;它作为终止节点

如果它作为经过节点,那么它的贡献为:当前区间与询问区间相交,并且询问区间不包含当前区间。

那么其贡献用可爱的小手写出来的话就是:\((r-l)\cdot(l-ql+r-qr)+\frac {(r-l+1)\cdot(r-l+2)}{2}\)

如果它作为终止节点,那么它的贡献为:完全包含当前区间的区间数量\(-\)完全包含其父亲区间的区间数量

如果我们将叶节点打上\(+1\)标记,其他的全部打上\(-1\)标记,稍微容斥一下就可以发现其子树的系数和就是当前节点以及子树所有节点的总贡献的系数。

大概就是这样吧QAQ

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#define int long long 
using namespace std;
const int maxn=1000;
int read(){
	int x=0,f=1;char ch=getchar();
	while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
int kx[maxn<<2],ky[maxn<<2],kxy[maxn<<2],b[maxn<<2];
int n,q,opt,lastans;
void build(int node,int l,int r){
	if(l==r){
		kx[node]+=r-1;ky[node]+=l+1;
		kxy[node]--;b[node]=(l-r+1-l*r);
		return ;
	}
	kx[node]-=(r-1);ky[node]-=(l+1);
	kxy[node]+=1;b[node]-=(l-r+1-l*r);
	kx[node]+=(l-r);ky[node]+=(r-l);
	b[node]+=(r-l+1)*(r-l+2)/2-1;
	b[node]+=(2*r*l-l*l-r*r);
	int mid=l+r>>1;
	build(node<<1,l,mid);build(node<<1|1,mid+1,r);
	kx[node]+=kx[node<<1]+kx[node<<1|1];
	ky[node]+=ky[node<<1]+ky[node<<1|1];
	kxy[node]+=kxy[node<<1]+kxy[node<<1|1];
	b[node]+=b[node<<1]+b[node<<1|1];
}
int query(int node,int l,int r,int ql,int qr){
	if(ql<=l && r<=qr){
		int ans=0;
		ans+=kx[node]*ql;ans+=ky[node]*qr;ans+=kxy[node]*ql*qr;ans+=b[node];
		return ans;
	}	
	if(l<=ql && qr<=r){
		int mid=l+r>>1,ans=0;
		ans=(qr-ql+1)*(qr-ql+2)/2;
		if(ql<=mid) ans+=query(node<<1,l,mid,ql,qr);
		if(mid<qr) ans+=query(node<<1|1,mid+1,r,ql,qr);
		return ans;
	}
	int ans=0,mid=l+r>>1;
	if(ql<l) ans+=(qr-ql+1+l-ql+1)*(qr-l+1)/2;
	if(r<qr) ans+=(qr-ql+1+qr-r+1)*(r-ql+1)/2;
	if(ql<=mid) ans+=query(node<<1,l,mid,ql,qr);
	if(mid<qr) ans+=query(node<<1|1,mid+1,r,ql,qr);
	return ans;
}
signed main(){
	n=read();q=read();opt=read();
	build(1,1,n);
	for(int i=1,x,y;i<=q;i++){
		x=read();y=read();
		x=(x^(lastans*opt))%n+1;y=(y^(lastans*opt))%n+1;
		if(x>y) swap(x,y);
		lastans=query(1,1,n,x,y);
		printf("%lld\n",lastans);
	}
	return 0;
}
posted on 2019-11-12 07:34  萌德真帅  阅读(159)  评论(0编辑  收藏  举报