Codeforces 342

342 D

题意

给你一个 \(3×n\) 的表格,要求放满 \(1×2\) 的多米诺骨牌,有些位置.可以放骨牌,有些位置X不能放骨牌。有一个位置O旁边至少有一个多米诺骨牌能够移动到这里(骨牌的宽对着这个格子),问有多少种方法。膜 \(10^9+7\)
\((3\le n\le 10^4)\)

Examples

Input

5
....X
.O...
...X.

Output

1

Input

5
.....
.O...
.....

Output

2

Input

3
...
...
..O

Output

4

状压dp。
对于每一列压一个状态(总共8种情况)。
关键在于怎么处理O
方法是,暴力枚举O旁边哪些骨牌能够移到它,总共16-1=15种情况。
然后想到这15种情况中有交集,然后容斥。
具体:设 \(f(...)\) 表示哪几个方向上的骨牌能够移到它,则答案为 \(f(1)+f(2)+f(3)+f(4)-f(1,2)-f(1,3)-f(1,4)-f(2,3)-f(2,4)-f(3,4)+f(1,2,3)+f(1,2,4)+f(1,3,4)+f(2,3,4)-f(1,2,3,4)\)

Code

#include<bits/stdc++.h>
#define maxn 10003
#define INF 1050000000
#define mod 1000000007
using namespace std;//dp[i][j]:在第i列mask中的行被覆盖,并且前i−1列被完全覆盖的放置种数
int dp[maxn][8],n,no[maxn],X,Y,dx[4]={1,0,-1,0},dy[4]={0,1,0,-1};
char s[3][maxn],t[3][maxn];
void PE(int& x,int y){if((x+=y)>=mod)x%=mod;}
void ME(int& x,int y){if((x+=mod-y)>=mod)x%=mod;}
int Count(int x){int ret=0;while(x){if(x&1)ret++;x>>=1;}return ret;}
int solve(){
    for(int i=1;i<=n;i++){
        no[i]=0;
        for(int j=0;j<3;j++){
            if(t[j][i]=='X'){
                no[i]|=(1<<j);
            }
        }
    }
    for(int i=0;i<=n;i++)for(int j=0;j<8;j++)dp[i][j]=0;
    dp[0][7]=1;
    for(int i=1;i<=n;i++){
        for(int j=0;j<8;j++){
            if(j&no[i])continue;
            dp[i][j|no[i]]=dp[i-1][7-j];
            if(j==3||j==6){
                PE(dp[i][j|no[i]],dp[i-1][7]);
            }
            if(j==7){
                PE(dp[i][j|no[i]],dp[i-1][3]);
                PE(dp[i][j|no[i]],dp[i-1][6]);
            }
        }
    }
    return dp[n][7];
}
int main(){
    scanf("%d",&n);
    for(int i=0;i<3;i++){
        scanf("%s",s[i]+1);
        for(int j=1;j<=n;j++){
            if(s[i][j]=='O')X=i,Y=j;
        }
    }
    int ans=0;
    for(int i=1;i<16;i++){
        for(int j=0;j<3;j++)for(int k=1;k<=n;k++)t[j][k]=s[j][k];
        t[X][Y]='X';
        bool ok=1;
        for(int j=0;j<4;j++){
            if(i&(1<<j)){
                int xx=X+dx[j],xxx=xx+dx[j],yy=Y+dy[j],yyy=yy+dy[j];
                if(xxx<0||xxx>2||yyy<1||yyy>n||s[xx][yy]=='X'||s[xxx][yyy]=='X'){ok=0;break;}
                t[xx][yy]=t[xxx][yyy]='X';
            }
        }
        if(!ok)continue;
        int tmp=solve();
        if(Count(i)&1)PE(ans,tmp);
        else ME(ans,tmp);
    }
    printf("%d\n",ans);
    return 0;
}

342 E

题意

一棵树,初始时 \(1\) 号节点为红色,其他节点为蓝色,有 \(m\) 次操作:

  1. 把一个蓝色节点涂成红色;
  2. 询问一个节点到最近的红色节点的距离。

\((2\le n\le 10^5)\)

Examples

Input
5 4
1 2
2 3
2 4
4 5
2 1
2 5
1 2
2 5
Output
0
3
2

解 1

当红色点个数 \(\le sqrt{n}\) 时暴力查询, \(>\) 时使用一个玄学的bfs修改。
\(O(n\sqrt{n}\log n),2121ms\)

Code 1

