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}\)
#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小時
分類討論
-
\(x,y\)同側
把 \([x,y]\)看成一個球 ,就是n-(y-x) 個球放到m個盒子裡 (可以空)
剩餘 n 個球,放到m個盒子裡 (可以空) -
異側
剩餘 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;
}