• 微信公众号:美女很有趣。 工作之余,放松一下,关注即送10G+美女照片!

bzoj4221 过不了做法

开发技术 开发技术 5小时前 4次浏览

考场上连正解的第一步都没有想到,所以推出来了这个怪异的算法

我看着这个题面,就想着把每个袋鼠表示成一个区间[bi,ai],这样当某些区间两两没有交集,就可以合并成一个大的区间。而最后没有区间能合并了,说明剩下的区间有交集。考虑枚举这个交集[l,r]。

此时我们的做法看着还是很离谱,但是仔细考虑一下,可以从这个交集入手。每一个大区间,他的头和尾必须跨过这个交集。继续想一想,可以把每个大区间分成三部分,第一次跨过l以及此前的,第一次跨过r以及此后的,还有l,r都没有跨过也就是在中间的。前两个部分显然预处理,在中间的部分最后算一下。

先想前两个部分,可以分为两类,一类是跨过l的小区间(读入的区间),显然个数是固定的。另一类是(ri<l)的区间,这样的区间都是等价的。所以设(dp_{i,j})表示跨过i,有j个等价区间的方案数。每次从i-1转移到i,发现有一些区间从跨越变成等价区间,另一些区间加入了跨越区间,一些等价区间可以并到跨越区间,正好可以组合数算方案。

然后是第三个部分。我在这卡了挺久,其实也不是很难。这个问题和原问题的区别就是,路径条数可以确定,而且不用考虑交集的问题。现在左右有两排个数相等的点,都是上面说的跨越和等价区间,中间是被[l,r]包含的小区间。另外有一些区间完全覆盖了[l,r],可以直接删除。考虑所有区间按l排序,按拓扑序依次加入区间。对于每个区间,他的前面有一些和他相交的区间,这些区间显然不在相同的链里,这些链不能放,而另外的链随意放。连dp都不用,直接做乘法就可以了。

我们求出的是交集包含[l,r]的方案,最后做个简单容斥即可。

如果您愿意实现我这个方法,我将不胜荣幸,并预祝您体验到调试代码的快乐。

复杂度(O(n^4)),得到了70的高分!可能能用多项式优化

#include<bits/stdc++.h>
using namespace std;
#define forg(i,x) for(register int i=fir[x];i;i=nxt[i])
#define uu unsigned
#define scanf a14=scanf
#define rint register int
#define fre(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
typedef long long ll;
typedef uu long long ull;
typedef pair<int,int>pii;
int a14;
inline int rd(int l,int r){return rand()%(r-l+1)+l;}

const int mxn=603,mod=1e9+7;
int n,ls[mxn],m;int lsx(int x){return lower_bound(ls+1,ls+m+1,x)-ls;}
struct two{int l,r;two(){}two(int ll,int rr){l=ll,r=rr;}}a[mxn];
ll f1[mxn][mxn],f2[mxn][mxn],C[mxn][mxn],jc[mxn];
int cl[mxn],cr[mxn],cg[mxn];vector<int>vl[mxn],vr[mxn],vx[mxn];
int cc[mxn],nn;ll re[mxn][mxn];
struct zsz{int a[mxn];void clr(){memset(a,0,sizeof(a));}void add(int x){for(;x;x-=x&-x)++a[x];}int ask(int x){int r=0;for(;x<=m;x+=x&-x)r+=a[x];return r;}}ar;
int main(){
    scanf("%d",&n);for(int i=1,l,r;i<=n;++i)scanf("%d%d",&r,&l),ls[++m]=l,ls[++m]=r,a[i]=two(l,r);
    C[0][0]=1;for(int i=1;i<=n;++i){C[i][0]=1;for(int j=1;j<=i;++j)C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;}jc[0]=1;for(int i=1;i<=n;++i)jc[i]=jc[i-1]*i%mod;
    sort(ls+1,ls+m+1),m=unique(ls+1,ls+m+1)-ls-1;for(int i=1;i<=n;++i)a[i].l=lsx(a[i].l),a[i].r=lsx(a[i].r);
    for(int i=1;i<=n;++i)++cl[a[i].l],++cr[a[i].r],vx[a[i].l].push_back(a[i].r);
    for(int i=1;i<=n;++i)for(int j=a[i].l;j<=a[i].r;++j)++cg[j],vl[j].push_back(a[i].r),vr[j].push_back(a[i].l);
    for(int i=1;i<=m;++i)sort(vl[i].begin(),vl[i].end()),sort(vr[i].begin(),vr[i].end()),sort(vx[i].begin(),vx[i].end());
    f1[0][0]=1;for(int i=1;i<=m;++i){
        int x=cr[i-1],y=cl[i];
        for(int j=0;j<=n;++j)if(f1[i-1][j]){
            int t=j+x;for(int k=0;k<=t;++k)(f1[i][t-k]+=f1[i-1][j]*C[t][k]%mod*C[y][k]%mod*jc[k])%=mod;
        }
    }
    f2[m+1][0]=1;for(int i=m;i;--i){
        int x=cl[i+1],y=cr[i];
        for(int j=0;j<=n;++j)if(f2[i+1][j]){
            int t=j+x;for(int k=0;k<=t;++k)(f2[i][t-k]+=f2[i+1][j]*C[t][k]%mod*C[y][k]%mod*jc[k])%=mod;
        }
    }
    for(int l=1;l<=m;++l)for(int r=l;r<=m;++r){
        int x=cg[l],y=cg[r];
        ar.clr();nn=0;int k2=0;for(int i:vl[l]){if(i>=r)break;ar.add(i);}
        int p=0,zs=vr[r].size();while(p!=zs&&vr[r][p]<=l)++p,--x,--y;
        for(int i=l+1;i<r;++i){
            for(int j:vx[i]){if(j>=r)break;cc[++nn]=ar.ask(i)+k2,ar.add(j);}
            for(;p!=zs&&vr[r][p]==i;++p)cc[++nn]=ar.ask(i)+(k2++);
        }
        for(;p!=zs;++p)assert(vr[r][p]==r),cc[++nn]=ar.ask(r)+(k2++);
        ll B=1;int k0=max(x,y);k2=k0-k2;
        if(x>y)for(int i=0;i<x-y;++i)B=B*(k2-i)%mod;
        for(int k=k0;k<=n;++k,B=B*(++k2)%mod)if(f1[l][k-x]&&f2[r][k-y]){
            ll fx=1;for(int i=1;i<=nn;++i)fx=fx*(k-cc[i])%mod;
            fx=fx*B%mod;
            re[l][r]+=fx*f1[l][k-x]%mod*f2[r][k-y]%mod;
        }
        re[l][r]%=mod;
    }
    ll ans=0;for(int l=1;l<=m;++l)for(int r=l;r<=m;++r)ans+=re[l][r]-re[l-1][r]-re[l][r+1]+re[l-1][r+1];
    ans=(ans%mod+mod)%mod;printf("%lldn",ans);
    return 0;
}


程序员灯塔
转载请注明原文链接:bzoj4221 过不了做法
喜欢 (0)