#include<bits/stdc++.h>
#define maxn 100003
#define INF 1050000000
using namespace std;
struct edge{
    int to,next;
}e[maxn<<1];
int head[maxn],cnte;
void add(int u,int v){
    e[++cnte].to=v;
    e[cnte].next=head[u];
    head[u]=cnte;
}
int n,lg[maxn],dep[maxn],fa[maxn][23],dis[maxn],a[maxn],cnt,q[maxn],*qhead,*qtail;
void init(int u,int last){
    fa[u][0]=last;
    for(int i=1;(1<<i)<=dep[u];i++){
        fa[u][i]=fa[fa[u][i-1]][i-1];
    }
    for(int i=head[u];i;i=e[i].next){
        int v=e[i].to;
        if(v==last)continue;
        dep[v]=dep[u]+1;
        init(v,u);
    }
}
int lca(int x,int y){
    if(dep[x]<dep[y])swap(x,y);
    while(dep[x]>dep[y]){
        x=fa[x][lg[dep[x]-dep[y]]];
    }
    if(x==y)return x;
    for(int i=lg[dep[x]];i>=0;i--){
        if(fa[x][i]!=fa[y][i]){
            x=fa[x][i];
            y=fa[y][i];
        }
    }
    return fa[x][0];
}
void bfs(){
    qhead=qtail=q;
    for(int i=1;i<=cnt;i++)*qtail++=a[i],dis[a[i]]=0;
    while(qhead!=qtail){
        int u=*qhead++;
        for(int i=head[u];i;i=e[i].next){
            int v=e[i].to;
            if(dis[u]+1<dis[v]){
                dis[v]=dis[u]+1;
                *qtail++=v;
            }
        }
    }
}
int main(){
    int Q;
    scanf("%d%d",&n,&Q);
    for(int i=1;i<n;i++){
        int u,v;
        scanf("%d%d",&u,&v);
        add(u,v);
        add(v,u);
    }
    for(int i=2;i<maxn;i++)lg[i]=lg[i-1]+((1<<(lg[i-1]+1))==i);
    init(1,0);
    for(int i=1;i<=n;i++)dis[i]=INF;
    a[++cnt]=1;
    while(Q--){
        int mo,x;
        scanf("%d%d",&mo,&x);
        if(mo==1){
            a[++cnt]=x;
            if(cnt*cnt>n){
                bfs();
                cnt=0;
            }
        }
        else{
            int ans=dis[x];
            for(int i=1;i<=cnt;i++){
                ans=min(ans,dep[x]+dep[a[i]]-(dep[lca(x,a[i])]<<1));
            }
            printf("%d\n",ans);
        }
    }
    return 0;
}

解 2

\(\log\) 的lca查询改为在欧拉序上用ST表 \(O(1)\) 查询。
\(O(n\sqrt{n}),312ms\)

Code 2

#include<bits/stdc++.h>
#define maxn 100003
#define INF 1050000000
using namespace std;
struct edge{
    int to,next;
}e[maxn<<1];
int head[maxn],cnte;
void add(int u,int v){
    e[++cnte].to=v;
    e[cnte].next=head[u];
    head[u]=cnte;
}
int n,dep[maxn],lg[maxn<<1],st[maxn<<1][23],dfn[maxn],cntdfn,dis[maxn],a[maxn],cnt,q[maxn],*qhead,*qtail;
void init(int u,int last){
    st[++cntdfn][0]=u;
    dfn[u]=cntdfn;
    for(int i=head[u];i;i=e[i].next){
        int v=e[i].to;
        if(v==last)continue;
        dep[v]=dep[u]+1;
        init(v,u);
        st[++cntdfn][0]=u;
    }
}
int work(int x,int y){return dep[x]<dep[y]?x:y;}
void initst(){
    for(int len=1;(1<<len)<=cntdfn;len++){
        for(int i=1;i+(1<<len)-1<=cntdfn;i++){
            st[i][len]=work(st[i][len-1],st[i+(1<<(len-1))][len-1]);
        }
    }
}
int lca(int x,int y){
    if(dfn[x]>dfn[y])swap(x,y);
    int tmp=lg[dfn[y]-dfn[x]+1];
    return work(st[dfn[x]][tmp],st[dfn[y]-(1<<tmp)+1][tmp]);
}
void bfs(){
    qhead=qtail=q;
    for(int i=1;i<=cnt;i++)*qtail++=a[i],dis[a[i]]=0;
    while(qhead!=qtail){
        int u=*qhead++;
        for(int i=head[u];i;i=e[i].next){
            int v=e[i].to;
            if(dis[u]+1<dis[v]){
                dis[v]=dis[u]+1;
                *qtail++=v;
            }
        }
    }
}
int main(){
    int Q;
    scanf("%d%d",&n,&Q);
    for(int i=1;i<n;i++){
        int u,v;
        scanf("%d%d",&u,&v);
        add(u,v);
        add(v,u);
    }
    for(int i=1;i<=n;i++)dis[i]=INF;
    init(1,0);
    for(int i=2;i<(maxn<<1);i++)lg[i]=lg[i-1]+((1<<(lg[i-1]+1))==i);
    initst();
    a[++cnt]=1;
    while(Q--){
        int mo,x;
        scanf("%d%d",&mo,&x);
        if(mo==1){
            a[++cnt]=x;
            if(cnt*cnt>n){
                bfs();
                cnt=0;
            }
        }
        else{
            int ans=dis[x];
            for(int i=1;i<=cnt;i++){
                ans=min(ans,dep[x]+dep[a[i]]-(dep[lca(x,a[i])]<<1));
            }
            printf("%d\n",ans);
        }
    }
    return 0;
}

