1. 程式人生 > 實用技巧 >NOI ONLINE部分題解

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;
}