85分蜜汁WA三个点求助

P4383 [八省联考 2018] 林克卡特树

Qiuly @ 2019-05-12 10:47:04

RT,无论是在 luogu 还是 loj 交了一遍又一遍,14,17,18 三个点总是 \rm{WA} 掉,不知道为什么,瞎改了很多地方这三个点还是不断的 \rm{WA} ,故求助大佬。

二分的地方自认为很迷,但是不知道应该怎么改才好......尝试过重构但未果。

Code:

#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;

const int N=6e5+2;
const ll inf=1e18+9;

int n,k,head[N],cnt;
struct date {
    ll x;int y;
    bool operator < (const date&var) const {return x==var.x?y>var.y:x<var.x;}
    date operator + (const date&var) {return (date){x+var.x,y+var.y};}
    date operator + (const ll&var) {return (date){x+var,y};}
}dp[3][N];
ll number(date var) {return var.x;}
struct Edge{int nxt,to,val;} G[N<<1];
void add(int u,int v,int w) {
    G[++cnt]=(Edge){head[u],v,w},head[u]=cnt;
    G[++cnt]=(Edge){head[v],u,w},head[v]=cnt;
}

template <typename _Tp> inline void IN(_Tp&x) {
    char ch;bool flag=0;x=0;
    while(ch=getchar(),!isdigit(ch)) if(ch=='-') flag=1;
    while(isdigit(ch)) x=x*10+ch-'0',ch=getchar();
    if(flag) x=-x;
}

void check(ll add,int u,int fa) {
    dp[0][u]=(date){0,0},
    dp[1][u]=(date){-inf,0},
    dp[2][u]=(date){add,1};
    for(int e=head[u];e;e=G[e].nxt) {
        int v=G[e].to;
        ll w=G[e].val;
        if(v==fa) continue;
        check(add,v,u);
        date num=max(dp[0][v],max(dp[1][v],dp[2][v]));

        dp[2][u]=max(dp[2][u],dp[2][u]+num);
        dp[2][u]=max(dp[2][u],dp[1][u]+(date){w,0}+max(dp[0][v],dp[1][v]+(date){-add,-1}));

        dp[1][u]=max(dp[1][u],dp[1][u]+num);
        dp[1][u]=max(dp[1][u],dp[0][u]+(date){w,0}+max(dp[1][v],dp[0][v]+(date){add,1}));

        dp[0][u]=dp[0][u]+num;
    }return;
}

ll wqs(ll sum) {
    ll l=-sum,r=sum,mid;
    date now;
    while(l<r) {
        mid=(l+r+1)>>1,check(mid,1,0);
        now=max(dp[0][1],max(dp[1][1],dp[2][1]));
        if(!(now.y^k)) {l=r=mid;break;}
        now.y<k?l=mid:r=mid-1;
    }
    check(l,1,0);
    now=max(dp[0][1],max(dp[1][1],dp[2][1]));
    return now.x-1ll*mid*k;
}

int main() {
    // freopen("lct2.in","r",stdin);
    // freopen("P4383.out","w",stdout);
    IN(n),IN(k);++k;
    ll sum=0;
    for(int i=1;i<n;++i) {
        int x,y,v;IN(x),IN(y),IN(v);
        add(x,y,v),sum+=abs(v);
    }
    printf("%lld\n",wqs(sum));
    return 0;
}   

by w4p3r @ 2019-08-13 16:06:01

    return now.x-1ll*mid*k;

改成

    return now.x-1ll*mid*now.y;

我才不会告诉你我也错在这儿


by Treaker @ 2020-06-18 19:43:02

@WAPER420 这是为啥呀。。。


by w4p3r @ 2020-06-18 20:00:53

@Treaker 我这都多久回答的了。。早忘了qaq


by Treaker @ 2020-06-18 20:05:45

@WAPER420 想想呗


by tommy0221 @ 2020-07-27 18:24:13

@Treaker 这个问题比较显然,建议重新看看您学wqs二分的那博客,如果找到的文章不那么差都会讲这个问题


by 5ab_juruo @ 2023-03-04 16:52:42

@WAPER4EVER 我按照你的方法改了被 LOJ 数据叉了。


by w4p3r @ 2023-03-04 18:05:22

@5ab_juruo 我的天哪。三年了还回复啊。我高一做的题这会儿都大一了。

马上我看一下吧。


by w4p3r @ 2023-03-04 18:25:43

@5ab_juruo 首先,我三年前讲错了,输出的答案就应该是 -k \times mid 而非 -now.y \times mid

其次,注意你的二分要与你在值相同时求边数最小/边数最大要保持一致。

例如我的二分是这样的:

    ll l=-inf,r=inf,ans=0;
    while(l<=r)
    {
        ll mid=(l+r)>>1;
        if(check(mid)<=k){ans=mid;r=mid-1;}
        else l=mid+1;
    }

那么这样的话在值相同时我应该求边数最小的方案。

但是我在原代码里求了边数最大的方案,所以导致了将 -k \times mid 改为 -now.y \times mid 就过了(这应该是一个巧合)。

如果你 AC 了该题却被 hack 掉了,注意一下我上面说的这个问题。


by w4p3r @ 2023-03-04 18:26:45

哎呀好像甚至不是高一做的,初三做的。。


by CarroT1212 @ 2023-12-26 15:39:02

@WAPER4EVER 之前一直没搞懂为什么 wqs 有时候按最小有时候按最大的,帮大忙了,感谢

这会您都大二了吧


|