hdu4812 D Tree,平衡树,启发式合并

今天模拟2013南京赛,两个半小时做完5题开始梦游。
每次都出不了难题有木有!都是水题的场手速又拼不过有木有!

hdu4812 D Tree,南京的k题。
每个点有一个权值,问是否存在一条路径,路径所有点的权值乘积模1e6+3等于k。


这个题,大家基本都是用点分治过的,复杂度O(nlogn)。
这里介绍一个有趣的解法,也是一个我感觉很有用的思想:启发式合并。

先说说启发式合并。
启发式合并就是对于两个相同的数据结构,将点数小的数据结构的点一个个暴力插入点大的数据结构中。
如此就完成了这两个数据结构的合并。

再来说说这玩意神奇之处。
以平衡树为例,一开始有n个点,每个点有包含本身的点的平衡树。
然后将任意两点的平衡树合并,最后合并成一棵树。
如果每次都遵循小的插到大的这样的合并方式,可以证明,插入的次数不会超过O(nlogn)次。
这里简单证明一下:对于每个点,如果它从小的树拔出来插到了大的树上,那么它所在的树的大小至少会增加一倍。
树最大就是n个点,那么这个点最大的插入次数不会超过 logn 次。



这样,本题就可以用这种方式求解了。
每个叶子节点,建一个包含自己的平衡树。
非叶子节点,从子节点中选一个点数最大的,将它的平衡树作为自己的平衡树,再将其它子节点的平衡树合并到这个平衡树中。
合并的过程查找是否有匹配的值并更新答案。复杂度O(nlognlogn)。
自己生成了一些随机大数据,比点分治要稍慢一些,但是在hdu提交居然要比某些点分治更快,2000+ms。

数的逆元要在开始时预处理,不然会悲剧。
具体看代码吧:


#pragma comment(linker,"/STACK:102400000,102400000")
#include
#include
#include
#include
using namespace std;
#define NN 101000
#define MM 1001000

map mp[NN];
int k,n;
int te,fi[NN],ne[NN*2],v[NN*2];
int ans1,ans2,tm;
int pinv[MM];
int son[NN],tot[NN],mul[NN];
int val[NN];
int tr[NN];

const int inf = 101000000;
const int mod = 1000003;

void extend_gcd(int a,int b,int &x,int &y){
    if (b==0){
        x=1,y=0;return;
    }
    else{
        extend_gcd(b,a%b,x,y);
        int t=x;
        x=y;
        y=t-a/b*y;
        return;
    }
}
void init_inv(){
    int i;
    int x,y;
    pinv[0]=0;
    int tmod=mod;
    for(i=1;ima) {ma=tot[vv];son[u]=vv;}
        }
    }
}

void dfs2(int u,int fa){
    int e,vv;
    int tmp,tinv,a,b;
    if (son[u]==-1){
        tr[u]=++tm;mp[tm][1]=u;mul[u]=val[u];
        return;
    }
    else {
        dfs2(son[u],u);tr[u]=tr[son[u]];
        mul[u]=mul[son[u]];
        tinv=(long long)mul[u]*val[u]%mod;
        tinv=(long long)pinv[tinv]*k%mod;
        if (mp[tr[u]].find(tinv)!=mp[tr[u]].end()){
            a=mp[tr[u]][tinv];
            b=u;
            if (a>b) {int tt=a;a=b;b=tt;}
            if (au)||(mp[tr[u]].find(vv)==mp[tr[u]].end())){
            mp[tr[u]][vv]=u;
        }
    }
    for(e=fi[u];e!=-1;e=ne[e]){
        vv=v[e];
        if (vv!=fa&&vv!=son[u]){
            dfs2(vv,u);
            map::iterator it;
            for(it=mp[tr[vv]].begin();it!=mp[tr[vv]].end();it++){
                tmp=(long long)it->first;
                tmp=(long long)tmp*mul[vv]%mod;
                tinv=(long long)tmp*mul[u]%mod*val[u]%mod;
                tinv=(long long)pinv[tinv]*k%mod;
                if (mp[tr[u]].find(tinv)!=mp[tr[u]].end()){
                    a=mp[tr[u]][tinv];
                    b=it->second;
                    if (a>b) {int tt=a;a=b;b=tt;}
                    if (ait->second)||(mp[tr[u]].find(tmp)==mp[tr[u]].end())){
                    mp[tr[u]][tmp]=it->second;
                }
            }
        }
    }
    mul[u]=(long long)mul[u]*val[u]%mod;
}

int main(){
    //freopen("kin2.txt","r",stdin);
    int i,a,b;
    init_inv();
    while(scanf("%d%d",&n,&k)!=EOF){
        for(i=1;i<=n;++i){
            scanf("%d",&val[i]);
            mp[i].clear();
        }
        te=0;
        memset(fi,-1,sizeof(fi));
        for(i=1;i


你可能感兴趣的:(启发式合并,HDU,数据结构)