NOI ONLINE部分题解

S-2涂色问题

CF1260C —— 双倍经验

一眼瞪出结论(当然细节挂了)

就比较\(k\)\(\frac {p_2-2}{p_1} +1\)的大小

\(k=0\)的时候显然是“NO” ——我忘了

而且可能不互质,所以要搞个gcd ——模了几个样例才想起来

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;
const int N=1000005;
inline 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 f*x;
}
int p1,p2,k;
int gcd(int a,int b) {
	return b?gcd(b,a%b):a;
}
int main() {
	// freopen("color.in","r",stdin);
	// freopen("color.out","w",stdout);
	int T=read();
	while(T--) {
		p1=read();p2=read();k=read();
		if(p1>p2) swap(p1,p2);
		int g=gcd(p1,p2);
		p1/=g;p2/=g;
		int ans=p2%p1,res;
		if(ans==0) res=p2/p1-1;
		else if(ans==1) res=p2/p1;
		else res=p2/p1+1;
		if(k==1) puts("NO");
		else if(k>res) puts("YES");
		else puts("NO");
	}
	return 0;
}


S-2 子序列问题

复述一下题解

https://www.luogu.com.cn/blog/duyi/solution-p6477

首先看到 \(a[i]\)的范围,离散化

朴素是枚举左右端点,再用set维护集合元素个数,瓶颈在于枚举端点。

正解是考虑每个\(a[i]\)的贡献区间,设\(a[i]\)上次出现的位置为\(pre[i]\),那么贡献区间即为 \([ (pre[i],i] , [i,n] ]\)

接下来考虑平方的问题 ————经典套路

平方看成在这些位置中,任选出两个位置(可以重复)的方案数!

设它们分别为\((i,j)\)(不妨设\((i\leq j)\))。则一对\((i,j)\)会对多少个区间产生贡献?不难发现,数量是:

\((i-\max(\text{pre}[i],\text{pre}[j]))\cdot (n-j+1)\quad (i>\text{pre}[j])\)

暴力枚举 \((i,j)\)复杂度是\(O(n^2)\)的。考虑优化

如何快速求出:\(\sum_{i=1}^{j}\max(\text{pre}[i],\text{pre}[j])\)。我们用一个变量,记录\(j\)前面所有\(\text{pre}[i]\)之和,则只需要把\(\leq\text{pre}\)的这部分值减去,再加上相同数量的\(\text{pre}[j]\)即可。可以用两个树状数组,都以\(\text{pre}\)值为下标,分别维护\(\text{pre}\)值小于\(x\)\(\text{pre}\)值之和,及其数量。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;
typedef long long ll;
const int N=1000005;
const int P=1e9+7;
inline 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 f*x;
}

ll s[N],num[N];//树状数组
int n,a[N],b[N],mp[N],pre[N];
inline void upd(int x,int v) {for(int i=x;i<=n;i+=i&-i)s[i]+=x*v,num[i]+=v;}
inline ll sum(int x){ll res=0;for(int i=x;i;i-=i&-i)res+=num[i]*(x+1)-s[i];return res;}

int main() {
	n=read();
	for(int i=1;i<=n;i++) a[i]=b[i]=read();
	sort(b+1,b+1+n);
	int tot=unique(b+1,b+1+n)-b-1;
	for(int i=1;i<=n;i++) {
		a[i]=lower_bound(b+1,b+1+tot,a[i])-b;
		pre[i]=mp[a[i]];
		mp[a[i]]=i;
	}
	ll cnt=0,ans=0;
	for(int i=1;i<=n;i++) {
		cnt=(cnt+i-pre[i]+2*(sum(i)-sum(pre[i]))%P+P)%P;
		ans=(ans+cnt)%P;
		upd(pre[i]+1,1);upd(i+1,-1);
	}
	printf("%lld\n",(ans%P+P)%P);
	return 0;
}

J-2 建设城市

组合数学

组合弱者——推+写 近1小时

分类讨论

  1. \(x,y\)同侧
    \([x,y]\)看成一个球 ,就是n-(y-x) 个球放到m个盒子里 (可以空)
    剩余 n 个球,放到m个盒子里 (可以空)

  2. 异侧

剩余 n-x个球,放到m-i+1个盒子里(可以空)
剩余 y-1个球,放到m-i+1个盒子里(可以空)
剩余 n-y个球,放到i个盒子里(可以空)

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;
typedef long long ll;
const int N=200005;
const int P=998244353;
inline 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 f*x;
}
int m,n,x,y;
ll fac[N],inv[N];
ll qpow(ll a,ll b) {
	ll res=1;
	while(b) {
		if(b&1) res=res*a%P;
		a=a*a%P;
		b>>=1;
	}
	return res;
}
inline ll C(int n,int m) {
	if(n<0||n<m||m<0) return 1;
	return fac[n]*inv[m]%P*inv[n-m]%P;
}
ll ans;
int main() {
	m=read();n=read();x=read();y=read();
	fac[0]=1;
	for(int i=1;i<=n+m;i++) 
		fac[i]=fac[i-1]*i%P;
	inv[n+m]=qpow(fac[n+m],P-2);
	for(int i=n+m;i;i--) inv[i-1]=inv[i]*i%P;
	if(x>y) swap(x,y);
	if(x<=n&&y>n) {//异侧
		y-=n;
		for(int i=1;i<=m;i++) 
			ans=(ans+C(x-2+i,i-1)*C(n-x+m-i,m-i)%P*C(y-1+m-i,m-i)%P*C(n-y+i-1,i-1)%P)%P;
		printf("%lld\n",ans);
	}
	else { //同侧
		ans=C(n-(y-x)+m-1,m-1)*C(n+m-1,m-1)%P%P;
		printf("%lld\n",ans);
	}
	return 0;
}

posted @ 2020-12-08 14:17  ke_xin  阅读(73)  评论(0编辑  收藏  举报