2015年4月30日 星期四

[CF 536D] Tavas in Kansas

作法:

首先先處理出給定的兩個點到其他所有點的距離。再來把到第一個點的距離們和到第二個點的距離們分別離散化,假設總共有 n 種到第一個點的距離, m 種到第二個點的距離,那麼考慮一張 n * m 的方格表(左上角是 ( 1 , 1 ) ,右下角是 ( n , m )),如果在原本的圖中有一個點到給定的兩個點的距離分別是 x1 和 y1 (已經離散化過了,所以 1 <= x1 <= n 且 1 <= y1 <= m),那麼就把這個方格表的 ( x,y ) 位置的價值加上這個點的點權。那麼這個遊戲就可以等價成:先手首先選一個 y0 ,把 y 座標 <= y0 的格子都取走,然後換後手選一個 x0 ,把 x 座標 <= x0 的格子都取走,一直重複這個動作直到格子被取完為止,取的點權和比較多的人就贏了。

但這裡我們還少考慮到了一個東西,就是題目有要求每次必須要至少選到原圖中的一個點,不過我們先思考不考慮那件事的情形。那麼這時就顯然是一個 DP ,記 dp1[ i ][ j ] 代表盤面剩下 ( i , j ) ~ (  n , m ) ,且此時輪到先手時先手可以獲得幾分, dp2[ i ][ j ] 則是此時輪到後手時後手可以獲得幾分,並且記 ( i , j ) ~ ( n , m ) 的分數總和為 S,那麼 dp1[ i ][ j ] 就會等於 S - min{ dp2[ i ][ k ] } , k = j+1 , ... , m+1 (註:當 k = m+1 時代表此時盤面為空)。同理 dp2[ i ][ j ] 的轉移式也類似,而這樣的轉移可以藉由維護好 min{ dp2[ i ][ k ] } 的方法來把複雜度降為 O( nm) 。具體來說,考慮按照從第 n 列做到第1列,同列時從第 m 行作到一行的順序計算每個 DP 值,那麼在算 dp1[ i ][ j ] 的時候,我們需要的值是 min{ dp2[ i ][ j+1 ] , ... , dp2[ i ][ m+1 ] } , 因此只要紀錄一個值 miy ,代表我們需要的值,並且在算完 dp2[ i ][ j ]的時候拿他去更新 miy 就好了,並且在我們計算到下一列的時候把 miy 的值清掉,就可以繼續用了。同理在算 dp2[ i ][ j ] 的時候,需要的會是 min{ dp1[ i+1 ][ j ] , ... , dp1[ n ][ j ] } ,但這時由於我們計算的順序不是直的,所以不能像剛才一樣只用一個值來維護,必須用一個陣列,也就是每次在算 dp2[ i ][ j ] 需要的值會是 mix[ j ],並且在算完 dp1[ i ][ j ] 之後把 mix[ j ] 用 dp1[ i ][ j ] 取 min ,維護好他的值。

上面成功解決了弱化版的問題,接下來要把題目原始的條件考慮進來。首先如果當前的狀態是 ( i , j ) ,並且輪到先手,設先手在這一局吃掉的矩形是 ( i , j ) ~ ( n , y0 ) ,那麼為了要讓這一步是合法的,就必須要有這個矩形裡面有至少一個原始圖中的點。但不能直接用這塊矩形的價值總和是否為 0 來判斷,因為題目裡也有提醒點權有可能是 0 ,因此還必須在另外處理一個 val 陣列,其中 val[ i ][ j ] 在 ( i , j ) 有對應到原圖中的一個點時其值為 1 ,否則為 0 ,並且處理出他的二維前綴和陣列,那麼就可以 O( 1 ) 知道這一步合不合法了。再來則是轉移的部份必須要更改,首先看 dp1[ i ][ j ] 的部份,在之前的情況是他可以轉移到 ( i , j+1 ) , ... , ( i , m ) ,但在這裡則不行,因為如果 ( i , j ) 到 ( n , y0 ) 的 val 總和是 0 的話,就會沒辦法轉移到 ( i , y0+1 ) ,但一個簡單的觀察是,如果 ( i , j ) 可以轉移到 ( i , y0 ) ,那麼就可以轉移到 ( i , y1 ) ,其中 y1 >= y0 ,因此我們可以用類似雙指標的方法,多紀錄一個 id 值,代表當前的 miy 的值等於 min{ dp2[ i ][ id+1 ] , dp2[ i ][ id+2 ] , ... , dp2[ i ][ m+1 ] } ,其中 id 代表的意義是「讓 ( i , j ) , ( n , id ) 形成的矩形內的 val 值總和不為 0 的最小值」,那麼就可以維護好 miy 值了。而在算 dp2[ i ][ j ] 的部份就幾乎一樣了,差別只有在和剛剛一樣的 mix 值會變成一個陣列,還有 id 值也會而已了。最後算出的 dp1[ 1 ][ 1 ] 就是先手能夠獲得的分數了。

