bzoj1227 [SDOI2009]虔誠的墓主人

傳送門

一上來先想到n2 做法,即枚舉每一個墓地,排列組合計算虔誠度,但這個時間複雜度是不現實的,所以要進行轉化,改爲用枚舉常青樹來計算相鄰常青樹間墓地的虔誠度之和,由於兩個相鄰的常青樹之間的墓地左右的常青樹數目都是相同的,所以問題轉化爲維護相鄰常青樹之間所有墓地上下常青樹數目形成的十字架的個數。又由於枚舉到的兩個常青樹之間的墓地上下常青樹數目一定可以在之前維護出來(枚舉到一棵常青樹的時候在記錄數組中將這一列屬於上半部分的個數減1,屬於下半部分的個數加1,然後用新的組合數乘積更新區間和,可用樹狀數組維護),而每棵常青樹左右的樹的數目又可以來回掃兩遍計算,所以題目就解決了。
我的語文太差了,如果看不懂就看代碼吧。。
ps:吐槽數據弱,計算楊輝三角的時候只枚舉列數或行數,只會錯1到2個點,一定要枚舉到行數和列數的最大值!

CODE:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define lowbit(x) x&(-x)
const int N=1e5+5;
struct node
{
    int x,y;
    inline bool operator <(const node other)const
    {
        if(y==other.y) return x<other.x;
        return y<other.y;
    }
}p[N];
int L[N],R[N],a[N];
int u[N],d[N];
int c[N][15];
int tmp1[N],tmp2[N],tmp;
int n,m,w,k,tot1,tot2,ans,l,r;
inline void add(int a[],int pos,int num)
{
    for(int i=pos;i<=tot1;i+=lowbit(i))
      a[i]+=num;
}
inline int ask(int a[],int pos)
{
    int ans=0;
    for(int i=pos;i;i-=lowbit(i))
      ans+=a[i];
    return ans;
}
int main()
{
    scanf("%d%d%d",&n,&m,&w);
    for(int i=1;i<=w;i++)
      scanf("%d%d",&p[i].x,&p[i].y),tmp1[i]=p[i].x,tmp2[i]=p[i].y;
    scanf("%d",&k);
    sort(p+1,p+w+1);
    sort(tmp1+1,tmp1+w+1);
    sort(tmp2+1,tmp2+w+1);
    tot1=unique(tmp1+1,tmp1+w+1)-tmp1-1;
    tot2=unique(tmp2+1,tmp2+w+1)-tmp2-1;
    for(int i=1;i<=w;i++)
      p[i].x=lower_bound(tmp1+1,tmp1+tot1+1,p[i].x)-tmp1,
      p[i].y=lower_bound(tmp2+1,tmp2+tot2+1,p[i].y)-tmp2;
    c[0][0]=1;
    for(int i=1;i<=max(tot1,tot2);i++)
    {
        c[i][0]=1;
        for(int j=1;j<=i&&j<=k;j++)
          c[i][j]=c[i-1][j-1]+c[i-1][j];
    }
    for(int i=1;i<=w;i++)
    {
        if(p[i].y==p[i-1].y) tmp++;
        else tmp=0;
        L[i]=tmp;
    }
    tmp=0;
    for(int i=w;i;i--)
    {
        if(p[i].y==p[i+1].y) tmp++;
        else tmp=0;
        R[i]=tmp;
    }
    for(int i=1;i<=w;i++)
      u[p[i].x]++;
    for(int i=1;i<=tot2;i++)
    {
        l=++r;
        while(r<=w&&p[r].y==i) r++;
        r--;
        add(a,p[l].x,-c[d[p[l].x]][k]*c[u[p[l].x]][k]);
        d[p[l].x]++,u[p[l].x]--;
        add(a,p[l].x,c[d[p[l].x]][k]*c[u[p[l].x]][k]);
        for(int j=l+1;j<=r;j++)
        {
            add(a,p[j].x,-c[d[p[j].x]][k]*c[u[p[j].x]][k]);
            d[p[j].x]++,u[p[j].x]--;
            add(a,p[j].x,c[d[p[j].x]][k]*c[u[p[j].x]][k]);
            ans+=c[L[j]][k]*c[R[j-1]][k]*(ask(a,p[j].x-1)-ask(a,p[j-1].x));
        }
    }
    printf("%d",ans&2147483647);
    return 0;
}

反思:思維的轉化是很重要的,有時候數據範圍太大,可以思考用較小的量來代替計算,還有一定要注意枚舉的範圍這個細節,經常會出錯!

發佈了137 篇原創文章 · 獲贊 6 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章