2016年2月28日 星期日

[HOJ 136][TIOJ 1346][IOI 2007 Day2][DP] Training

原題連結
這道題也是各種折騰人......
題解可以參考這篇

稍微說一下我自己在看完題解後想不通的點。

剛開始我實在想不通:為什麼總複雜度不是狀態數*邊數呢?(O(2^10 * n * m))
如果我們按照常用的記憶化搜索,對於每個狀態dp[i][j]都把所有的邊嘗試一遍的話確實會退化成這樣子的。
所以在這邊加了點巧思,讓複雜度不會爛成那樣子。

關鍵點之一:每個lca為自己的邊(也就是可以拿來轉移的邊)會覆蓋到1 or 2條自己邊,其中1條邊的情況對應到「自己是這條邊的其中一個點」。
關鍵點之二:對於所有dp[i][j],若有dp[i][x]可轉移到dp[i][j],那麼必有bitcnt(j)>bitcnt(x)。
這裡bitcnt(x)指x在二進位下1的數量。

想通了第二點之後,我們就可以遞增二進位下1的數量來計算dp了。

剩下的細節還是很多的。
例如因為我們需要查詢要拿來轉移的邊會覆蓋到哪些不能刪除的邊(本來的樹邊),我開了一個map用來紀錄兩個點之間的邊編號關係(聽起來好饒舌=口=),在處理邊的時候會用上。
lca的部份用的是倍增算法,主要是因為我比較熟這個,當然換成offline的Tarjan也是可以的。

精華點在於dp和getcost的部份,多說無益,不妨直接閱讀XD。

#include<cstdio>
#include<cstdlib>
#include<vector>
#include<map>
using namespace std;
inline int cntbit(int x)
{
    x=((x&0xAAAAAAAA)>>1)+(x&0x55555555);
    x=((x&0xCCCCCCCC)>>2)+(x&0x33333333);
    x=((x&0xF0F0F0F0)>>4)+(x&0x0F0F0F0F);
    x=((x&0xFF00FF00)>>8)+(x&0x00FF00FF);
    x=((x&0xFFFF0000)>>16)+(x&0x0000FFFF);
    return x;
}
const int maxn=1000+5;
const int maxlog=10+2;
const int maxdeg=10+2;
int N,M;
struct Edge {int from,to,w;};
vector<Edge> edges;
vector<int> G[maxn],Glca[maxn];
int anc[maxn][maxlog],dep[maxn];
int LCA(int a,int b)
{
    if(dep[a]>dep[b]) swap(a,b);
    for(int log=maxlog-1;log>=0;log--)
        if(anc[b][log]!=-1 && dep[anc[b][log]]>=dep[a]) b=anc[b][log];
    if(a==b) return a;
    for(int log=maxlog-1;log>=0;log--)
        if(anc[a][log]!=-1 && anc[b][log]!=-1 && anc[a][log]!=anc[b][log])
            a=anc[a][log],b=anc[b][log];
    return anc[a][0];
}
map<int,int> id[maxn];
void dfs(int now,int fa,int depth)
{
    anc[now][0]=fa;dep[now]=depth;
    for(int i=0;i<G[now].size();i++)
    {
        Edge& e=edges[G[now][i]];
        int to=e.from==now?e.to:e.from;
        id[now][to]=i;
        if(to==fa || e.w!=0) continue;
        dfs(to,now,depth+1);
    }
}
void preprocess()
{
    dfs(0,-1,0);
    for(int log=1;log<maxlog;log++)
        for(int i=0;i<N;i++) if(anc[i][log-1]!=-1) anc[i][log]=anc[anc[i][log-1]][log-1];
    for(int i=0;i<edges.size();i++)
    {
        Edge& e=edges[i];
        if(e.w==0) continue;
        int lca=LCA(edges[i].from,edges[i].to);
        Glca[lca].push_back(i);
    }
}
int d[maxn][(1<<maxdeg)];
int getcost(int start,int end,int& cover,int& cnt,bool cycle)
{
    cnt++;
    int ret=0,now=end,fa=start;
    for(;;)
    {
        if(cycle && now==end) ret+=d[now][(1<<G[now].size())-1-(1<<id[now][anc[now][0]])];
        else ret+=d[now][(1<<G[now].size())-1-(1<<id[now][fa])-(1<<id[now][anc[now][0]])];
        if(anc[now][0]==start) cover=id[start][now];
        cnt++;
        fa=now;now=anc[now][0];
        if(now==start) return ret;
    }
}
void dp(int now,int fa)
{
    for(int i=0;i<G[now].size();i++)
    {
        Edge& e=edges[G[now][i]];
        int to=e.from==now?e.to:e.from;
        if(e.w!=0 || to==fa) continue;
        dp(to,now);
    }
    for(int i=0;i<Glca[now].size();i++)
    {
        Edge& e=edges[Glca[now][i]];
        int edge1,edge2,cnt=0;
        if(e.from == now || e.to==now) // cover one tree edge,one back edge
        {
            int to=e.from==now?e.to:e.from;
            edge1=id[now][to];
            int cost=getcost(now,to,edge2,cnt,0)+e.w;
            if(cnt%2==0) continue;
            d[now][(1<<edge1)|(1<<edge2)]=max(d[now][(1<<edge1)|(1<<edge2)],cost);
        }
        else // cover two tree edge
        {
            int to1=e.from,to2=e.to;
            int cost=getcost(now,to1,edge1,cnt,1)+getcost(now,to2,edge2,cnt,1)+e.w;cnt--;
            if(cnt%2==0) continue;
            d[now][(1<<edge1)|(1<<edge2)]=max(d[now][(1<<edge1)|(1<<edge2)],cost);
        }
    }
    int all=G[now].size();
    vector<int> toadd;
    for(int i=0;i<(1<<all);i++) if(d[now][i]) toadd.push_back(i);
    vector<vector<int> > bitnum;
    bitnum.resize(all+1);
    for(int i=0;i<(1<<all);i++) bitnum[cntbit(i)].push_back(i);
    for(int bitcnt=0;bitcnt<all;bitcnt++)
        for(int i=0;i<bitnum[bitcnt].size();i++)
        {
            int x=bitnum[bitcnt][i];
            for(int j=0;j<all;j++) if(!(x&(1<<j)))
            {
                Edge& e=edges[G[now][j]];
                int to=e.from==now?e.to:e.from;
                if(to==fa || e.w!=0) continue;
                d[now][x|(1<<j)]=max(d[now][x|(1<<j)],d[now][x]+d[to][(1<<G[to].size())-1]);
            }
            for(int j=0;j<toadd.size();j++) if(!(x&toadd[j]))
                d[now][x|toadd[j]]=max(d[now][x|toadd[j]],d[now][x]+d[now][toadd[j]]);
        }
    vector<vector<int> > ().swap(bitnum);
    vector<int> ().swap(toadd);
}
int main()
{
    int ans=0;
    scanf("%d%d",&N,&M);
    for(int i=0,a,b,c;i<M;i++)
    {
        scanf("%d%d%d",&a,&b,&c);a--,b--;
        ans+=c;
        edges.push_back((Edge){a,b,c});
        G[a].push_back(edges.size()-1);
        G[b].push_back(edges.size()-1);
    }
    preprocess();
    dp(0,-1);
    printf("%d\n",ans-d[0][(1<<G[0].size())-1]);
}

沒有留言:

張貼留言