NOI模拟19

网格图我用的spfa,然后寄了,不对,还有建议直接建边,要不然每次上下左右找的话常数太大了!!

第一题似乎是个nb题,于是我开始想\(meet\ in\ the\ middle\),于是搞到了60,正解是个傻逼做法

后面两个只会打最短路辽~~

T1 书

关于这个傻逼正解我就不多说啥了,我来说一下我的折半状压

发现有用的对只有20个,所以我们压这样的对是否可以在之后出现,然后用一些奇妙的办法,可以将复杂度降至 \(\mathcal{O(2^{\frac{n}{2}}n^3)}\)

于是这个做法极其难写又难调,还是写正解吧,正解就是傻逼dp,nnd

折半dp
#include<bits/stdc++.h>
using namespace std;
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
int read(){
    int s=0,t=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')t=-1;ch=getchar();}
    while(isdigit(ch)){s=(s<<1)+(s<<3)+(ch^48);ch=getchar();}
    return s*t;
}
const int N=1<<20;
const int mod=1e9+7;
int n,a[45];char b[45];
int dp[2][N][41],ans;
bool ok(int x){return !(x&1);}
signed main(){
    freopen("book.in","r",stdin);
    freopen("book.out","w",stdout);
    // cerr<<(sizeof(dp)>>20)<<endl;
    n=read();scanf("%s",b+1);
    fo(i,1,n-1)a[i]=b[i]-'0';
    int u=(1<<(n>>1))-1;
    int now=0;dp[now][0][0]=1;
    fo(o,0,n-1){
        now^=1;
        int nt=o+1;while(a[nt]==1)nt++;
        int len=nt-o;
        fo(s,0,u){
            int cnt=0;
            fo(i,1,n>>1)if(!(s>>i-1&1)){
                cnt+=2;
                if(ok(i)&&(!(s>>((i>>1)-1)&1)))cnt--;
            }
            cnt=n-cnt-o;
            if(len==1){
                fo(i,0,n)if(dp[now^1][s][i]){
                    if(i&&((i*2<=n&&!(s>>i-1&1))||(ok(i)&&!(s>>(i>>1)-1&1)))){cnt++;}
                    int t=s;if(i)t|=((i*2<=n)?(1<<i-1):0)|(ok(i)?(1<<((i>>1)-1)):0);
                    dp[now][t][0]=(dp[now][t][0]+1ll*dp[now^1][s][i]*cnt)%mod;
                    fo(j,1,n>>1){
                        if(i!=j&&i*2!=j&&j*2!=i&&(!(s>>j-1&1)||(ok(j)&&!(s>>((j>>1)-1)&1)))){
                            dp[now][t][j]=(dp[now][t][j]+dp[now^1][s][i])%mod;
                        }
                        if(j*4>n&&i!=j&&i!=j*2&&(!(s>>(j-1)&1))){
                            dp[now][t][j*2]=(dp[now][t][j*2]+dp[now^1][s][i])%mod;
                        }
                    }
                    if(i&&((i*2<=n&&!(s>>i-1&1))||(ok(i)&&!(s>>(i>>1)-1&1)))){cnt--;}
                    dp[now^1][s][i]=0;
                }
            }
            else {
                fo(i,0,n)if(dp[now^1][s][i]){
                    fo(j,1,n/(1<<len-1))if(i!=j){
                        bool flag=true;if(j*(1<<len-1)==i)continue;
                        fo(k,0,len-2)if(j*(1<<k)==i||(s>>(j*(1<<k))-1)&1){flag=false;break;}
                        if(!flag)continue;

                        if(i*2!=j&&j*2!=i){
                            int t=s;if(i)t|=((i*2<=n)?(1<<i-1):0)|(ok(i)?(1<<((i>>1)-1)):0);
                            if(ok(j))t|=(1<<((j>>1)-1));
                            fo(k,0,len-2)t|=(1<<(j*(1<<k)-1));
                            if(j*(1<<len-1)*2<=n)dp[now][t][j*(1<<len-1)]=(dp[now][t][j*(1<<len-1)]+dp[now^1][s][i])%mod;
                            else dp[now][t][0]=(dp[now][t][0]+dp[now^1][s][i])%mod;
                        }

                        if(j*(1<<len-1)*2!=i&&j*(1<<len-1)!=i*2){
                            int t=s;if(i)t|=((i*2<=n)?(1<<i-1):0)|(ok(i)?(1<<((i>>1)-1)):0);
                            fo(k,0,len-1)if(j*(1<<k)*2<=n)t|=(1<<(j*(1<<k)-1));
                            if(ok(j)&&!(s>>(j>>1)-1&1))dp[now][t][j]=(dp[now][t][j]+dp[now^1][s][i])%mod;
                            else dp[now][t][0]=(dp[now][t][0]+dp[now^1][s][i])%mod;
                        }
                    }
                    dp[now^1][s][i]=0;
                }
            }
        }
        o=nt-1;
    }
    fo(s,0,u)fo(i,0,n)if(dp[now][s][i])ans=(ans+dp[now][s][i])%mod;
    printf("%d\n",ans);
    return 0;
}
AC_code(傻逼dp)
#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define int long long
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
int read(){
    int s=0,t=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')t=-1;ch=getchar();}
    while(isdigit(ch)){s=(s<<1)+(s<<3)+(ch^48);ch=getchar();}
    return s*t;
}
const int N=45;
const int mod=1e9+7;
struct DP{
    int a[7];bool fl;
    DP(){memset(a,0,sizeof(a));fl=false;}
    void init(){memset(a,0,sizeof(a));fl=false;}
    bool operator < (DP x)const{
        fo(i,0,6)if(a[i]!=x.a[i])return a[i]<x.a[i];
        return fl<x.fl;
    }
};
map<DP,int> mp[N];
int n,tp[N],ans;char ch[N];
signed main(){
    freopen("book.in","r",stdin);
    freopen("book.out","w",stdout);
    n=read();scanf("%s",ch+1);
    DP sta;fo(i,1,n)if(i&1){
        int nw=i,ct=0;
        while(nw<=n)ct++,nw*=2;
        if(ct>1)sta.a[ct-2]++;
    }mp[0][sta]=1;int lst=0;
    fo(o,0,n-1){
        int to=o+1;while(ch[to]=='1')to++;
        int len=to-o;
        for(pair<DP,int> x:mp[o]){
            DP s=x.first;int v=x.second,sm;
            sm=0;fo(i,2,6)tp[i]=s.a[i-2],sm+=s.a[i-2]*i;
            tp[1]=n-o-sm;tp[7]=s.a[5];tp[8]=s.a[6];
            fo(i,len,6)if(tp[i])fo(j,1,i-len+1){
                int xsz=tp[i],xsf=tp[i];DP t;
                if((!s.fl||lst==1)&&i==tp[8]&&j==1)xsz--;
                if((s.fl||lst==1)&&i==tp[7]&&j==i-len+1){xsf--;if(len==1)xsz--;}
                fo(i,2,6)t.a[i-2]=tp[i];
                if(i>1)t.a[i-2]--;if(j-1>1)t.a[j-1-2]++;if(i-len+1-j>1)t.a[i-len+1-j-2]++;
                t.a[5]=j-1;t.a[6]=i-len+1-j;
                t.fl=0;mp[to][t]=(mp[to][t]+v*xsz)%mod;
                if(len!=1)t.fl=1,mp[to][t]=(mp[to][t]+v*xsf)%mod;
            }
        }
        lst=len;o=to-1;
    }
    for(pair<DP,int> x:mp[n])ans=(ans+x.second)%mod;
    printf("%lld\n",ans);
}