解 3

https://blog.csdn.net/acmmmm/article/details/17270855
树链剖分,开的线段树上每个节点维护两个值:最近红色节点到链顶端的距离 \(U\) ;最近红色节点到链底端的距离 \(D\)
询问时每次跳到一个节点,就在线段树里询问链顶端到当前节点的 \(D\) 值和链底端到当前节点的 \(U\) 值。

Code 3

#include<bits/stdc++.h>
#define maxn 100003
#define INF 1050000000
using namespace std;
struct edge{
    int from,to,next;
}e[maxn<<1];
int head[maxn],cnte;
void add(int u,int v){
    e[++cnte].to=v;
    e[cnte].from=u;
    e[cnte].next=head[u];
    head[u]=cnte;
}
int n,fa[maxn],son[maxn],dep[maxn],sz[maxn],dfn[maxn],num[maxn],rig[maxn],cntdfn,
top[maxn],cntli[maxn],numli[maxn],dis[maxn];

namespace SEG{
    struct node{
        int disup,disdown;
        node():disup(INF),disdown(INF){}
    }t[maxn<<2];
    void pushup(node& t1,const node& t2,const node& t3){
        t1.disup=min(t2.disup,t3.disup);
        t1.disdown=min(t2.disdown,t3.disdown);
    }
    void change(int p,int l,int r,int pos,int u){
        if(l==r){
            t[p].disup=dis[u]+numli[u];
            t[p].disdown=dis[u]+cntli[top[u]]-numli[u]-1;
            return;
        }
        int mid=(l+r)>>1;
        if(pos<=mid)change(p<<1,l,mid,pos,u);
        else change(p<<1|1,mid+1,r,pos,u);
        pushup(t[p],t[p<<1],t[p<<1|1]);
    }
    int query(int p,int l,int r,int seg_l,int seg_r,bool up){
        if(seg_l<=l&&r<=seg_r){
            return up?t[p].disup:t[p].disdown;
        }
        int mid=(l+r)>>1,ret=INF;
        if(seg_l<=mid)ret=min(ret,query(p<<1,l,mid,seg_l,seg_r,up));
        if(seg_r>mid)ret=min(ret,query(p<<1|1,mid+1,r,seg_l,seg_r,up));
        return ret;
    }
}

void initdep(int u,int last){
    fa[u]=last;
    sz[u]=1;
    int mxsz=0;
    for(int i=head[u];i;i=e[i].next){
        int v=e[i].to;
        if(v==fa[u])continue;
        dep[v]=dep[u]+1;
        initdep(v,u);
        sz[u]+=sz[v];
        if(sz[v]>mxsz)mxsz=sz[v],son[u]=v;
    }
}
void initdfn(int u,int tp){
    dfn[u]=++cntdfn;
    num[cntdfn]=u;
    top[u]=tp;
    numli[u]=cntli[tp];
    cntli[tp]++;
    if(son[u]){
        initdfn(son[u],tp);
        for(int i=head[u];i;i=e[i].next){
            int v=e[i].to;
            if(v==fa[u]||v==son[u])continue;
            initdfn(v,v);
        }
    }
    rig[u]=cntdfn;
}
void change(int u){
    for(int v=u;v;v=fa[top[v]]){
        if(dep[u]-dep[v]<dis[v]){
            dis[v]=dep[u]-dep[v];
            SEG::change(1,1,n,dfn[v],v);
        }
        else break;
    }
}
int query(int u){
    int ret=INF;
    for(int v=u;v;v=fa[top[v]]){
        ret=min(ret,min(SEG::query(1,1,n,dfn[top[v]],dfn[top[v]]+numli[v],0)-(cntli[top[v]]-numli[v]-1),
        SEG::query(1,1,n,dfn[top[v]]+numli[v],dfn[top[v]]+cntli[top[v]]-1,1)-numli[v])+dep[u]-dep[v]);
    }
    return ret;
}
void init(){
    initdep(1,0);
    initdfn(1,1);
}

int main(){
    int Q;
    scanf("%d%d",&n,&Q);
    for(int i=1;i<n;i++){
        int u,v;
        scanf("%d%d",&u,&v);
        add(u,v);
        add(v,u);
    }
    init();
    for(int i=1;i<=n;i++)dis[i]=INF;
    change(1);
    while(Q--){
        int mo,x;
        scanf("%d%d",&mo,&x);
        if(mo==1){
            change(x);
        }
        else{
            printf("%d\n",query(x));
        }
    }
    return 0;
}

转载于:https://www.cnblogs.com/BlogOfchc1234567890/p/10423021.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值