洛谷 P6918 [ICPC2016 WF]Branch Assignment

题目链接: 洛谷P6918 CodeforcesGym101242B

题目大意

有一张 \(n\) 个点,\(r\) 条带权边的强连通有向图,对于 \(i,j\in [1,b]\)\(w(i,j)=dis(i,b+1)+dis(b+1,j)\) ,现在将序号 \(1\)\(b\) 的点划分为 \(s\) 个点集 \(S_1,S_2,...,S_s\),求 \(\sum_{k=1}^s\sum_{i,j\in S_k}w(i,j)\) 的最小值。

\(2\leq n\leq 5 000\)\(1\leq s\leq b\leq n-1\) ,边权 \(\in [0,10 000]\)

思路

对于一个集合 \(S\)

\[\sum_{i,j\in S}w(i,j)=\sum_{i,j\in S}(dis(i,b+1)+dis(b+1,j))=(|S|-1)\sum_{i\in S}(dis(i,b+1)+dis(b+1,i)) \]

\(a_i=dis(i,b+1)+dis(b+1,i)\) ,则 \(\sum_{i,j\in S}w(i,j)\) 即为 \((|S|-1)\sum_{i\in S}a_i\),设节点 \(i\) 所在的集合大小为 \(h_i\) ,那么有 \(ans=min\{\sum_{i=1}^bh_ia_i\}\) ,根据排序不等式,\(a_i\) 单调递增 \(h_i\) 单调递减时答案最优,由此先将 \(a_i\) 递增排序,取 \(A_i\) 为前缀和,得到一个 \(O(b^2s)\)\(dp\)

\[dp_{c,i}=min_{0\leq j<i}\{dp_{c-1,j}+(i-j)(A_i-A_j)\} \]

显然 \(w(j,i)=(i-j)(A_i-A_j)\) 满足四边形不等式(这里的 \(w\) 不是原题面中的那个),结合下图,\(A_i\)\(i\) 分别为横纵坐标,\(a\leq b\leq c\leq d\)\(w(a,d)+w(b,c)>w(a,b)+w(c,d)\) :

从而最优决策点 \(\xi_{c,i}\) 关于 \(i\) 单增,可将 \(dp\) 优化到 \(O(bslogb)\) ,经过观察,还会注意到 \(\xi_{c,i}\) 同样是对 \(c\) 单增的,

证明:

\(x=\xi_{c,i}\)\(y=\xi_{c+1,i}\) ,根据最优决策性应有:

\[dp_{c-1,x}+w(x,i)\leq dp_{c-1,y}+w(y,i)\\ dp_{c,y}+w(y,i)\leq dp_{c,x}+w(x,i) \]

移项可得:

\[dp_{c-1,x}-dp_{c-1,y}\leq w(y,i)-w(y,x)\leq dp_{c,x}-dp_{c,y}\\ dp_{c-1,x}+dp_{c,y}\leq dp_{c,x}+dp_{c-1,y} \]

由于 \(dp_{c,i}\) 满足四边形不等式,所以 \(x\leq y\) ,从而 \(\xi_{c,i}\) 关于 \(c\) 单增。

所以 \(\xi_{c-1,i}\leq \xi_{c,i}\leq \xi_{c,i+1}\),内层逆序枚举转移即可,时间复杂度:

\[\sum_{c=1}^s\sum_{i=0}^b(\xi_{c,i+1}-\xi_{c-1,i})=\sum_{i=0}^b\xi_{s,i}+s\cdot b=O(b(b+s)) \]

(中间一步可以画个矩阵自行理解)

这个做法已经可以轻松通过本题了,但是可以发现有一个性质没有用到,即 \(i-\xi_{c,i}\) 应随 \(i\) 增加而减小(后面划分的块要越来越小),利用这个性质,\(dp\) 可以进一步优化到 \(O(nlog^2n)\) ,具体做法在这里

Code:

\(O(b(b+s))\) 代码

#include<iostream>
#include<cstring>
#include<queue>
#include<algorithm>
#include<fstream>
#define mem(a,b) memset(a,b,sizeof(a))
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define per(i,b,a) for(int i=(b);i>=(a);i--)
#define N 5050
#define M 50500
#define ll long long
#define PLL pair<ll,ll>
#define mk make_pair
#define fr first
#define sc second
using namespace std;
int head[N],to[M],nxt[M],len[M];
int _head[N],_to[M],_nxt[M];
ll cnt,dis[N],_dis[N];
bool vis[N];
priority_queue<PLL,vector<PLL>,greater<PLL> > q;
int n,b,s,r;
ll a[N],A[N],best[N][N],dp[N][N];
void init(){mem(head,-1),mem(_head,-1),cnt=-1;}
void add_e(int a,int b,int *H,int *NX,int *T){
    NX[++cnt]=H[a],H[a]=cnt,T[cnt]=b;
}
bool Min(ll &a,ll b){return a>b?a=b,1:0;}
void dijkstra(ll *d,int *H,int *NX,int *T){
    mem(vis,false);
    memset(d,0x3f,(n+1)*sizeof(ll));
    d[b+1]=0;
    q.push(mk(0,b+1));
    while(!q.empty()){
        int cur=q.top().sc; q.pop();
        if(vis[cur])continue;
        vis[cur]=true;
        for(int i=H[cur];~i;i=NX[i]){
            if(Min(d[T[i]],d[cur]+len[i])) 
                q.push(mk(d[T[i]],T[i]));
        }
    }
}
int main(){
    //freopen("data.in","r",stdin);
    //freopen("mine.out","w",stdout);
    ios::sync_with_stdio(false);
    cin>>n>>b>>s>>r;
    init();
    int u,v,l;
    rep(i,1,r){
        cin>>u>>v>>l;
        add_e(u,v,head,nxt,to);
        cnt--,add_e(v,u,_head,_nxt,_to);
        len[cnt]=l;
    }
    dijkstra(dis,head,nxt,to);
    dijkstra(_dis,_head,_nxt,_to);
    rep(i,1,b)a[i]=dis[i]+_dis[i];
    sort(a+1,a+b+1);
    rep(i,1,b)A[i]=A[i-1]+a[i];
    mem(dp,0x3f);
    dp[0][0]=0;
    rep(c,1,s){
        best[c][b+1]=b;
        per(i,b,0)rep(k,best[c-1][i],min(best[c][i+1],(ll)i))
            if(Min(dp[c][i],dp[c-1][k]+(ll)(i-k)*(A[i]-A[k])))best[c][i]=k;
    }
    cout<<dp[s][b]-A[b]<<endl;
    return 0;
}

