21.7.2 t3
阿新 • • 發佈:2021-07-02
tag:二分,容斥,組合計數
仔細觀察可以發現,對於一個方程來說,將 \(t\) 作為橫座標,解數看成縱座標,那麼會是一個上凸函式(而且是對稱的,但不重要)。眾所周知幾個上凸函式的和也是上凸函式,而上凸函式的頂點可以二分,所以可以分段然後每一段二分頂點。
問題變為如何求一個點的值。
簡單轉化一下,有 \(n\) 個球,放到 \(3\) 個盒子裡,第 \(i\) 個盒子只能裝 \(a_i\) 個球,求有多少種裝法。
可以容斥,列舉強制讓哪些盒子的球超過容量,容斥係數為 \((-1)^{|S|}\)。
所以複雜度 \(O(n^2logm)\)
所以為什麼n只有50啊喂
#include<bits/stdc++.h> using namespace std; template<typename T> inline void Read(T &n){ char ch; bool flag=false; while(!isdigit(ch=getchar()))if(ch=='-')flag=true; for(n=ch^48;isdigit(ch=getchar());n=(n<<1)+(n<<3)+(ch^48)); if(flag)n=-n; } typedef long long ll; int n, a[5][5][55], mn[55], mx[55]; inline ll calc(int k){return 1ll*(k+2)*(k+1)/2;} inline ll calc(int t, int pos){ ll ans=0; for(int s=0; s<8; s++){ int cnt=0, sum=0; for(int i=0; i<3; i++) if(s>>i&1) cnt++, sum += a[i+1][2][pos]-a[i+1][1][pos]+1; if(sum>t) continue; if(cnt&1) ans -= calc(t-sum); else ans += calc(t-sum); } return ans; } inline ll check(int t){ ll res=0; for(int i=1; i<=n; i++) if(mn[i]<=t and t<=mx[i]) res += calc(t-mn[i],i); return res; } int bp[105]; inline ll solve(int l, int r){ int L=l, R=r; while(l<r){ int mid = l+r>>1; if(check(mid)<check(mid+1)) l = mid+1; else r = mid; } ll res=0; for(int i=l-2; i<=l+2; i++) if(L<=i and i<=R) res = max(res,check(i)); return res; } int main(){ // freopen("3.in","r",stdin); Read(n); int cnt=0; bp[++cnt] = -1; for(int T=1; T<=n; T++){ for(int i=1; i<=2; i++) for(int j=1; j<=3; j++) Read(a[j][i][T]); for(int i=1; i<=3; i++) mn[T] += a[i][1][T], mx[T] += a[i][2][T]; bp[++cnt] = mn[T]-1; bp[++cnt] = mx[T]; } sort(bp+1,bp+cnt+1); cnt = unique(bp+1,bp+cnt+1)-bp-1; ll res=0; for(int i=1; i<cnt; i++) res = max(res,solve(bp[i]+1,bp[i+1])); cout<<res<<'\n'; return 0; }