【CERC2016】【BZOJ4792】村莊 搜索

題目大意

  有一個 \(2^n\times 2^n\) 的網格,左下角座標爲 \((0,0)\),右上角座標爲 \((2^n,2^n)\)spa

  定義格點 \((x,y)\) 爲座標系中座標爲 \((x,y)\) 的整點。code

  定義格子 \((x,y)\) 爲左下角格點爲 \((x,y)\),右上角格點爲 \((x+1,y+1)\) 的格子。blog

  你要給每一個格子標號:若一個格子的座標爲 \((x,y)\),其中 \(x=(x_0x_1\ldots x_{n-1}),y=(y_0y_1\ldots y_{n-1})\),則這個格子的編號爲 \((x_0y_0x_1y_1\ldots x_{n-1}y_{n-1})\)遞歸

  網格上有一個 \(m\) 個點的簡單多邊形。這個多邊形的全部頂點都是整點,全部邊都與座標軸平行。get

  記 \(C\) 爲多邊形內全部格子的編號組成的集合。string

  你每次操做能夠選擇兩個數 \([l,r]\),刪去 \(C\)\([l,r]\) 之間的全部數,代價爲 \(r-l+1\)it

  每次給你一個 \(k\),問你在操做不超過 \(k\) 次的狀況下刪去 \(C\) 中的全部數,最少總代價是多少。io

  \(n\leq 30,m\leq 200,q\leq {10}^6\)class

題解

  先把 \(C\) 中全部連續段找出來。記個數爲 \(cnt\)\(\lvert C\rvert =tot\)搜索

  那麼當 \(k\geq cnt\) 時答案爲 \(tot\)

  當 \(k\) 變小時,每次要選兩個相鄰的連續段,把這兩個連續段中間的空隙填上。

  顯然每次選的必定是最小的空隙。

  那麼咱們只用處理出每種長度的空隙的個數就行了。

  先講一下官方題解的作法:

  對於一個矩形,咱們求出這個矩形內全部空隙的長度和多邊形與這個矩形的交中編號最小最大的點。

  每次對於一個矩形 \((xl,xr,yl,yr)\),若是這個矩形是正方形,就把這個矩形沿着 \(x=\frac{(xl+xr)}{2}\) 切開,不然就沿着 \(y=\frac{yl+yr}{2}\) 切開。固然你把每一個正方形都切成四塊也是能夠的。

  這樣每一個矩形內的格子的編號都是連續的。

  若是切開來的兩個矩形形狀相同,就只遞歸一邊,不然就兩邊都遞歸。

  每次會產生多個長度爲 \(s2.min-s1.max-1\) 的空隙。

  題解說這樣作的時間複雜度是 \(O(nm^3)\) 的。

  有一個更好的作法:

  首先,矩形和空隙長度都只有 \(O(nm)\) 種。

  搜索樹有 \(O(n)\) 層。對於搜索的樹每一層,把多邊形放到這一層全部矩形組成的網格中。

  把多邊形頂點所在的矩形染成紅色,把與紅色矩形相鄰的矩形染成藍色,剩下的矩形是白色的。

  對於一個白色的矩形,若是這個矩形沒有被多邊形徹底覆蓋,也沒有和多邊形相離,那麼這個矩形必定和某一個藍色矩形是相同的。

  這樣每層只有 \(O(m)\) 個矩形,總共有 \(O(nm)\) 個矩形。因此空隙長度也只有 \(O(nm)\) 種。

  直接把每一個矩形哈希下來跑 BFS 便可。

  時間複雜度:\(O(nm^2)\)

