SPOJ HIGH Highways莫队算法—基础知识介绍(转载)

生成树的计数 Matrix-Tree定理

莫队算法

莫队算法可用于解决一接近可离线都以获区间[l,r][l,r]的答案后,能当O(1)O(1)或O(log2n)O(log2⁡n)得到区间[l,r+1][l,r+1]或[l−1,r][l−1,r]的答案的问题

先行押这么一个题材:

叫出n个数字,m次询问,每次询问以距离[li,ri][li,ri]次任选两单数字相当于的概率是有点。(n,q<=50000)(小z的袜子)

在区间[l,r][l,r]遭遇,这个概率是:

∑vi=1C(2,f(i))C(2,r−l+1)∑i=1vC(2,f(i))C(2,r−l+1)

(v代表数字值,f(i)表示数字i在距离内冒出的次数)

 

是因为并未加和性质,传统的线条树啊的一心派不齐用了也!

设想分子,因为C(2,x)=x2−x2C(2,x)=x2−x2,所以成员=∑vi=1f(i)2−∑vi=1f(i)2∑i=1vf(i)2−∑i=1vf(i)2
显然 ∑vi=1f(i)=r−l+1∑i=1vf(i)=r−l+1

只要得知区间[l,r][l,r]的答案怎么要区间[l,r+1][l,r+1]的答案也?仔细思考。恩,有矣。区间[l,r+1][l,r+1]与区间[l,r][l,r]对比仅多矣一个元素Z,这种变动是可怜有点之,那么前式中成员的值S=S0−f(Z)2+(f(Z)+1)2−1=S0+2∗f(Z)S=S0−f(Z)2+(f(Z)+1)2−1=S0+2∗f(Z),同时++f(z),恩,O(1)O(1)的。这样的话,在拍卖下一个了解[li,ri][li,ri]经常,复杂度就是O(|r−ri|+|l−li|)O(|r−ri|+|l−li|)的。同样的点子,也得以O(1)O(1)内呼吁出[l−1,r][l−1,r],[l+1,r][l+1,r],[l,r−1][l,r−1]。这样的方对自由数据见是非常好之,但也不难给起故意卡你的数码。

这,就用莫队算法来支撑腰了,这也是莫队算法优化的精华。

注意到,每个区间可以抽象成平面中的点,每次更换的花都相当与由某点到外一些底曼哈顿距离的长度。恩,所以啊?

从而我们花的就是这些面中的接触联通的曼哈顿距离。平面点的曼哈顿无限小生成树!

针对!但平面点的曼哈顿最小生成树怎么告为?枚举两片沾连接O(n2)O(n2),毫无意义。其实平面点的曼哈顿绝小生成树有基于平面区域划分的O(nlog2n)O(nlog2n)的求法,但我们发出再次简洁之措施。对,分块!

诚,利用分块,我们得以实现O(nn√)O(nn)的工夫复杂度。虽然求解平面点的曼哈顿极小生成树是O(nlog2n)O(nlog2n)的,但根据莫队舆论中之辨证,用到此地经常,仍然是O(nn√)O(nn),只不过常数小部分而已。

分块的做法:
取x=(√n)x=(n),以[1,x],[x+1,2x],[2x+1,3x]…[1,x],[x+1,2x],[2x+1,3x]…分块
之所以pos数组维护端点i在第pos[i]片被,然后便搞呗。

这样搞:

1):排序,以错误段点所于的块也第一重中之重字,以右手端点也次主要字

2):从左往右处理询问(离线)

3):不断调整l,r的位置并同时修改

时刻复杂度证明:

右侧端点走:
率先我们考虑一个片里面的变状态
是因为一个片里面的打听还遵循下手端点排序
于是我们右端点在一个片里面最多倒n次
有  class=”MathJax”
data-mathml=”<math xmlns="http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;mi&gt;O&lt;/mi&gt;&lt;mo stretchy="false">(</mo><msqrt><mi>n</mi></msqrt><mo stretchy="false">)</mo></math>”> id=”MathJax-Span-407″ class=”math”> class=”mrow”>O id=”MathJax-Span-410″ class=”mo”>( class=”msqrt”> id=”MathJax-Span-413″ class=”mi”>n√ class=”mo”>) class=”MJX_Assistive_MathML”>O(n)个片,那么同一个片内的右端点移动最多便是 class=”MathJax_Preview”> class=”MathJax”
data-mathml=”<math xmlns="http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;mi&gt;O&lt;/mi&gt;&lt;mo stretchy="false">(</mo><mi>n</mi><msqrt><mi>n</mi></msqrt><mo stretchy="false">)</mo></math>”> id=”MathJax-Span-415″ class=”math”> class=”mrow”>O id=”MathJax-Span-418″ class=”mo”>( class=”mi”>n id=”MathJax-Span-421″ class=”mrow”> class=”mi”>n√ class=”mo”>) class=”MJX_Assistive_MathML”>O(nn)
接下来考虑由一个块及另外一个片导致的右端点变化
最为特别情况,右端点由n到1,那么移动n次
有  class=”MathJax”
data-mathml=”<math xmlns="http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;mi&gt;O&lt;/mi&gt;&lt;mo stretchy="false">(</mo><msqrt><mi>n</mi></msqrt><mo stretchy="false">)</mo></math>”> id=”MathJax-Span-424″ class=”math”> class=”mrow”>O id=”MathJax-Span-427″ class=”mo”>( class=”msqrt”> id=”MathJax-Span-430″ class=”mi”>n√ class=”mo”>) class=”MJX_Assistive_MathML”>O(n)个块
那由一个块及外一个片的轩然大波就会起 class=”MathJax_Preview”> class=”MathJax”
data-mathml=”<math xmlns="http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;mi&gt;O&lt;/mi&gt;&lt;mo stretchy="false">(</mo><msqrt><mi>n</mi></msqrt><mo stretchy="false">)</mo></math>”> id=”MathJax-Span-432″ class=”math”> class=”mrow”>O id=”MathJax-Span-435″ class=”mo”>( class=”msqrt”> id=”MathJax-Span-438″ class=”mi”>n√ class=”mo”>) class=”MJX_Assistive_MathML”>O(n)次……
因此这种右端点移动的次数为是 id=”MathJax-Element-35-Frame” class=”MathJax”
data-mathml=”<math xmlns="http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;mi&gt;O&lt;/mi&gt;&lt;mo stretchy="false">(</mo><mi>n</mi><msqrt><mi>n</mi></msqrt><mo stretchy="false">)</mo></math>”> id=”MathJax-Span-440″ class=”math”> class=”mrow”>O id=”MathJax-Span-443″ class=”mo”>( class=”mi”>n id=”MathJax-Span-446″ class=”mrow”> class=”mi”>n√ class=”mo”>) class=”MJX_Assistive_MathML”>O(nn)次
没别的事件致使右手端点运动了
左端点走:
和一个块里面,由于左端点都当一个尺寸也 class=”MathJax_Preview”> class=”MathJax”
data-mathml=”<math xmlns="http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;mi&gt;O&lt;/mi&gt;&lt;mo stretchy="false">(</mo><msqrt><mi>n</mi></msqrt><mo stretchy="false">)</mo></math>”> id=”MathJax-Span-449″ class=”math”> class=”mrow”>O id=”MathJax-Span-452″ class=”mo”>( class=”msqrt”> id=”MathJax-Span-455″ class=”mi”>n√ class=”mo”>) class=”MJX_Assistive_MathML”>O(n)的距离内
因此在同样片里面走一坏,左端点最多变化 class=”MathJax_Preview”> class=”MathJax”
data-mathml=”<math xmlns="http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;mi&gt;O&lt;/mi&gt;&lt;mo stretchy="false">(</mo><msqrt><mi>n</mi></msqrt><mo stretchy="false">)</mo></math>”> id=”MathJax-Span-457″ class=”math”> class=”mrow”>O id=”MathJax-Span-460″ class=”mo”>( class=”msqrt”> id=”MathJax-Span-463″ class=”mi”>n√ class=”mo”>) class=”MJX_Assistive_MathML”>O(n)
总共有n个询问……
故同样块里面的移位最多n次
那同一个片里面的左端点变化最为多是 id=”MathJax-Element-38-Frame” class=”MathJax”
data-mathml=”<math xmlns="http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;mi&gt;O&lt;/mi&gt;&lt;mo stretchy="false">(</mo><mi>n</mi><msqrt><mi>n</mi></msqrt><mo stretchy="false">)</mo></math>”> id=”MathJax-Span-465″ class=”math”> class=”mrow”>O id=”MathJax-Span-468″ class=”mo”>( class=”mi”>n id=”MathJax-Span-471″ class=”mrow”> class=”mi”>n√ class=”mo”>) class=”MJX_Assistive_MathML”>O(nn)的
考虑超过块
各国由第i个片及第i+1单片,左端点最为充分加上 class=”MathJax_Preview”> class=”MathJax”
data-mathml=”<math xmlns="http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;mi&gt;O&lt;/mi&gt;&lt;mo stretchy="false">(</mo><msqrt><mi>n</mi></msqrt><mo stretchy="false">)</mo></math>”> id=”MathJax-Span-474″ class=”math”> class=”mrow”>O id=”MathJax-Span-477″ class=”mo”>( class=”msqrt”> id=”MathJax-Span-480″ class=”mi”>n√ class=”mo”>) class=”MJX_Assistive_MathML”>O(n)
共计能长 id=”MathJax-Element-40-Frame” class=”MathJax”
data-mathml=”<math xmlns="http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;mi&gt;O&lt;/mi&gt;&lt;mo stretchy="false">(</mo><msqrt><mi>n</mi></msqrt><mo stretchy="false">)</mo></math>”> id=”MathJax-Span-482″ class=”math”> class=”mrow”>O id=”MathJax-Span-485″ class=”mo”>( class=”msqrt”> id=”MathJax-Span-488″ class=”mi”>n√ class=”mo”>) class=”MJX_Assistive_MathML”>O(n)次
之所以跨越块导致的左端点走是 id=”MathJax-Element-41-Frame” class=”MathJax”
data-mathml=”<math xmlns="http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;mi&gt;O&lt;/mi&gt;&lt;mo stretchy="false">(</mo><mi>n</mi><mo stretchy="false">)</mo></math>”> id=”MathJax-Span-490″ class=”math”> class=”mrow”>O id=”MathJax-Span-493″ class=”mo”>( class=”mi”>n class=”mo”>) class=”MJX_Assistive_MathML”>O(n)的
综上,分块做法是 id=”MathJax-Element-42-Frame” class=”MathJax”
data-mathml=”<math xmlns="http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;mi&gt;O&lt;/mi&gt;&lt;mo stretchy="false">(</mo><mi>n</mi><mo>&#x2217;</mo><msqrt><mi>n</mi></msqrt><mo stretchy="false">)</mo></math>”> id=”MathJax-Span-496″ class=”math”> class=”mrow”>O id=”MathJax-Span-499″ class=”mo”>( class=”mi”>n∗ id=”MathJax-Span-502″ class=”msqrt”> class=”mrow”>n√ id=”MathJax-Span-505″
class=”mo”>) class=”MJX_Assistive_MathML”>O(n∗n)。