T2 鼠

发现n非常的小,似乎和noi_online的最后一个题非常的像....并没有什么鸟关系

考场上只会最短路,发现可以将贡献拆成两部分,于是可以分治,每次从分治中心向两侧跑最短路,在中间合并

合并的过程是个二维数点,每次钦定谁是最小值就容易转化了

AC_code
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
int read(){
    int s=0,t=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')t=-1;ch=getchar();}
    while(isdigit(ch)){s=(s<<1)+(s<<3)+(ch^48);ch=getchar();}
    return s*t;
}
const int N=1e5+5;
const int mod=1e9+7;
const int inf=0x3f3f3f3f3f3f3f3f;
struct BIT{
    int tr1[3*N],tr2[3*N],n;
    void init(int x){n=x;fo(i,1,n)tr1[i]=tr2[i]=0;}
    void ins(int x,int v){
        v%=mod;
        for(int i=x;i<=n;i+=(i&-i)){
            tr2[i]=(tr2[i]+v)%mod;
            tr1[i]=(tr1[i]+1)%mod;
        }
    }
    pair<int,int> qry(int x){
        int ret1=0,ret2=0;
        for(int i=x;i;i-=(i&-i)){
            ret1=(ret1+tr1[i])%mod;
            ret2=(ret2+tr2[i])%mod;
        }
        return {ret1,ret2};
    }
}bit;
int mx[4]={0,0,1,-1};
int my[4]={1,-1,0,0};
vector<pair<int,int>> e[3*N];
int n,a[4][N],v[3*N],id[4][N],ans;
inline bool jd(int x,int y){return x&&x<=3&&y&&y<=n;}
struct DS{
    int i,d;
    bool operator < (DS a)const{
        return d>a.d;
    }
};
priority_queue<DS> q;
int dis[4][3*N];bool vis[3*N];
void DIJ(int l,int r,int id,int tp){
    fo(i,l,r)fo(j,1,3)dis[tp][(i-1)*3+j]=inf,vis[(i-1)*3+j]=false;
    dis[tp][id]=v[id];q.push(DS{id,dis[tp][id]});
    while(!q.empty()){
        int x=q.top().i;q.pop();
        // cerr<<x<<" "<<vis[x]<<" "<<dis[tp][x]<<endl;
        if(vis[x])continue;vis[x]=true;
        for(pair<int,int> y:e[x]){
            if(y.first<=(l-1)*3||y.first>r*3)continue;
            // cerr<<y<<endl;
            if(dis[tp][y.first]<=dis[tp][x]+y.second)continue;
            dis[tp][y.first]=dis[tp][x]+y.second;
            q.push(DS{y.first,dis[tp][y.first]});
        }
    }
}
struct node{
    int a,b,id;
    bool operator < (node x)const{
        if(a!=x.a)return a<x.a;
        return b<x.b;
    }
    bool operator == (node x)const{return a==x.a&&b==x.b;}
}le[3*N],ri[3*N];
int lsh[3*N],lh,ll[N],rr[N];
void sol(int l,int r){
    // cerr<<l<<" "<<r<<endl;
    if(l==r){
        ans=(ans+a[1][l]+a[2][l]*2+a[3][l]+min(ll[l],rr[l]))%mod;
        // cerr<<l<<" "<<a[1][l]<<" "<<a[2][l]<<" "<<a[3][l]<<" "<<ans<<endl;
        return ;
    }
    int mid=l+r>>1,sl=(mid-l+1)*3,sr=(r-mid)*3;
    
    sol(l,mid);sol(mid+1,r);

    fo(i,1,3)fo(j,l,r)e[id[i][j]].clear();
    fo(i,1,3)fo(j,l,mid){
        fo(k,0,3){
            int x=i+mx[k],y=j+my[k];
            if(!(x&&x<=3&&y>=l&&y<=mid))continue;
            e[id[i][j]].push_back({id[x][y],v[id[x][y]]});
        }
    }
    e[id[1][l]].push_back({id[3][l],ll[l]-v[id[1][l]]});
    e[id[3][l]].push_back({id[1][l],ll[l]-v[id[3][l]]});
    fo(i,1,3)fo(j,mid+1,r){
        fo(k,0,3){
            int x=i+mx[k],y=j+my[k];
            if(!(x&&x<=3&&y>mid&&y<=r))continue;
            e[id[i][j]].push_back({id[x][y],v[id[x][y]]});
        }
    }
    e[id[1][r]].push_back({id[3][r],rr[r]-v[id[1][r]]});
    e[id[3][r]].push_back({id[1][r],rr[r]-v[id[3][r]]});

    fo(i,1,3)DIJ(l,mid,id[i][mid],i);
    fo(i,1,3)DIJ(mid+1,r,id[i][mid+1],i);

    // if(l==1&&r==2){
    //     fo(i,1,sl)cerr<<i+(l-1)*3<<" "<<dis[1][i+(l-1)*3]<<endl;
    //     fo(i,1,sr)cerr<<i+mid*3<<" "<<dis[1][i+mid*3]<<endl;
    // }

    // cerr<<ans<<endl;
    // cerr<<"SB1"<<endl;
    fo(i,1,sl)le[i]=node{dis[2][i+(l-1)*3]-dis[1][i+(l-1)*3],dis[3][i+(l-1)*3]-dis[1][i+(l-1)*3],i+(l-1)*3};
    fo(i,1,sr)ri[i]=node{dis[1][i+mid*3]-dis[2][i+mid*3],dis[1][i+mid*3]-dis[3][i+mid*3],i+mid*3};
    lh=0;fo(i,1,sl)lsh[++lh]=le[i].b;fo(i,1,sr)lsh[++lh]=ri[i].b;
    sort(lsh+1,lsh+lh+1);lh=unique(lsh+1,lsh+lh+1)-lsh-1;bit.init(lh);
    sort(le+1,le+sl+1);sort(ri+1,ri+sr+1);int ir=1;
    // cerr<<"ZZ"<<endl;
    fo(i,1,sl)le[i].b=lower_bound(lsh+1,lsh+lh+1,le[i].b)-lsh;
    fo(i,1,sr)ri[i].b=lower_bound(lsh+1,lsh+lh+1,ri[i].b)-lsh;
    // cerr<<"FK"<<endl;
    fo(i,1,sl){
        while(ir<=sr&&ri[ir].a<=le[i].a){
            bit.ins(ri[ir].b,dis[1][ri[ir].id]%mod),ir++;
            // cerr<<"SB"<<" "<<dis[1][ri[ir].id]<<" "<<ri[ir].id<<endl;
        }
        pair<int,int> res=bit.qry(le[i].b);
        ans=(ans+res.first*(dis[1][le[i].id]%mod)%mod+res.second)%mod;
        // cerr<<i<<" "<<ir<<" "<<ans<<" "<<res.first<<" "<<res.second<<" "<<dis[1][le[i].id]<<" "<<le[i].id<<endl;
    }
    // cerr<<ans<<endl;

    // cerr<<"SB2"<<endl;
    fo(i,1,sl)le[i]=node{dis[1][i+(l-1)*3]-dis[2][i+(l-1)*3],dis[3][i+(l-1)*3]-dis[2][i+(l-1)*3],i+(l-1)*3};
    fo(i,1,sr)ri[i]=node{dis[2][i+mid*3]-dis[1][i+mid*3],dis[2][i+mid*3]-dis[3][i+mid*3],i+mid*3};
    lh=0;fo(i,1,sl)lsh[++lh]=le[i].b;fo(i,1,sr)lsh[++lh]=ri[i].b;
    sort(lsh+1,lsh+lh+1);lh=unique(lsh+1,lsh+lh+1)-lsh-1;bit.init(lh);
    sort(le+1,le+sl+1);sort(ri+1,ri+sr+1);ir=1;
    fo(i,1,sl)le[i].b=lower_bound(lsh+1,lsh+lh+1,le[i].b)-lsh;
    fo(i,1,sr)ri[i].b=lower_bound(lsh+1,lsh+lh+1,ri[i].b)-lsh;
    fo(i,1,sl){
        while(ir<=sr&&ri[ir].a<le[i].a)bit.ins(ri[ir].b,dis[2][ri[ir].id]%mod),ir++;
        pair<int,int> res=bit.qry(le[i].b);
        ans=(ans+res.first*(dis[2][le[i].id]%mod)%mod+res.second)%mod;
    }
    // cerr<<ans<<endl;

    // cerr<<"SB3"<<endl;
    fo(i,1,sl)le[i]=node{dis[1][i+(l-1)*3]-dis[3][i+(l-1)*3],dis[2][i+(l-1)*3]-dis[3][i+(l-1)*3],i+(l-1)*3};
    fo(i,1,sr)ri[i]=node{dis[3][i+mid*3]-dis[1][i+mid*3],dis[3][i+mid*3]-dis[2][i+mid*3],i+mid*3};
    lh=0;fo(i,1,sl)lsh[++lh]=le[i].b;fo(i,1,sr)lsh[++lh]=ri[i].b;
    sort(lsh+1,lsh+lh+1);lh=unique(lsh+1,lsh+lh+1)-lsh-1;bit.init(lh);
    sort(le+1,le+sl+1);sort(ri+1,ri+sr+1);ir=1;
    fo(i,1,sl)le[i].b=lower_bound(lsh+1,lsh+lh+1,le[i].b)-lsh;
    fo(i,1,sr)ri[i].b=lower_bound(lsh+1,lsh+lh+1,ri[i].b)-lsh;
    fo(i,1,sl){
        while(ir<=sr&&ri[ir].a<le[i].a)bit.ins(ri[ir].b,dis[3][ri[ir].id]%mod),ir++;
        pair<int,int> res=bit.qry(le[i].b-1);
        ans=(ans+res.first*(dis[3][le[i].id]%mod)%mod+res.second)%mod;
    }
    // cerr<<l<<" "<<r<<" "<<ans<<endl;
}
signed main(){
    freopen("mouse.in","r",stdin);
    freopen("mouse.out","w",stdout);
    n=read();
    fo(i,1,3)fo(j,1,n)a[i][j]=read();
    fo(i,1,3)fo(j,1,n)id[i][j]=(j-1)*3+i,v[id[i][j]]=a[i][j];
    int mn=inf;
    fo(i,1,n){
        mn=min(mn,a[2][i]);
        mn+=a[1][i]+a[3][i];
        ll[i]=mn;
    }
    mn=inf;
    fu(i,n,1){
        mn=min(mn,a[2][i]);
        mn+=a[1][i]+a[3][i];
        rr[i]=mn;
    }
    // fo(i,1,n)ans=(ans+res[i])%mod,cerr<<res[i]<<endl;
    sol(1,n);ans=ans*2%mod;
    printf("%lld\n",ans);
}

T3 束

这个题是非常棒的一个题,我对优化建图这种东西一直是不太熟悉的,但是似乎现在找到了感觉

首先我们可以将整张图拆成许多横着和竖着的线段,然后对这些线段建图,发现是\(n^2\)级别的,我们用线段树优化,然后跑0/1bfs,这个东西就是用双端队列,0往前,1往后,然后就完了

那么我就要对每个点统计横竖线段的最小值,因为横竖线段的差值一定是1,这样我们就统计差值是1的个数,最后减掉然后除以2就好啦

没写代码,懒了

posted @ 2022-06-08 14:45  fengwu2005  阅读(43)  评论(0编辑  收藏  举报