2017北京国庆刷题Day6 afternoon

期望得分:100+100+40=240

实际得分:100+0+40=140

 

二进制拆分、二进制前缀和

#include<cstdio>
#include<iostream>
using namespace std;
typedef long long LL;
#define N 100001
int a[N],b[N],c[N];
const int mod=1e9+7;
int suma[N][32],sumb[N][32];
int bit[31];
void read(int &x)
{
    x=0; char c=getchar();
    while(!isdigit(c)) c=getchar();
    while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); }
}
int finda(int r,int wh)
{
    long long tot=0;
    for(int i=0;i<=30;i++)
        if(wh&bit[i]) tot=(tot+1ll*(r-suma[r][i])*bit[i])%mod;
        else tot=(tot+1ll*suma[r][i]*bit[i])%mod;
    return int(tot%mod);
}
int findb(int r,int wh)
{
    long long tot=0;
    for(int i=0;i<=30;i++)
        if(wh&bit[i]) tot=(tot+1ll*(r-sumb[r][i])*bit[i])%mod;
        else tot=(tot+1ll*sumb[r][i]*bit[i])%mod;
    return int(tot%mod);
}
int main()
{
    freopen("xorarray.in","r",stdin);
    freopen("xorarray.out","w",stdout);
    int n;
    scanf("%d",&n);
    int len,x;
    for(int i=1;i<=n;i++) 
    {
        read(a[i]); 
        len=0; x=a[i]; 
        while(x) suma[i][len++]=suma[i-1][len]+(x&1),x/=2;
        for(int j=len;j<=30;j++) suma[i][j]=suma[i-1][j];
    }
    for(int i=1;i<=n;i++) 
    {
        read(b[i]); 
        len=0; x=b[i]; 
        while(x) sumb[i][len++]=sumb[i-1][len]+(x&1),x/=2;
        for(int j=len;j<=30;j++) sumb[i][j]=sumb[i-1][j];
    }
    bit[0]=1;
    for(int i=1;i<=30;i++) bit[i]=bit[i-1]<<1;
    c[1]=a[1]^b[1];
    for(int k=2;k<=n;k++)
    {
        c[k]=c[k-1];
        c[k]=(c[k]+finda(k-1,b[k]))%mod;
        c[k]=(c[k]+findb(k,a[k]))%mod;
    }
    for(int i=1;i<=n;i++) printf("%d ",c[i]);
}
View Code

60分做法:

先做一遍最小生成树

枚举两个点i,j,那么替换掉的是最小生成树上i,j路径上权值最大的边

倍增维护

时间复杂度:O(n*n*logn)

100分做法:

换一个角度,考虑被替换掉的边的贡献

边e要想被替换掉,那么点 i,j 要满足两个条件:

① 设e的两端点为u,v,i∈u,j∈v

② e的边权是连通 i,j 必经之路(最短路)上边权最大的边

怎么找这条边?——Kruscal算法

Kruscal算法每次找还没有加进去的权值最小的边,所以满足必经之路

还没有加进去的边权最小的边,是已加进去的边权最大的边,所以满足路径上边权最大

所以,设最小生成树的总权值为sum,设当前边权为w,当前边连接的两点的集合大小分别为 s1、s2

ans=(sum*n*(n-1)- 2*Σ s1*s2*w)/(n*(n-1))

n*(n-1):任意选两个点共有这些选法

Σ 前乘2:u,v 和 v,u 算不同的方案

#include<cstdio>
#include<algorithm>
#define N 20001
#define M 100001
using namespace std;
typedef long long LL;
int fa[N],siz[N];
struct node
{
    int u,v,w;
}e[M];
bool cmp(node p,node q)
{
    return p.w<q.w;
}
int find(int i) { return fa[i]==i ? i : fa[i]=find(fa[i]); }
int main()
{
    freopen("detective.in","r",stdin);
    freopen("detective.out","w",stdout);
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++) scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);
    sort(e+1,e+m+1,cmp);
    for(int i=1;i<=n;i++) fa[i]=i,siz[i]=1;
    int u,v;
    LL sum=0,cnt=0;
    for(int i=1;i<=m;i++)
    {
        u=find(e[i].u); v=find(e[i].v);
        if(u==v) continue;
        sum+=e[i].w;
        cnt+=1ll*siz[u]*siz[v]*e[i].w;
        fa[v]=u; siz[u]+=siz[v];
    }
    printf("%.2lf",sum-cnt*2.0/(n*(n-1)));
}
View Code

考场上替换掉的不是 i,j 路径上权值最大的边,替换的是与i,j相连的边权最大的边,0分。

连接i,j 之后,要满足还是一颗树,去掉i,j之间的任意一条边即可

分治+DP+bitset

若点(x1,y1)能访问到点(x2,y2)

假设x1<=x2,y1<=y2

那么(x1,y1)往右下扩展,(x2,y2)往左上扩展,两个点一定在中间某一行相遇

所以可以利用分治的思想枚举相遇的那一行

如果x1和x2都在枚举的这一行的上面,那就扔到左边

如果x1和x2都在枚举的这一行的下面,那就扔到右边

左边和右边的分别继续分治下去

如果x1和x2一个在上面,一个在下面,那就判断这两个点能否在这一行相遇

能在这一行相遇,两个点一定能相遇

反之,一定不能相遇

如何判断 ?

假设当前枚举的相遇行为mid,

f[i][j][k]=0/1表示第i行第j列的点,向右下扩展,能否扩展到第mid行的第k列

