2022杭电多校——1

Dragon slayer

1)开二倍点的解法

需要些优化的搜索

优化1:(学习自正解)

可以在枚举所有状态时,将包含当前状态的所有状态更新

for(int j=0;j<k;j++){
    st[i|(1<<j)]=st[i];//优化1
}

优化2:(同样学习自正解)

在对当前状态进行搜索之前,将未被消除的墙点找出来,而不是一开始把墙建出来再在状态中特判

 for(int i=0;i<k;i++){
        if((f&(1<<i))==0){//当前状态不能消除的墙
            for(int j=p[i].st.x;j<=p[i].ed.x;j++){
                for(int z=p[i].st.y;z<=p[i].ed.y;z++){
                    g[j][z]+=1<<i;
                }
            }
        }
    }
const int N=31,M=(1<<15)+10;
int n,m,k;
int sx,sy,ex,ey;
int g[N][N];
bool vis[N][N],st[M];
struct P{
    PII st,ed;
}p[N];
PII q[N*N];
int dx[]={0,0,1,-1},dy[]={1,-1,0,0};
inline bool bfs(int f){
    for(int i=0;i<=n;i++){
        for(int j=0;j<=m;j++){
            vis[i][j]=0;
            g[i][j]=0;
        }
    }
    for(int i=0;i<k;i++){
        if((f&(1<<i))==0){//当前状态不能消除的墙
            for(int j=p[i].st.x;j<=p[i].ed.x;j++){
                for(int z=p[i].st.y;z<=p[i].ed.y;z++){
                    g[j][z]+=1<<i;
                }
            }
        }
    }
    int tt=-1,hh=0;
    q[++tt]={sx,sy};vis[sx][sy]=1;
    while(hh<=tt){
        int x=q[hh].x,y=q[hh++].y;
        if(x==ex&&y==ey) return true;
        for(int i=0;i<4;i++){
            int a=x+dx[i],b=y+dy[i];
            if(a<0||b<0||a>n||b>m||vis[a][b]) continue;
            if(g[a][b]) continue;
            vis[a][b]=1;
            q[++tt]={a,b};
        }
    }
    return false;
}
inline void solve(){
    cin>>n>>m>>k>>sx>>sy>>ex>>ey;
    n*=2,m*=2;
    sx=sx*2+1,sy=sy*2+1,ex=ex*2+1,ey=ey*2+1;
    for(int i=0;i<k;i++){
        int x1,x2,y1,y2;
        cin>>x1>>y1>>x2>>y2;
        p[i].st.x=min(x1,x2)*2,p[i].st.y=min(y1,y2)*2;
        p[i].ed.x=max(x1,x2)*2,p[i].ed.y=max(y1,y2)*2;
    }
    for(int i=0;i<(1<<k);i++){
        st[i]=0;
    }
    int ans=k;
    for(int i=0;i<(1<<k);i++){
        if(st[i]) continue;
        st[i]=bfs(i);
        if(st[i]){
            int cnt=0,t=i;
            for(int j=0;j<k;j++){
                cnt+=t&1;
                t>>=1;
            }
            ans=min(ans,cnt);
            for(int j=0;j<k;j++){
                st[i|(1<<j)]=st[i];//优化1
            }
        }
    }
    cout<<ans<<endl;
}

2)正解做法

把一个数组中的一个位置当作一个格子——处理偏移量

(x,y)就代表在格子(x,y)-(x+1,y+1)

枚举状态时判断

左右走时是否有竖着的墙,

上下走时是否有横着的墙

3)01bfs(双端队列广搜)

Random

经典概率论知识

在[0,1]之间random可以看作均匀分布,期望就是 a + b   2 a+b\over\ 2  2a+b

最终操作相对于生成n-m个数,所以期望是

E ( ( n − m ) X ) = ( n − m ) E ( X ) = ( n − m ) 2 E((n-m)X)=(n-m)E(X)={(n-m)\over 2} E((nm)X)=(nm)E(X)=2(nm)

Backpack

感谢恒哥的讲解,恒哥牛逼!!!

朴素做法:

f[i][j][k]表示从前i个物品中选,异或和为j,体积为k的方案是否存在

转移方程:

f[i][j][k] = f[i-1][j^w][k-v] | f[i-1][j][k]

先明显三层循环,会超时

循环过程中f[i][j][k]就是由f[i-1][j][k]转移过来的,剩下的就是处理出f[i-1][j^w][k-v]

因为异或和枚举的是[0,1023],所以第二维[j^w]相当于已经知道,只需要求出[k-v]

可以利用bitset压位优化第三维

bitset<N>f[i][j]从前i个物品中选,异或和为j,体积为k的方案是否存在

第k个位置为1就说明方案存在

体积一维表示为一个二进制串,这样[k-v]就可以用f[i]<<=v一次算出来

