省选模拟23

今天的题确实是有技术含量,我改到现在才(21:00)改完

以为自己要垫底了,zxb上来就开打,但是我发现第一题并不是很容易做的

于是半个小时之后转战第二题,发现是个插头dp,于是写完之后就知道自己T飞了

弃掉干T3,然而只会第一个部分分,也就是暴力分

T1 高维游走

能看出来是个背包,用那个啥啥(库默尔)定理,得到后面所有选择是t0的二进制划分,与ti是自己

于是我们可以直接做背包,这样的话是值域的

我们想要合并背包,考场上没有想出来太好的办法,于是就鸽掉了

发现当前位只会受到这一位的选择和之前的进位的影响,那么我们想要记录最后一位剩下多少并且和后面合并

有一点dp套dp的意思

我们直接记录最后一位是x的数有多少个数是合法的

但是我们发现这样记录好像没有办法转移诶!!???毕竟我们不知道这里面和别的重复的有多少

于是我们想把最后一位是0~m的都表示出来,而这个任何数的范围很小(最多进m位好吧),可以二进制

我们用某一个二进制的状态表示当前状态下的合法的数有多少个,这样转移也是一起转移,就不怕记重了......

AC_code
#include<bits/stdc++.h>
using namespace std;
#define ll 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*10+ch-'0';ch=getchar();}
    return s*t;
}
int T,n,u;ll t[15];
ll f[40][1<<11];
int zy[15][1<<11];
pair<int,int> ok[1<<21];
signed main(){
    freopen("travel.in","r",stdin);
    freopen("travel.out","w",stdout);
    T=read();
    fo(i,0,10){
        fo(s,0,(1<<11)-1){
            fo(j,0,10){
                if(!(s>>j&1))continue;
                zy[i][s]^=(1ll<<j+i);
            }
        }
    }
    fo(s,0,(1<<21)-1){
        int ss1=0,ss2=0;
        fo(j,0,20)if(s>>j&1){
            if(j&1)ss1|=(1ll<<(j>>1));
            else ss2|=(1ll<<(j>>1));
        }
        ok[s]=make_pair(ss1,ss2);
    }
    while(T--){
        n=read();fo(i,0,n)t[i]=read();
        f[0][1]=1;u=(1<<n+1)-1;
        fo(i,0,34){
            if(!(t[0]>>i&1)){
                fo(s,0,u){int buc=0;
                    fo(i,0,n)if(s>>i&1)buc^=(1<<i);
                    int ss1=ok[buc].first,ss2=ok[buc].second;
                    if(ss1)f[i+1][ss1]+=f[i][s];
                    if(ss2)f[i+1][ss2]+=f[i][s];
                }
                continue;
            }
            fo(s,0,u){int buc=0;
                fo(k,0,n){
                    if(!(t[k]>>i&1))continue;
                    buc^=zy[k][s];
                }
                int ss1=ok[buc].first,ss2=ok[buc].second;
                if(ss1)f[i+1][ss1]+=f[i][s];
                if(ss2)f[i+1][ss2]+=f[i][s];
            }
        }
        printf("%lld\n",f[35][1]);
        fo(i,0,35)fo(s,0,u)f[i][s]=0;
    }
}

T2 过山车

正解竟然是网络流,发现一个规律,网格图是可以黑白染色变成二分图的

并且一般网络流的图就考虑正着最大和总的减去反着最小......

这个就是总的减去反着最小,我们跑最小费用最大流就好了