代碼

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<unordered_map>
using namespace std;
int rd(){int s=0,c,b=0;while(((c=getchar())<'0'||c>'9')&&c!='-');if(c=='-'){c=getchar();b=1;}do{s=s*10+c-'0';}while((c=getchar())>='0'&&c<='9');return b?-s:s;}
typedef long long ll;
typedef pair<ll,ll> pll;
const int N=10000;
const int M=100000;
const ll p=1000000007;
const ll w1=127;
const ll w2=129;
ll fp(ll a,ll b)
{
    ll s=1;
    for(;b;b>>=1,a=a*a%p)
        if(b&1)
            s=s*a%p;
    return s;
}
const ll inv1=fp(w1-1,p-2);
const ll inv2=fp(w2-1,p-2);
int min(int a,int b)
{
    return a<b?a:b;
}
int max(int a,int b)
{
    return a>b?a:b;
}
ll pw1[2][50000],pw2[2][50000];
struct point{int x,y;};
int n,m;
point a[M];
void init()
{
    ll v=w1;
    for(int i=0;i<=1;i++)
    {
        pw1[i][0]=1;
        for(int j=1;j<=1<<15;j++)
            pw1[i][j]=pw1[i][j-1]*v%p;
        v=pw1[i][1<<15];
    }
    v=w2;
    for(int i=0;i<=1;i++)
    {
        pw2[i][0]=1;
        for(int j=1;j<=1<<15;j++)
            pw2[i][j]=pw2[i][j-1]*v%p;
        v=pw2[i][1<<15];
    }
}
ll pow1(ll x)
{
    return pw1[1][x>>15]*pw1[0][x&((1<<15)-1)]%p;
}
ll pow_sum1(ll x)
{
    return (pow1(x+1)-1)*inv1%p;
}
ll calc1(ll l,ll r)
{
    return pow_sum1(r-l)*pow1(l)%p;
}
ll pow2(ll x)
{
    return pw2[1][x>>15]*pw2[0][x&((1<<15)-1)]%p;
}
ll pow_sum2(ll x)
{
    return (pow2(x+1)-1)*inv2%p;
}
ll calc2(ll l,ll r)
{
    return pow_sum2(r-l)*pow2(l)%p;
}
struct rect
{
    int xl,xr,yl,yr;
    rect(int a=0,int b=0,int c=0,int d=0):xl(a),xr(b),yl(c),yr(d){}
};
map<ll,ll> g;
map<ll,int> c[32];
rect d[100000];
ll f[100000];
ll h[100000];
pll s[100000];
int c22[100000];
int c21[100000];
int c11[100000];
int c12[100000];
int num;
int __;
ll insert(int xl,int xr,int yl,int yr,int x,ll v)
{
    ll s=0;
    for(int i=1;i<=m;i++)
    {
        __++;
        int _=(i==m?1:i+1);
        if(a[i].y!=a[_].y||a[i].y>yr)
            continue;
        int x1=min(a[i].x,a[_].x);
        int x2=max(a[i].x,a[_].x);
        if(x1>xr||x2<xl)
            continue;
        int y1=max(a[i].y,yl);
        int y2=yr;
        x1<xl?x1=xl:0;
        x2>xr?x2=xr:0;
        if(a[i].x<a[_].x)
            s+=calc1(x1-xl,x2-xl-1)*calc2(y1-yl,y2-yl-1)%p;
        else
            s-=calc1(x1-xl,x2-xl-1)*calc2(y1-yl,y2-yl-1)%p;
    }
    s=(s%p+p)%p;
    int &o=c[x][s];
    if(!o)
    {
        num++;
        o=num;
        h[num]=s;
        d[num]=rect(xl,xr,yl,yr);
    }
    f[o]+=v;
    return o;
}
pll e[1000000];
ll s1[1000000],s2[1000000];
int t;
int main()
{
#ifndef ONLINE_JUDGE
    freopen("b.in","r",stdin);
    freopen("b.out","w",stdout);
#endif
    init();
    
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
        scanf("%d%d",&a[i].x,&a[i].y);

    int rt=insert(0,1<<n,0,1<<n,n,1);
    
    for(int i=n;i>=1;i--)
    {
        for(auto v:c[i])
        {
            int xl=d[v.second].xl;
            int xr=d[v.second].xr;
            int yl=d[v.second].yl;
            int yr=d[v.second].yr;
            ll _=f[v.second];
            int xmid=(xl+xr)>>1;
            int ymid=(yl+yr)>>1;
            c11[v.second]=insert(xl,xmid,yl,ymid,i-1,_);
            c12[v.second]=insert(xl,xmid,ymid,yr,i-1,_);
            c21[v.second]=insert(xmid,xr,yl,ymid,i-1,_);
            c22[v.second]=insert(xmid,xr,ymid,yr,i-1,_);
        }
    }
    
    fprintf(stderr,"%d\n",__);
//  return 0;
    
    for(int i=num;i>=1;i--)
    {
        int xl=d[i].xl;
        int xr=d[i].xr;
        int yl=d[i].yl;
        int yr=d[i].yr;
        ll area=ll(xr-xl)*(yr-yl);
        if(area==1)
        {
            if(h[i]==1)
                s[i]=pll(0,0);
            else
                s[i]=pll(-1,-1);
            continue;
        }
        pll s1,s2;
        pll s11=s[c11[i]],s12=s[c12[i]];
        if(s12.first==-1)
            s1=s11;
        else if(s11.first==-1)
            s1=pll(s12.first+area/4,s12.second+area/4);
        else
        {   
            s12.first+=area/4;
            s12.second+=area/4;
            g[s12.first-s11.second-1]+=f[i];
            s1=pll(s11.first,s12.second);
        }
        pll s21=s[c21[i]],s22=s[c22[i]];
        if(s22.first==-1)
            s2=s21;
        else if(s21.first==-1)
            s2=pll(s22.first+area/4,s22.second+area/4);
        else
        {   
            s22.first+=area/4;
            s22.second+=area/4;
            g[s22.first-s21.second-1]+=f[i];
            s2=pll(s21.first,s22.second);
        }
        if(s2.first==-1)
            s[i]=s1;
        else if(s1.first==-1)
            s[i]=pll(s2.first+area/2,s2.second+area/2);
        else
        {
            s2.first+=area/2;
            s2.second+=area/2;
            g[s2.first-s1.second-1]+=f[i];
            s[i]=pll(s1.first,s2.second);
        }
    }
    
    pll all=s[rt];
    g.erase(0);
    ll cnt=1,tot=all.second-all.first+1;
    for(auto v:g)
    {
        e[++t]=v;
        tot-=v.first*v.second;
        cnt+=v.second;
    }
    sort(e+1,e+t+1);
    for(int i=1;i<=t;i++)
    {
        s1[i]=s1[i-1]+e[i].second;
        s2[i]=s2[i-1]+e[i].first*e[i].second;
    }
    
    
//  return 0;
    
    int q;
    ll x;
    scanf("%d",&q);
    for(int i=1;i<=q;i++)
    {
        x=rd();
        if(x>=cnt)
            printf("%lld\n",tot);
        else
        {
            x=cnt-x;
            ll ans=tot;
            int y=upper_bound(s1+1,s1+t+1,x)-s1;
            ans+=s2[y-1];
            ans+=e[y].first*(x-s1[y-1]);
            printf("%lld\n",ans);
        }
    }
    return 0;
}
相關文章
相關標籤/搜索