21.7.2 t3

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;
}
posted @ 2021-07-02 16:39  oisdoaiu  阅读(30)  评论(0编辑  收藏  举报