聯合省選2020 A卷 題解
D1T1 冰火戰士
Description
有兩個元素集合 \(S,T\),每個元素都有權值 \(c,w\),\(q\) 次插入或刪除一個元素,詢問:
\[\max_{t}(2\min(\sum_{i\in S}[c_i\le t]w_i,\sum_{i\in T}[c_i\ge t]w_i)) \]並求出達到最大值的 \(t\),若有多個 \(t\) ,取其中最大的。
\(q\le 2\times 10^6,c_i\le 2\times 10^9\)。
Solution
首先最終的 \(t\) 一定會在某一個 \(c_i\) 處取到,因此可以先將 \(c_i\) 離散化。
由於最終求的是兩坨東西的 \(\min\)
求不降函式的零點,容易想到直接用線段樹維護 \(a_t-b_t\),然後線上段樹上二分,這樣做是 \(\mathcal O(n\log n)\) 的,但常數較大,可能會被卡。
事實上,我們也可以用樹狀陣列維護,具體而言,用兩棵樹狀陣列分別維護 \(a_t\) 與 \(b_t\)。找交界點時,假設現在我們正在 \(pos\) 位置(初始為 \(0\)),目前的 \(a_{pos}-b_{pos}\le 0\)
至此我們 \(\mathcal O(n\log n)\) 的找到了最大的使得 \(a_t\le b_t\) 的 \(t\),\(t+1\) 就是最小的 \(a_t>b_t\) 的 \(t\),顯然答案一定在它們中產生。注意當 \(t+1\)
最終複雜度為小常數 \(\mathcal O(n\log n)\)。
Code
#include<bits/stdc++.h>
using namespace std;
const int N=2e6+10;
int q,d[N],tot,ct[2];
struct node{
int t,x,y;
}a[N];
struct BIT{
int c[N];
inline int lowbit(int x){return x&(-x);}
inline void update(int x,int y){
for(;x<=tot;x+=lowbit(x)) c[x]+=y;
}
inline int query(int x){
int ans=0;
for(;x;x-=lowbit(x)) ans+=c[x];
return ans;
}
}T[2];
int dt;
int main(){
scanf("%d",&q);
for(int i=1,op,k;i<=q;++i){
scanf("%d",&op);
if(op==1) scanf("%d%d%d",&a[i].t,&a[i].x,&a[i].y);
else{
scanf("%d",&k);
a[i]=a[k];a[i].y=-a[i].y;
}
d[i]=a[i].x;
}
sort(d+1,d+q+1);
tot=unique(d+1,d+q+1)-d-1;
for(int i=1;i<=q;++i){
a[i].x=lower_bound(d+1,d+tot+1,a[i].x)-d;
int tp=a[i].t;
if(a[i].y>0) ct[tp]++;
else ct[tp]--;
if(!tp) T[0].update(a[i].x,a[i].y);
else T[1].update(a[i].x+1,-a[i].y),dt+=a[i].y;
if(!ct[0]||!ct[1]){puts("Peace");continue;}
int now=0,ret=-dt;
for(int i=20;i>=0;--i){
if(now+(1<<i)>tot) continue;
int x=now+(1<<i);
if(T[0].c[x]-T[1].c[x]+ret<=0) now=x,ret+=T[0].c[x]-T[1].c[x];
}
ret=min(T[0].query(now),T[1].query(now)+dt);
int tmp=min(T[0].query(now+1),T[1].query(now+1)+dt);
if(now<tot&&tmp>=ret){
int lim=T[1].query(now+1);
ret=0;now=0;
for(int i=20;i>=0;--i){
if(now+(1<<i)>tot) continue;
int x=now+(1<<i);
if(ret+T[1].c[x]>=lim) ret+=T[1].c[x],now=x;
}
ret=tmp;
}
if(!ret){puts("Peace");continue;}
printf("%d %d\n",d[now],ret<<1);
}
return 0;
}
D1T2 組合數問題
Description
請求出:
\[\sum_{k=0}^{n}f(k)x^k\binom nk\pmod p \]其中 \(f(k)\) 是關於給定的一個 \(m\) 次多項式 \(f(k)=\sum_{i=0}^{m}a_ik^i\)。
\(m\le 1000,n,x,p\le 10^9\)。
Soltion
首先將多項式展開:
\[=\sum_{k=0}^{n}\sum_{i=0}^{m}a_ik^ix^k\binom nk\\ =\sum_{i=0}^{m}a_i\sum_{k=0}^{n}k^ix^k\binom nk \]注意到 \(x^k\dbinom nk\) 類似二項式定理的形式,而前面這個 \(k^i\) 怎麼看怎麼不對勁,於是套路的將它轉化為下降冪:
\[=\sum_{i=0}^{m}a_i\sum_{k=0}^{n}\sum_{j=0}^{i}\ \left\{\begin{matrix}i\\j\end{matrix}\right\}k^{\underline{j}} x^k\binom nk\\ =\sum_{i=0}^{m}a_i\sum_{j=0}^{i}\ \left\{\begin{matrix}i\\j\end{matrix}\right\}\sum_{k=0}^{n}k^{\underline{j}} x^k\binom nk\\ =\sum_{i=0}^{m}a_i\sum_{j=0}^{i}\ \left\{\begin{matrix}i\\j\end{matrix}\right\}\sum_{k=0}^{n}\binom{k}{j}j! x^k\binom nk\\ \]這是三項式係數,可以轉化為
\[=\sum_{i=0}^{m}a_i\sum_{j=0}^{i}\ \left\{\begin{matrix}i\\j\end{matrix}\right\}j!\sum_{k=0}^{n}\binom{n}{j}\binom{n-j}{k-j} x^k\\ =\sum_{i=0}^{m}a_i\sum_{j=0}^{i}\ \left\{\begin{matrix}i\\j\end{matrix}\right\}j!\binom nj\sum_{k=0}^{n-j}\binom{n-j}{k} x^kx^j\\ \]終於舒服了,二項式定理得到:
\[=\sum_{i=0}^{m}a_i\sum_{j=0}^{i}\ \left\{\begin{matrix}i\\j\end{matrix}\right\}j!\binom njx^j(x+1)^{n-j} \]直接遞推處理第二類斯特林數,即可做到 \(\mathcal O(m^2)\)
Code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1010;
struct FastMod{
typedef unsigned long long ULL;
typedef __uint128_t LLL;
ULL b,m;
void init(ULL b){this->b=b,m=ULL((LLL(1)<<64)/b);}
ULL operator()(ULL a)const{
ULL q=(ULL)((LLL(m)*a)>>64);
ULL r=a-q*b;
return r>=b?r-b:r;
}
}mod;
inline ll operator %(ll x,const FastMod &mod){return mod(x);}
int n,x,md,m,a[N],str[N][N],base[N],low[N];
inline int ksm(int x,int y){
int ret=1;
for(;y;y>>=1,x=1ll*x*x%mod) if(y&1) ret=1ll*ret*x%mod;
return ret;
}
inline void init(){
for(int i=str[0][0]=1;i<=m;++i){
for(int j=1;j<=m;++j)
str[i][j]=(str[i-1][j-1]+1ll*j*str[i-1][j])%mod;
}
low[0]=1;
for(int i=1;i<=m;++i) low[i]=1ll*low[i-1]*(n-i+1)%mod;
}
int main(){
scanf("%d%d%d%d",&n,&x,&md,&m);
mod.init(md);
init();
int ans=0;
for(int i=0;i<=m;++i){
scanf("%d",&a[i]);
int ret=0,pw1=1;
for(int j=0;j<=i;++j,pw1=1ll*pw1*x%mod)
ret=(ret+1ll*str[i][j]*low[j]%mod*pw1%mod*ksm(x+1,n-j))%mod;
ans=(ans+1ll*ret*a[i])%mod;
}
printf("%d\n",ans);
return 0;
}
D1T3 魔法商店
這是一道神仙論文題,我在之前的部落格保序迴歸問題中已進行了詳細的講解。