const int N=1050;
bitset<N>f[N],g[N];
int n,m,k;
void solve(){
    cin>>n>>m;k=1024;
    for(int i=0;i<N;i++){
        f[i].reset();
        g[i].reset();
    }
    f[0][0]=1;
    for(int i=1;i<=n;i++){
        int v,w; cin>>v>>w;
        for(int j=0;j<k;j++){
            g[j]=f[j];
            g[j]<<=v;
        }
        for(int j=0;j<k;j++){
            f[j]|=g[j^w];
        }
    }
    for(int i=k-1;i>=0;i--){
        if(f[i][m]){
            cout<<i<<endl;
            return ;
        }
    }
    cout<<-1<<endl;
}

Ball

同样是bitset的巧妙使用

可以先O(n^2)找出所有的两点之间距离,按照距离递增排序,

当枚举到第i条边时,我们只要找一下与这条边的两个端点ab距离大于i和小于i的所有点的方案,我们可以用bitset优化这个过程

p[i]表示与点i距离小于当前边的点的数量,取(p[a]^p[b]).count()就能得到所有方案。

#include<bits/stdc++.h>
#define endl '\n'
#define x first
#define y second
#define inf 0x3f3f3f3f
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
#define mod (int)1e9+7
#define NO puts("NO")
#define YES puts("YES")
#define No puts("No")
#define Yes puts("Yes")
using namespace std;
typedef pair<int,int>PII;
typedef long long ll;
const int N=2010,M=N*N;
int n,m;
int a[N],b[N];
bitset<N>bset[N];//表示当前枚举了哪些边
struct P{
    int a,b,w;
    bool operator<(const P&x)const{
        return w<x.w;
    }
}p[M];
int prim[M],tot;
bool vis[M];
void prime(int M){
    vis[1]=1;//注意把1判定为非质数
    for(int i=2;i<=M;i++){
        if(!vis[i]) prim[tot++]=i;
        for(int j=0;prim[j]<=M/i;j++){
            vis[i*prim[j]]=1;
            if(i%prim[j]==0) break;
        }
    }
}
int get(int i,int j){
    return abs(a[i]-a[j])+abs(b[i]-b[j]);
}
inline void solve(){
    cin>>n>>m;
    for(int i=0;i<n;i++) bset[i].reset();
    for(int i=0;i<n;i++) cin>>a[i]>>b[i];
    int cnt=0;
    for(int i=0;i<n;i++){
        for(int j=i+1;j<n;j++){
            p[cnt++]={i,j,get(i,j)};
        }
    }
    sort(p,p+cnt);
    int ans=0;
    for(int i=0;i<cnt;i++){
        int a=p[i].a,b=p[i].b,w=p[i].w;
        if(!vis[w]) ans+=(bset[a]^bset[b]).count();
        bset[a][b]=bset[b][a]=1;
    }
    cout<<ans<<endl;
}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
//    freopen("in.txt","r",stdin);
//    freopen("out.txt","w",stdout);
//    mp.reserve(1024);
//    mp.max_load_factor(0.25);
    prime(M);
    int T=1;
    cin>>T;
    while(T--){
        solve();
    }
    return 0;
}

Path

分层图

const int N=1e6+10,M=2e6+10;
int n,m,s,k;
int h[N],e[M],ne[M],w[M],f[M],idx;
bool vis[N][2];
int dist[N][2],ch[N];
struct P{
    int u,cnt,d;
    bool operator<(const P&x)const{
        return d>x.d;
    }
};
priority_queue<P>q;
void init(){
    idx=0;
    for(int i=1;i<=n;i++){
        h[i]=-1;
        dist[i][1]=dist[i][0]=INF;
        vis[i][1]=vis[i][0]=0;
        ch[i]=0;
    }
}
void add(int a,int b,int c,int d){
    w[idx]=c,f[idx]=d,ne[idx]=h[a],e[idx]=b,h[a]=idx++;
}
void djs(){
    set<int>S;
    for(int i=1;i<=n;i++){
        if(i!=s) S.insert(i);
    }
    dist[s][0]=0;
    q.push({s,0,0});
    int color=0;
    while(q.size()){
        P t=q.top();q.pop();
        ++color;
        int u=t.u,cnt=t.cnt,d=t.d;
        if(!cnt) S.erase(u);
        else{
            for(int i=h[u];~i;i=ne[i]){
                int j=e[i];
                ch[j]=color;
            }
            vector<int>de;
            for(auto i:S){
                if(ch[i]!=color){
                    de.push_back(i);
                    dist[i][0]=dist[u][cnt];
                    q.push({i,0,dist[i][0]});
                }
            }
            for(auto i:de) S.erase(i);
        }
        if(cnt) d-=k;
        for(int i=h[u];~i;i=ne[i]){
            int j=e[i];
            if(dist[j][f[i]]>d+w[i]){
                dist[j][f[i]]=d+w[i];
                q.push({j,f[i],dist[j][f[i]]});
            }
        }
    }
}
inline void solve(){
   cin>>n>>m>>s>>k;
   init();
   while(m--){
        int a,b,c,d; cin>>a>>b>>c>>d;
        add(a,b,c,d);
   }
   djs();
   for(int i=1;i<=n;i++){
        cout<<((min(dist[i][0],dist[i][1])==INF)?-1:min(dist[i][0],dist[i][1]))<<' ';
   }
   cout<<endl;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值