AC_code
#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*10+ch-'0';ch=getchar();}
    return s*t;
}
const int N=6005;
const int inf=0x3f3f3f3f;
struct E{int to,nxt,val,cot;}e[N*25];
int head[N*3],rp=1,s=4500*3+1,t=4500*3+2;
void add_edg(int x,int y,int z,int w){
    e[++rp].to=y;e[rp].nxt=head[x];
    e[rp].val=z;e[rp].cot=w;head[x]=rp;
}
int dis[N*3],pre[N*3],epr[N*3],flo[N*3];
bool vis[N*3];
bool spfa(){
    memset(dis,0x3f,sizeof(dis));
    queue<int> q;while(!q.empty())q.pop();
    q.push(s);dis[s]=0;flo[s]=inf;
    while(!q.empty()){
        int x=q.front();q.pop();vis[x]=false;
        for(int i=head[x];i;i=e[i].nxt){
            int y=e[i].to;
            if(dis[y]<=dis[x]+e[i].cot||!e[i].val)continue;
            dis[y]=dis[x]+e[i].cot;
            flo[y]=min(flo[x],e[i].val);
            pre[y]=x;epr[y]=i;
            if(!vis[y])q.push(y),vis[y]=true;
        }
    }
    return dis[t]!=inf;
}
pair<int,int> netflow(){
    int sum=0,cos=0;
    while(spfa()){
        int now=t;
        sum+=flo[t];cos+=dis[t];
        while(now!=s){
            e[epr[now]].val-=flo[t];
            e[epr[now]^1].val+=flo[t];
            now=pre[now];
        }
    }
    return make_pair(sum,cos);
}
int n,m,jz[155][35],w[155][35];
int all,wll,id[155][35],cid;
bool jud(int x,int y){return (x<=n&&x&&y<=m&&y&&jz[x][y]);}
signed main(){
    freopen("roller.in","r",stdin);
    freopen("roller.out","w",stdout);
    n=read();m=read();
    fo(i,1,n)fo(j,1,m)jz[i][j]=read()^1;
    fo(i,1,n)fo(j,1,m)w[i][j]=read();
    fo(i,1,n)fo(j,1,m){
        if(jz[i][j]){
            id[i][j]=++cid;
            cid+=2;wll+=w[i][j];
        }
    }
    fo(i,1,n)fo(j,1,m){
        if(!jz[i][j])continue;
        if(i+j&1){
            all+=2;//cerr<<i<<" "<<j<<endl;
            add_edg(s,id[i][j],2,0);
            add_edg(id[i][j],s,0,0);
            add_edg(id[i][j],id[i][j]+1,1,0);
            add_edg(id[i][j]+1,id[i][j],0,0);
            add_edg(id[i][j],id[i][j]+1,1,w[i][j]);
            add_edg(id[i][j]+1,id[i][j],0,-w[i][j]);
            add_edg(id[i][j],id[i][j]+2,1,0);
            add_edg(id[i][j]+2,id[i][j],0,0);
            add_edg(id[i][j],id[i][j]+2,1,w[i][j]);
            add_edg(id[i][j]+2,id[i][j],0,-w[i][j]);
            if(jud(i+1,j)){
                add_edg(id[i][j]+1,id[i+1][j]+1,1,0);
                add_edg(id[i+1][j]+1,id[i][j]+1,0,0);
            }
            if(jud(i-1,j)){
                add_edg(id[i][j]+1,id[i-1][j]+1,1,0);
                add_edg(id[i-1][j]+1,id[i][j]+1,0,0);
            }
            if(jud(i,j+1)){
                add_edg(id[i][j]+2,id[i][j+1]+2,1,0);
                add_edg(id[i][j+1]+2,id[i][j]+2,0,0);
            }
            if(jud(i,j-1)){
                add_edg(id[i][j]+2,id[i][j-1]+2,1,0);
                add_edg(id[i][j-1]+2,id[i][j]+2,0,0);
            }
        }
        else {
            add_edg(id[i][j],t,2,0);
            add_edg(t,id[i][j],0,0);
            add_edg(id[i][j]+1,id[i][j],1,0);
            add_edg(id[i][j],id[i][j]+1,0,0);
            add_edg(id[i][j]+1,id[i][j],1,w[i][j]);
            add_edg(id[i][j],id[i][j]+1,0,-w[i][j]);
            add_edg(id[i][j]+2,id[i][j],1,0);
            add_edg(id[i][j],id[i][j]+2,0,0);
            add_edg(id[i][j]+2,id[i][j],1,w[i][j]);
            add_edg(id[i][j],id[i][j]+2,0,-w[i][j]);
        }
    }
    pair<int,int> ret=netflow();
    //cerr<<ret.first<<" "<<ret.second<<endl;
    if(ret.first!=all)printf("-1");
    else printf("%d",wll-ret.second);
}

