$$ \newcommand{\seq}[2]{{#1}_{1},{#1}_{2},\cdots,{#1}_{#2}} \newcommand{\num}[1]{1,2,\cdots,#1} \newcommand{\stra}[2]{\begin{bmatrix}#1 \\ #2\end{bmatrix}} \newcommand{\strb}[2]{\begin{Bmatrix}#1 \\ #2\end{Bmatrix}} \newcommand{\dw}[1]{\underline{#1}} \newcommand{\up}[1]{\overline{#1}} $$

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;
}
posted @ 2019-02-23 16:08  chc_1234567890  阅读(200)  评论(0)    收藏  举报