2015 上海网赛 HDU5469 树分治

/*
*Rainto96
*Beijing University of Posts and Telecommunications School of Software Engineering
*http://blog.csdn.net/u011775691
题意:
10000个节点的一颗树
每个节点有个字母
给出目标字符串
求是否存在点对u,v使得u到v的路径上的字母正好组成这个字符串
解法:
就那么搞,前缀记一下,后缀记一下,然后查查缺的部分有没有
查很有讲究
如果用map存hash值的话,常数太大(自己写的也过不了)很难过
其实可以用bool值存遍历过的状态,是这样的,如果发现当前结点到根的字符串恰好是某个前缀或者某个后缀,则把那里的开关打开
查询的话也是查对应的缺的部分开关有没有打开,于是就从带常数的O(1)变为了朴素的O(1)
这里有一些小细节要注意,多测几组样例就好了
*/
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <cstring>
#include <string>
#include <cmath>
#include <set>
#include <map>
#include <vector>
#include <climits>
using namespace std;
#define pb push_back
#define ALL(x) x.begin(),x.end()
#define VINT vector<int>
#define PII pair<int,int>
#define MP(x,y) make_pair((x),(y))
#define ll long long
#define ull unsigned ll
#define MEM0(x)  memset(x,0,sizeof(x))
#define MEM(x,val) memset((x),val,sizeof(x))
#define scan(x) scanf("%d",&(x))
#define scan2(x,y) scanf("%d%d",&(x),&(y))
#define scan3(x,y,z) scanf("%d%d%d",&(x),&(y),&(z))
#define scan4(x,y,z,k) scanf("%d%d%d%d",&(x),&(y),&(z),&(k))
#define Max(a,b) a=max(a,b)
#define Min(a,b) a=min(a,b)
using namespace std;
const int MAXN = 10111;
int head[MAXN];//
struct Edge{
        int u,v,nxt;
}ed[MAXN*2];
int n;
bool del[MAXN];//


int wt[MAXN];
int newroot;
int maxn=0x3f3f3f3f;
int dfs(int now,int fa,int all,int& newroot , int dep){
        wt[now]=1;
        int mx=0;
        //if(dep>100000) cerr<<"fuck";
        for(int i=head[now];i!=-1;i=ed[i].nxt){
                int to=ed[i].v;
                if(to==fa || del[to]) continue;
                int siz=dfs(to,now,all,newroot,dep+1);
                Max(mx,siz);
                wt[now]+=siz;
        }
        Max(mx,all-wt[now]);
        if(mx < maxn) maxn=mx ,newroot=now;
        return wt[now];
}

int searchit(int root,int all){
        maxn=0x3f3f3f3f;
        dfs(root,-1,all,newroot,1);
        return newroot;
}


int ednum=0;//
bool found=false;//
int Len;//
void init(){
        MEM(head,-1);
        MEM0(del);
        ednum=0;
        found=false;//
}

const ull MAGIC = 33;
ull mi[MAXN];//

char node[MAXN] , S[MAXN];//
ull hsv;

pair<ull,ull > que[MAXN*3];//
int tail;//

bool st1[MAXN],st2[MAXN];//suf , pre
ull pre[MAXN],suf[MAXN];
//set<ull >st1,st2;//
#define P(x) ((x)-'a'+1)
bool gao(int now,int fa,ull hs,ull ant,int len,int root){
        ull here = hs*MAGIC + P(node[now]);
        ull anthere = ant + P(node[now])*mi[len-2];
        if(here == suf[len]) que[tail++] = MP(len , 1 );
        if(anthere == pre[len-1]) que[tail++] = MP(len-1 , 2 );//这里原来是len,2 就错了
        if(here == hsv || (here == suf[len] && st2[Len-len])  || (anthere == pre[len-1] && st1[Len-len+1])  ){
                found=true;
                return true;
        }
        for(int i = head[now];i!=-1;i=ed[i].nxt){
                int to =ed[i].v ;
                if(to == fa || del[to]) continue;
                if(gao(to,now ,here , anthere  , len+1 ,root)) return true;
        }
        return false;
}



PII Sque[22111];
int Shead=0,Stail=0;
void solve(int rt,int sz){
        Shead=0,Stail=0;
        Sque[Stail++] = MP(rt,sz);
        while(Shead<Stail){
                PII now = Sque[Shead++];
                int nr = searchit(now.first , now.second);
                del[nr] =true;
                //cerr<<nr<<endl;
                MEM0(st1);
                MEM0(st2);
                st1[1] = P(node[nr]) == suf[1];//这里是要先开的,根节点的情况,因为可能后缀就只用一个根节点,前缀是不包括根节点的
                if(P(node[nr]) == hsv) found=true;
                for(int i=head[nr];i!=-1;i=ed[i].nxt){
                        int to = ed[i].v;
                        if(del[to]) continue;
                        tail=0;
                        if(P(node[nr]) == hsv ) {
                                found=true;
                                return;
                        }
                        if(gao(to,-1,P(node[nr]),0,2 ,nr)) return;
                        for(int j=0;j<tail;j++) {
                                //st1.insert(que[j].first) , st2.insert(que[j].second);
                                if(que[j].second  == 1 ) st1[que[j].first] = true;
                                else st2[que[j].first] = true;
                        }
                        Sque[Stail++] = MP(to,wt[to]);
                }
        }
}

int main(){
	#ifndef ONLINE_JUDGE
		//freopen("/home/rainto96/in.txt","r",stdin);
	#endif
	mi[0] = 1;
	for(int i= 1;i<MAXN;i++) mi[i] = mi[i-1]*MAGIC;
	int T;scan(T);
	for(int Cas= 1;Cas<=T;Cas++){
                printf("Case #%d: ",Cas);
                scan(n);
                init();
                for(int i=0;i<n-1;i++){
                        int u,v;
                        scan2(u,v);
                        ed[ednum] = (Edge){u,v,head[u]};
                        head[u] = ednum++;
                        ed[ednum] = (Edge){v,u,head[v]};
                        head[v] = ednum++;
                }
                scanf("%s",node+1);
                scanf("%s",S);
                hsv=0;
                Len = strlen(S);
                for(int i=0;i<Len;i++){
                        hsv = hsv*MAGIC + S[i]-'a'+1;
                        pre[i+1] = hsv;
                }
                ull ttt=0;
                for(int i=Len-1 , now=0;i>=0;i--,now++){
                        ttt += mi[now] * (S[i] - 'a' + 1);
                        suf[now+1] = ttt;
                }
                solve(1 ,n );
                if(found) puts("Find");
                else puts("Impossible");
	}
        return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值