g[i][j][k]=0/1表示第i行第j列的点,向左上扩展,能否扩展到第mid行的第k列

若 f[x1][y1][k] 和 g[i][j][k] 有一个k同时为true,那么两个点就可以相遇

第三维可以用bitset简化

f的转移:

预处理:f[mid][j][j]=第mid行第j列是否有障碍

倒叙枚举mid行之上的i,j,f[i][j]|=f[i][j+1],f[i][j]|=f[i+1][j]

g的转移类似,

正序枚举mid行之下的,加号改成减号即可

#include<cstdio>
#include<vector>
#include<bitset>
#define N 501
#define M 1000001
using namespace std;
int n,m;
bool mp[N][N],ans[M];
char s[N];
bitset<N>f[N][N],g[N][N];
struct node
{
    int sx,sy,tx,ty,id;
};
node p;
void solve(vector<node>v,int l,int r)
{
    if(l>r) return;
    int mid=l+r>>1;
    for(int i=mid;i>=l;i--)
        for(int j=m;j;j--)
        {
            f[i][j]=0;
            if(!mp[i][j]) continue;
            if(i==mid) f[i][j].set(j);
            else f[i][j]|=f[i+1][j];
            if(j!=m) f[i][j]|=f[i][j+1];
        }
    for(int i=mid;i<=r;i++)
        for(int j=1;j<=m;j++)
        {
            g[i][j]=0;
            if(!mp[i][j]) continue;
            if(i==mid) g[i][j].set(j);
            else g[i][j]|=g[i-1][j];
            if(j!=1) g[i][j]|=g[i][j-1];
        }
    vector<node>vl,vr;
    for(vector<node>::iterator it=v.begin();it!=v.end();it++)
    {
        p=*it;
        if(p.tx<mid) vl.push_back(p);
        else if(p.sx>mid) vr.push_back(p);
        else ans[p.id]=(f[p.sx][p.sy]&g[p.tx][p.ty]).any();
    }
    solve(vl,l,mid-1);
    solve(vr,mid+1,r);
}
int main()
{
    freopen("boardgame.in","r",stdin);
    freopen("boardgame.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%s",s+1);
        for(int j=1;j<=m;j++) mp[i][j]= s[j]=='.' ? true : false;
    }
    int Q;
    scanf("%d",&Q);
    vector<node>v;
    for(int i=1;i<=Q;i++)
    {
        scanf("%d%d%d%d",&p.sx,&p.sy,&p.tx,&p.ty);
        p.id=i;
        v.push_back(p);
    }
    solve(v,1,n);
    for(int i=1;i<=Q;i++) ans[i] ? puts("Yes") : puts("No");
    return 0;
}
View Code

考场40分双向宽搜,然而跟普通的bfs一个样

#include<cstdio>
#include<iostream>
using namespace std;
#define N 501
bool mp[N][N];
char s[N];
int n,m,rd[N][N][2];
int sx,sy,tx,ty;
int vis[N][N];
int q[N*N],h,t,cnt,bl[N][N];
void read(int &x)
{
    x=0; char c=getchar();
    while(!isdigit(c)) c=getchar();
    while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); }
}
int turn(int i,int j)
{
    return (i-1)*m+j;
}
bool bfs()
{
    cnt++; h=0,t=1;
    q[++h]=turn(sx,sy);
    int now,x,y;
    vis[sx][sy]=cnt;
    vis[tx][ty]=cnt;
    q[++t]=turn(tx,ty);
    bl[sx][sy]=1; bl[tx][ty]=2;
    while(h<=t)
    {
        now=q[h++];
        x=(now-1)/m+1; y=now-(x-1)*m;
        if(bl[x][y]==1)
        {
            if(mp[x+1][y])
            {
                if(bl[x+1][y]==2 && vis[x+1][y]==cnt) return true;
                if(vis[x+1][y]!=cnt) vis[x+1][y]=cnt,q[++t]=turn(x+1,y),bl[x+1][y]=1;
            }
            if(mp[x][y+1])
            {
                if(bl[x][y+1]==2 && vis[x][y+1]==cnt) return true;
                if(vis[x][y+1]!=cnt) vis[x][y+1]=cnt,q[++t]=turn(x,y+1),bl[x][y+1]=1;
            }
        }
        else
        {
            if(mp[x-1][y])
            {
                if(bl[x-1][y]==1 && vis[x-1][y]==cnt) return true;
                if(vis[x-1][y]!=cnt) vis[x-1][y]=cnt,q[++t]=turn(x-1,y),bl[x-1][y]=2;
            }
            if(mp[x][y-1])
            {
                if(bl[x][y-1]==1 && vis[x][y-1]==cnt) return true;
                if(vis[x][y-1]!=cnt) vis[x][y-1]=cnt,q[++t]=turn(x,y-1),bl[x][y-1]=2;
            }
        }
    }
    return false;
}
int main()
{
    freopen("boardgame.in","r",stdin);
    freopen("boardgame.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%s",s+1);
        for(int j=1;j<=m;j++) mp[i][j]=s[j]=='.' ? true : false;
    }
    int q;
    scanf("%d",&q);
    while(q--)
    {
        read(sx); read(sy); read(tx); read(ty);
        if(!mp[sx][sy] || !mp[tx][ty]) { puts("No"); continue; }
        if(sx==tx && sy==ty) { puts("Yes"); continue; }
        if(tx<=sx && ty<=sy) { puts("No"); continue; }
        if(bfs()) puts("Yes");
        else puts("No");
    }
}
View Code
原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/7643157.html