zoukankan      html  css  js  c++  java
  • 完美子图

    完美子图 (分治 (starstarstar ))

    Descrption

    • (Q) 和小 (P) 都非常喜欢做一些有趣的题目,他们经常互相出一些题目来考对方。
    • 一天,小 (Q) 给小 (P) 出了这样一道题目:给出一个 (n imes n) 的网格图,在网格中放置 (n) 个点,(不会有两个点放置在同一个网格中)。如果一个 (m imes m(1≤m≤n)) 的子网格图恰好包含 (m) 个点,则称这样的子网格图为完美子网格图。
    • 现在小 (Q) 问小 (P),对于给定的网格图存在多少个完美子网格图。小 (P) 这回被难住了,他请你来帮忙,你能帮他解决这个问题吗?

    Input

    • 第一行,一个正整数 (n),表示网格图的大小以及点的数量。
    • 接下来 (n) 行,每行两个整数, (x_i,y_i),表示第 (i) 个点的坐标。
    • 保证每一行每一列只有一个点

    Output

    • 一行,完美子网格图的数量。

    Sample Input

    5
    1 1
    3 2
    2 4
    5 5
    4 3
    

    Sample Output

    10
    

    Hint

    • 显然,分别以 ((2,2))((4,4)) 为左上,右下顶点的一个子网格图中有 (3) 个点,我们找到了一个完美子网格图,类似的完美子网格图在原图中能找出 (10) 个。
    • 对于 (30\%) 的数据,(n ≤ 100);
    • 对于 (60\%) 的数据,(n ≤ 5000) ;
    • 对于 (100\%) 的数据,(n ≤ 50000) ;
    • 来源:(Codeforces 526F)

    分析

    • 每行每列只有一个,我们很快就能想到我们做状压 (dp) 第一题《特殊方格棋盘》,很容易用一维表示放置状态。
    • 这样我们就得到了一个 (1sim n) 组成的一个序列。问题就可以简化为:给定了 (n) 个数的一个排列,问这个序列中有多少个子区间的数恰好的连续的。相当于统计有多少个子区间 ([l,r]) 满足 (Max-Min=r-l)(Max,Min) 分别为区间 $ [l,r]$ 的最大、最小值。
    • 对这个问题,我们可以用分治来解决,我们把区间分成两部分即:([l,mid],[mid+1,r] (mid=frac{l+r}{2})) ,这样我们只需要考虑跨过 (mid) 的区间,不跨的则是递归的子问题。
      1. 预处理去区间的最大最小值:
      • (Min[i],Max[i]):当 (lle ile mid) 时表示 (isim mid) 的最小值和最大值。
      • (Mix[i],Max[i]):当 (mid+1le i le r) 时表示 (mid+1sim i) 的最小、最大值。
      1. 如果单个的点正好是一个 (1 imes 1) 的完美子图。在两个区间合并的时候,跨过 (mid) 的区间的最值问题分成下面四种情况:
        1. 最大最小值均在左边,此时我们枚举区间的左端点 (i) ,则右端点 (j=i+Max[i]-Min[i])
        2. 最大最小值均在右边,此时我们枚举右端点 (i),则有左端点 (j=i-(Max[i]-Min[i]))
        3. 最小值在 (mid) 左边,最大值在 (mid) 右边,枚举区间的左端点 (i) 则有:(Max[j]-Min[i]=j-iRightarrow Min[i]-i=Max[j]-j)
        4. 最小值在 (mid) 右边,最大值在 (mid) 左边,枚举区间右端点 (i) 则有:(j=i-(Max[j]-Min[i])Rightarrow Min[i]+i=Max[j]+j)
      2. 对于上面的情况 (1,2) 容易解决,只要枚举其中一个端点另一个端点就确定了,但第 (3,4) 种情况的端点不确定,比如说第 (3) 中情况,当左边的 (i) 确定了,但右边的 (j) 并不确定,因为随着 向右扫描,(Max[j]) 在发生变化,所以要扫描右边所有满足条件的 (j) 。不过我们不需要对每一个 (i) 都对 (j)(mid+1) 开始扫描,而只需从上一个 (j) 接着往下扫描即可,因为当 (i--) 的时候,如果左侧增加的这个数更新了最小值,则上一轮已经扫描过 (mid+1sim j) 仍然满足 (Min[j]>Min[i]) 。如果增加的是一个较大的数且大于了 (Max[j]) 此时我们也只需往后扫描找到当前的满足条件的 (j) 即可。具体情况见代码。

    Code

    #include <bits/stdc++.h>
    const int maxn=1e5+5,Inf=0x3f3f3f3f;
    typedef long long LL;
    int Min[maxn],Max[maxn],cnt[maxn<<1];
    int n,a[maxn];
    LL ans;
    void Divi(int L,int R){
        if(L==R){ans++;return;}//单独一个是完美子图
        int mid=(L+R)>>1;
        Divi(L,mid);
        Divi(mid+1,R);
        //预处理左右两边的最值
        Max[mid]=Min[mid]=a[mid];
        Max[mid+1]=Min[mid+1]=a[mid+1];
        for(int i=mid-1;i>=L;--i){
            Max[i]=std::max(Max[i+1],a[i]);
            Min[i]=std::min(Min[i+1],a[i]);
        }
        for(int i=mid+2;i<=R;++i){
            Max[i]=std::max(Max[i-1],a[i]);
            Min[i]=std::min(Min[i-1],a[i]);
        }
        for(int i=mid;i>=L;i--){//最大、最小均在左侧
            int j=Max[i]-Min[i]+i;//区间[i,j]
            if(j<=R && j>mid && Max[j]<Max[i] && Min[i]<Min[j])
                ans++;
        }
        for(int i=mid+1;i<=R;++i){//都在右侧
            int j=i-Max[i]+Min[i];
            if(j<=mid && j>=L && Max[j]<Max[i] && Min[j]>Min[i])
                ans++;
        }
        int j=mid+1,k=mid+1;
        for(int i=mid;i>=L;--i){//左小右大:Min[i]-i==Max[j]-j
            while(j<=R && Min[j]>Min[i])//右边的最小值要比左边的大
                cnt[Max[j]-j+n]++,++j;//Max[j]-j有可能为负,所以+n
            while(k<j && Max[k]<Max[i])//右边如果有从mid+1开始连续的区间的最大值比左边小--
                cnt[Max[k]-k+n]--,++k;
            ans+=(LL)cnt[Min[i]-i+n];
        }
        while(k<j)//清空,避免影响下面
            cnt[Max[k]-k+n]--,k++;
        j=mid;k=mid;
        for(int i=mid+1;i<=R;++i){//左大右小:Min[i]+i==Max[j]+j
            while(j>=L && Min[j]>Min[i])
                cnt[Max[j]+j]++,--j;
            while(k>j && Max[k]<Max[i])
                cnt[Max[k]+k]--,--k;
            ans+=(LL)cnt[Min[i]+i];
        }
        while(k>j)
            cnt[Max[k]+k]--,--k;
    }
    void Solve(){
        scanf("%d",&n);
        int x,y;
        for(int i=1;i<=n;++i)
            scanf("%d%d",&x,&y),a[x]=y;
        Divi(1,n);
        printf("%lld
    ",ans);
    }
    int main() {
        Solve();
        return 0;
    }
    
  • 相关阅读:
    SQL Server 2005: About login password hashes
    record drop database in sql log
    一些锁的示例
    find the physical location for specified data row
    运行sp_xp_cmdshell_proxy_account 出现的错误
    删除用户出现的错误
    conversion to dalvik format failed with error 1
    文章标题 标签提取
    in order to continue installation,please close the following application;
    提示找不到 C:\Program Files\VMware\dndlogs\dndlog.conf
  • 原文地址:https://www.cnblogs.com/hbhszxyb/p/13283005.html
Copyright © 2011-2022 走看看