code :



#include<bits/stdc++.h>
#define LL long long
#define INF (1LL<<60)
using namespace std;
const int maxn=2000+10 ;
 
struct P{int to,dis;};
vector<P> v[maxn] ;
 
struct Q
{
    int id ; LL dis ;
    bool operator < (const Q &rhs) const
    {
        return dis>rhs.dis ;
    }
};
 
priority_queue<Q> pq ;
bool done[maxn] ;
void Dijkstra(int st,LL *d)
{
    fill(d,d+maxn,INF) ;
    d[st]=0 ; pq.push((Q){st,0}) ;
    memset(done,0,sizeof(done)) ;
    while(!pq.empty())
    {
        Q u=pq.top() ; pq.pop() ;
        if(done[u.id]) continue ;
        done[u.id]=1 ;
        for(auto i : v[u.id]) if(d[i.to]>d[u.id]+i.dis)
            d[i.to]=d[u.id]+i.dis , pq.push((Q){i.to,d[i.to]}) ;
    }
}
 
LL d1[maxn],d2[maxn] ;
vector<LL> vx,vy ;
 
LL S(int x1,int y1,int x2,int y2,LL a[maxn][maxn])
{
    return a[x2][y2]-a[x1-1][y2]-a[x2][y1-1]+a[x1-1][y1-1] ;
}
 
int w[maxn] ;
LL sum[maxn][maxn] ;
LL dp1[maxn][maxn],dp2[maxn][maxn],val[maxn][maxn] ;
LL mix[maxn] ;
int nowx[maxn] ;
 
main()
{
    int n,m,s,e ; scanf("%d%d%d%d",&n,&m,&s,&e) ;
    for(int i=1;i<=n;i++) scanf("%d",&w[i]) ;
    while(m--)
    {
        int x,y,dis ; scanf("%d%d%d",&x,&y,&dis) ;
        v[x].push_back((P){y,dis}) ;
        v[y].push_back((P){x,dis}) ;
    }
 
    Dijkstra(s,d1) ;
    Dijkstra(e,d2) ;
    for(int i=1;i<=n;i++) vx.push_back(d1[i]) , vy.push_back(d2[i]) ;
    sort(vx.begin(),vx.end()) ;
    sort(vy.begin(),vy.end()) ;
    vx.resize(unique(vx.begin(),vx.end())-vx.begin()) ;
    vy.resize(unique(vy.begin(),vy.end())-vy.begin()) ;
    int sx=vy.size() , sy=vx.size() ;
 
    for(int i=1;i<=n;i++)
    {
        int x=lower_bound(vx.begin(),vx.end(),d1[i])-vx.begin()+1 ;
        int y=lower_bound(vy.begin(),vy.end(),d2[i])-vy.begin()+1 ;
        val[y][x]=1 ; sum[y][x]+=w[i] ;
    }
    for(int i=1;i<=sx;i++) for(int j=1;j<=sy;j++)
        sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+sum[i][j] ,
        val[i][j]=val[i-1][j]+val[i][j-1]-val[i-1][j-1]+val[i][j] ;
 
    for(int i=1;i<=sy;i++) mix[i]=INF , nowx[i]=sx ;
 
    LL miy=INF ;
    for(int i=sx;i>=1;i--) for(int j=sy,nowy=sy;j>=1;j--)
    {
        if(j==sy) miy=INF ;
        if(S(i,j,sx,j,val)) while(nowy>=j)
        {
            miy=min(miy,dp2[i][nowy+1]) ;
            nowy-- ;
        }
        if(S(i,j,i,sy,val)) while(nowx[j]>=i)
        {
            mix[j]=min(mix[j],dp1[nowx[j]+1][j]) ;
            nowx[j]-- ;
        }
 
        if(miy!=INF) dp1[i][j]=S(i,j,sx,sy,sum)-miy ;
        if(mix[j]!=INF) dp2[i][j]=S(i,j,sx,sy,sum)-mix[j] ;
    }
 
    LL ans=dp1[1][1] , all=S(1,1,sx,sy,sum) ;
    if(2*ans==all) printf("Flowers\n") ;
    else if(2*ans<all) printf("Cry\n") ;
    else printf("Break a heart\n") ;
}
 

沒有留言:

張貼留言