仿佛之前gerw学长讲了同样通了姥爷说过千篇一律尽,然而并无掌握。本来不打算学的偏两天内相遇了三道这样的写,十分不适。

总结

莫队算法在解决离线区间询问几乎是无敌的。
恩典,几乎要能够离线,用分块的莫队算法都能够取一个好听的的解法。
之所以尽管起不少扩张(解决线段树等数据结构由于要区间加和性而休可知解决之题材),如区间众数,平均数什么的。
恩。棒!

附:
[BZOJ]2038 小Z的袜子 分片 莫队算法

#include <cstdio>
#include <cmath>
#include <algorithm>

using namespace std;

const int maxn = 50000 + 500;
typedef long long LL;

LL gcd(LL a,LL b)
{
    return (b==0)?a:gcd(b,a%b);
}

int pos[maxn];
int col[maxn];
int f[maxn];
int n,m;

struct Query
{
    int l,r,id;
    LL a,b;
    friend bool operator < (const Query &R,const Query &T)
    {
        return pos[R.l]<pos[T.l] || (pos[R.l]==pos[T.l] && R.r<T.r);
    }
    void modify()
    {
        LL k=gcd(a,b);
        a/=k,b/=k;
    }
}Q[maxn];
bool cmp_id(const Query &a,const Query &b)
{
    return a.id<b.id;
}