T3 木棍

原来是差分约束!!!

用前缀和记录有多少个左端点

然后差分约束就行了

AC_code
#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*10+ch-'0';ch=getchar();}
    return s*t;
}
const int N=10005;
int n,k,m;
struct E{int to,nxt,val;}e[20000000];
int head[N],rp;
void add_edg(int x,int y,int z){
    // cerr<<x<<" "<<y<<" "<<z<<endl;
    e[++rp].to=y;e[rp].nxt=head[x];
    e[rp].val=z;head[x]=rp;
}
int dis[N],cnt[N];
bool vis[N];
int lsh[N],lh;
struct STI{int a,b;}sti[N];
struct LIM{int a,b,c;}lim[N];
set<int> st;
vector<int> bg[N],ed[N];
struct node{
    int x,ds;
    bool operator < (node a)const{
        return ds>a.ds;
    }
};
priority_queue<node> q;
signed main(){
    freopen("stick.in","r",stdin);
    freopen("stick.out","w",stdout);
    n=read();m=read();k=read();
    fo(i,1,n){sti[i].a=read();sti[i].b=read()+1-k;lsh[++lh]=sti[i].a;lsh[++lh]=sti[i].b;}
    fo(i,1,m){lim[i].a=read();lim[i].b=read()+1-k;lim[i].c=read();lsh[++lh]=lim[i].a;lsh[++lh]=lim[i].b;}
    sort(lsh+1,lsh+lh+1);lh=unique(lsh+1,lsh+lh+1)-lsh-1;
    fo(l,1,lh-1)fo(r,l+1,lh){
        add_edg(l,r,((lsh[r]-lsh[l]-1)/k+1));
    }
    fo(i,1,n){
        int a=lower_bound(lsh+1,lsh+lh+1,sti[i].a)-lsh;
        int b=lower_bound(lsh+1,lsh+lh+1,sti[i].b)-lsh;
        bg[a].push_back(i);ed[b].push_back(i);
    }
    fo(l,1,lh){
        int sum=0;st.clear();
        fo(r,l,lh){
            for(int i:bg[r])st.insert(i);
            for(int i:ed[r])if(st.find(i)!=st.end())sum++,st.erase(i);//cerr<<i<<endl;
            add_edg(r,l,-sum);
        }
    }
    fo(i,1,m){
        int l=lower_bound(lsh+1,lsh+lh+1,lim[i].a)-lsh;
        int r=lower_bound(lsh+1,lsh+lh+1,lim[i].b)-lsh;
        add_edg(l,r,lim[i].c);
    }
    memset(dis,0x3f,sizeof(dis));
    q.push(node{1,0});dis[1]=0;cnt[1]=1;
    while(!q.empty()){
        int x=q.top().x;q.pop();vis[x]=false;
        if(1.0*clock()/CLOCKS_PER_SEC>=0.6){printf("No");return 0;}
        for(int i=head[x];i;i=e[i].nxt){
            int y=e[i].to;
            if(dis[y]<=dis[x]+e[i].val)continue;
            dis[y]=dis[x]+e[i].val;
            if(cnt[y]>lh){printf("No");return 0;}
            if(!vis[y])q.push(node{y,dis[y]}),vis[y]=true;
        }
    }
    printf("Yes");
}
posted @ 2022-03-06 21:33  fengwu2005  阅读(44)  评论(0编辑  收藏  举报