坑1:数组传指针 ll *d 对其 memeset 的时候不能直接 sizeof(d);,这样的返回值是错的。
坑2:不能对指针指向的数据进行强制类型转换,会CE
\(O(nlog^2n)\) 的做法咕了,有空再补吧。

update on 2021.4.28

补上来了,洛谷最优解,做法是决策单调性+ \(wqs\) 二分,有个注意点,二分的上界比较玄学,设为 \(2nr^2\times10000\) 在第四个点挂了,设成 \(Inf\) 才能过。(这一段时间改进了一下码风)

//This is a solution of O(nlog^2n)

#include<iostream>
#include<cstring>
#include<queue>
#include<algorithm>
#include<fstream>
#define mem(a,b) memset(a, b, sizeof(a))
#define rep(i,a,b) for (int i = (a); i <= (b); i++)
#define per(i,b,a) for (int i = (b); i >= (a); i--)
#define N 5050
#define M 50500
#define ll long long
#define PLL pair<ll, ll>
#define mk make_pair
#define fr first
#define sc second
#define Inf 0x3f3f3f3f3f3f3f3f
using namespace std;

int head[N], to[M], nxt[M], len[M];
int _head[N], _to[M], _nxt[M];
ll cnt, dis[N], _dis[N];
bool vis[N];
priority_queue<PLL, vector<PLL>, greater<PLL> > q;

int n, b, s, r;
ll a[N], A[N], best[N][N];
PLL dp[N];
struct choice{
    int l, r, i;
}c[N];

void init(){ mem(head, -1), mem(_head, -1), cnt = -1;}
void add_e(int a, int b, int *H, int *NX, int *T){
    NX[++cnt] = H[a], H[a] = cnt, T[cnt] = b;
}

bool Min(ll &a, ll b){ return a > b ? a = b, 1 : 0; }
void dijkstra(ll *d, int *H, int *NX, int *T){
    mem(vis, false);
    memset(d, 0x3f, (n+1)*sizeof(ll));
    d[b+1] = 0;
    q.push(mk(0, b+1));
    while (!q.empty()){
        int cur = q.top().sc;
        q.pop();
        if (vis[cur]) continue;
        vis[cur] = true;
        for(int i = H[cur]; ~i; i = NX[i]){
            if (Min(d[T[i]], d[cur] + len[i]))
                q.push(mk(d[T[i]], T[i]));
        }
    }
}

bool better(int i, int j, int k){
    return dp[i].fr+(k-i)*(A[k]-A[i]) < dp[j].fr+(k-j)*(A[k]-A[j]);
}
int binary_search(int l, int r, int i, int j){
    int mid;
    while(l < r){
        mid = (l+r)>>1;
        if(better(i, j, mid)) r = mid;
        else l = mid+1;
    }
    return l;
}
int cal(ll Cost){
    int l = 0, r = 0;
    dp[0] = {0, 0};
    c[0] = {1, b, 0};
    rep(i,1,b){
        while(l <= r && c[l].r < i) l++;
        c[l].l = i;
        dp[i].fr = dp[c[l].i].fr + (i-c[l].i)*(A[i]-A[c[l].i]) + Cost;
        dp[i].sc = dp[c[l].i].sc+1;
        int pos = b+1;
        while(l <= r && better(i, c[r].i, c[r].l)) pos = c[r--].l;
        if(l <= r && better(i, c[r].i, c[r].r)) pos = binary_search(c[r].l, c[r].r, i, c[r].i);
        if(l <= r) c[r].r = pos-1;
        if(pos <= b) c[++r] = {pos, b, i};
    }
    return dp[b].sc;
}
int main(){
    ios::sync_with_stdio(false);
    cin>>n>>b>>s>>r;
    init();
    int u, v, l;
    rep(i,1,r){
        cin>>u>>v>>l;
        add_e(u, v, head, nxt, to);
        cnt--, add_e(v, u, _head, _nxt, _to);
        len[cnt] = l;
    }

    dijkstra(dis, head, nxt, to);
    dijkstra(_dis, _head, _nxt, _to);
    rep(i,1,b) a[i] = dis[i] + _dis[i];
    sort(a+1, a+b+1);
    rep(i,1,b) A[i] = A[i-1] + a[i];
    
    ll left = 0, right = Inf, mid;
    while(left < right){
        mid = (left+right)>>1;
        if(cal(mid) <= s) right = mid;
        else left = mid+1;
    }
    cal(left);
    cout<< dp[b].fr-left*s-A[b] <<endl;
    return 0;
}
posted @ 2021-03-06 16:53  Neal_lee  阅读(95)  评论(0编辑  收藏  举报