void init()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i)
        scanf("%d",&col[i]);
    int limit=(int)sqrt((double)n+0.5);
    for(int i=1;i<=n;++i)
        pos[i]=(i-1)/limit+1;//左端点分块
    for(int i=1;i<=m;++i)
    {
        scanf("%d%d",&Q[i].l,&Q[i].r);
        Q[i].id=i;
    }
    sort(Q+1,Q+m+1);
}

void modify(int p,LL &ans,int add)
{
    ans=ans+2*add*f[col[p]]+1;
    f[col[p]]+=add;
}

void solve()
{
    LL ans=0;
    int l=1,r=0;
    for(int i=1;i<=m;++i)
    {
        if(r<Q[i].r)
        {
            for(r=r+1;r<Q[i].r;++r)
                modify(r,ans,1);
            modify(r,ans,1);
        }
        if(Q[i].l<l)
        {
            for(l=l-1;Q[i].l<l;--l)
                modify(l,ans,1);
            modify(l,ans,1);
        }
        if(Q[i].r<r)
            for(;Q[i].r<r;--r)
                modify(r,ans,-1);
        if(l<Q[i].l)
            for(;l<Q[i].l;++l)
                modify(l,ans,-1);
        if(Q[i].l==Q[i].r)
        {
            Q[i].a=0,Q[i].b=1;
            continue;
        }
        Q[i].a=ans-(Q[i].r-Q[i].l+1),Q[i].b=(LL)(Q[i].r-Q[i].l+1)*(Q[i].r-Q[i].l);
        Q[i].modify();
    }
    sort(Q+1,Q+m+1,cmp_id);
    for(int i=1;i<=m;++i)
        printf("%lld/%lld\n",Q[i].a,Q[i].b);
}

int main()
{
    init();
    solve();

    return 0;
}

 

Refrence:
http://foreseeable97.logdown.com/posts/158522-233333

http://ydcydcy1.blog.163.com/blog/static/21608904020134411543898/

http://vawait.com/manhattanmst/

http://blog.csdn.net/huzecong/article/details/8576908

 

定理如下。

style=”font-size: 15px”
data-mathml=”<math xmlns="http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;mn&gt;1.&lt;/mn&gt;&lt;mspace width="1em" /><mi>G</mi></math>”> class=”MJX_Assistive_MathML”>1.G底度数矩阵D[G] 是一个 class=”MathJax_Preview”> class=”MathJax”
data-mathml=”<math xmlns="http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;mi&gt;n&lt;/mi&gt;&lt;mo&gt;&amp;#x2217;&lt;/mo&gt;&lt;mi&gt;n&lt;/mi&gt;&lt;/math&gt;"> class=”MJX_Assistive_MathML”>n∗n的矩阵 当 class=”MathJax_Preview”> class=”MathJax”
data-mathml=”<math xmlns="http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;mo&gt;&amp;#x2260;&lt;/mo&gt;&lt;mi&gt;j&lt;/mi&gt;&lt;/math&gt;"> class=”MJX_Assistive_MathML”>i≠j时, id=”MathJax-Element-39-Frame” class=”MathJax”
data-mathml=”<math xmlns="http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;msub&gt;&lt;mi&gt;d&lt;/mi&gt;&lt;mrow class="MJX-TeXAtom-ORD"><mi>i</mi><mi>j</mi></mrow></msub><mo>=</mo><mn>0</mn></math>”> class=”MJX_Assistive_MathML”>dij=0;当 class=”MathJax_Preview”> class=”MathJax”
data-mathml=”<math xmlns="http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mi&gt;j&lt;/mi&gt;&lt;/math&gt;"> class=”MJX_Assistive_MathML”>i=j时, class=”MathJax_Preview”> class=”MathJax”
data-mathml=”<math xmlns="http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;msub&gt;&lt;mi&gt;d&lt;/mi&gt;&lt;mrow class="MJX-TeXAtom-ORD"><mi>i</mi><mi>j</mi></mrow></msub></math>”> class=”MJX_Assistive_MathML”>dij等于 class=”MathJax_Preview”> class=”MathJax”
data-mathml=”<math xmlns="http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;/math&gt;"> class=”MJX_Assistive_MathML”>i的度数;
class=”MathJax”
data-mathml=”<math xmlns="http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;mn&gt;2.&lt;/mn&gt;&lt;mspace width="1em" /><mi>G</mi></math>”> class=”MJX_Assistive_MathML”>2.G的邻接矩阵A[G];

style=”font-size: 15px”>我们定义G的Kirchhoff矩阵(也叫做拉普拉斯算子), class=”MathJax_Preview”> class=”MathJax”
data-mathml=”<math xmlns="http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;mi&gt;C&lt;/mi&gt;&lt;mo stretchy="false">[</mo><mi>G</mi><mo stretchy="false">]</mo><mo>=</mo><mi>D</mi><mo stretchy="false">[</mo><mi>G</mi><mo stretchy="false">]</mo><mo>&#x2212;</mo><mi>A</mi><mo stretchy="false">[</mo><mi>G</mi><mo stretchy="false">]</mo></math>”> class=”MJX_Assistive_MathML”>C[G]=D[G]−A[G],则Matrix-Tree定理可以描述为:

style=”font-size: 15px”>G的拥有不同的生成树的个数等其Kirchhoff矩阵 class=”MathJax_Preview”> class=”MathJax”
data-mathml=”<math xmlns="http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;mi&gt;C&lt;/mi&gt;&lt;mo stretchy="false">[</mo><mi>G</mi><mo stretchy="false">]</mo></math>”> class=”MJX_Assistive_MathML”>[G]其余一个 class=”MathJax_Preview”> class=”MathJax”
data-mathml=”<math xmlns="http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;mi&gt;n&lt;/mi&gt;&lt;mo&gt;&amp;#x2212;&lt;/mo&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;/math&gt;"> class=”MJX_Assistive_MathML”>n−1号主子式的行列式的绝对值。

 

莫见面证明。不思套。留个坑。可能为尚未来填的时了。

足足学会了怎么算行列式的价值。。

认真地看自身的代码写的老大优美。

图片 1图片 2

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
typedef long long LL;
const int maxn=100;
using namespace std;
double a[maxn][maxn];
int T,g[maxn][maxn],x,y,n,m;
bool zero(double x) {return (x>0?x:-x)<1e-15;}
double solve() {
    double res=1;    
    for(int i=1;i<n;i++) {
        int now=i;
        for(int j=i+1;j<n;j++) 
            if(a[j][i]>a[now][i]) 
                now=j;
        if(now!=i) 
            for(int j=i;j<n;j++) 
                swap(a[i][j],a[now][j]);
        if(zero(a[i][i])) return 0;
        res*=a[i][i];
        for(int j=i+1;j<n;j++) a[i][j]/=a[i][i];
        a[i][i]=1;
        for(int j=i+1;j<n;j++)
            for(int k=i+1;k<n;k++)
                a[j][k]-=a[j][i]*a[i][k];
    }
    if(res<0) res=-res;
    return res;
}
int main()
{
    scanf("%d",&T);
    while(T--) {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) a[i][j]=g[i][j]=0;
        for(int i=1;i<=m;i++) {
            scanf("%d%d",&x,&y);
            g[x][y]=g[y][x]=1;
            a[x][x]++; a[y][y]++;
        }
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                a[i][j]-=g[i][j];
        printf("%.0lf\n",solve());
    }
    return 0;
}

View Code

 

相关文章

admin

网站地图xml地图