BZOJ 4151([AMPPZ2014]The Cave-树,贪心)

8 篇文章 0 订阅
5 篇文章 0 订阅

题意:给一棵n个节点的树和m条限制,每条限制形如 dis(x,ai)+dis(x,bi)di ,其中 dis(a,b) 表示a到b经过的边的个数。问图中是否存在x满足所有限制,有的话输出任意一个合法解,否则输出无解。

设x为根。
依次遍历每条限制,如果 ai,bi 均在x的子树中且限制不成立,那么x向这2个点的lca到x的这条链上向子树方向移动,直至满足限制,如果移动到lca处仍不满足限制则说明无解。

现在证明如果有解,那么x一定为合法解。

证明:
首先合法解必然在x的子树内。
如果 ai,bi 均在x的子树内,显然满足。
如果 ai,bi 恰有1个点在x的子树内,说明x在 ai,bi 的链上,显然不可能更优。
如果 ai,bi 均在x的子树外,显然x的深度越小越好。

#include<bits/stdc++.h> 
using namespace std;
#define For(i,n) for(int i=1;i<=n;i++)
#define Fork(i,k,n) for(int i=k;i<=n;i++)
#define ForkD(i,k,n) for(int i=n;i>=k;i--)
#define Rep(i,n) for(int i=0;i<n;i++)
#define ForD(i,n) for(int i=n;i;i--)
#define RepD(i,n) for(int i=n;i>=0;i--)
#define Forp(x) for(int p=pre[x];p;p=next[p])
#define Forpiter(x) for(int &p=iter[x];p;p=next[p])  
#define Lson (o<<1)
#define Rson ((o<<1)+1)
#define MEM(a) memset(a,0,sizeof(a));
#define MEMI(a) memset(a,0x3f,sizeof(a));
#define MEMi(a) memset(a,128,sizeof(a));
#define MEMx(a,b) memset(a,b,sizeof(a));
#define INF (0x3f3f3f3f)
#define F (1000000007)
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define vi vector<int> 
#define pi pair<int,int>
#define SI(a) ((a).size())
#define Pr(kcase,ans) printf("Case #%d: %lld\n",kcase,ans);
#define PRi(a,n) For(i,n-1) cout<<a[i]<<' '; cout<<a[n]<<endl;
#define PRi2D(a,n,m) For(i,n) { \
                        For(j,m-1) cout<<a[i][j]<<' ';\
                        cout<<a[i][m]<<endl; \
                        } 
#define ALL(x) (x).begin(),(x).end()
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
ll mul(ll a,ll b){return (a*b)%F;}
ll add(ll a,ll b){return (a+b)%F;}
ll sub(ll a,ll b){return ((a-b)%F+F)%F;}
void upd(ll &a,ll b){a=(a%F+b%F)%F;}
inline int read()
{
    int x=0,f=1; char ch=getchar();
    while(!isdigit(ch)) {if (ch=='-') f=-1; ch=getchar();}
    while(isdigit(ch)) { x=x*10+ch-'0'; ch=getchar();}
    return x*f;
} 
#define MAXN (310000)
int n,m;
int t_cnt=0,dfn[MAXN],dep[MAXN],r[MAXN];
ll deep[MAXN];
struct edge{
    int v,w;
};
vector<edge> e[MAXN];
int fa[MAXN][21];
void dfs(int x,int fa,ll d=0,ll d2=0) {
    ::fa[x][0]=fa;
    For(i,20) ::fa[x][i]=::fa[::fa[x][i-1]][i-1];
    deep[x]=d;dep[x]=d2;
    dfn[x]=++t_cnt;
    for(int i=0;i<SI(e[x]);i++) {
        edge node = e[x][i];
        if (node.v!=fa) {
            dfs(node.v,x,d+node.w,d2+1);
        }
    }r[x]=t_cnt;
}
int lca(int x,int y) {
    if (dep[x]<dep[y]) swap(x,y);
    RepD(i,20) if (dep[fa[x][i]]>=dep[y]) {
        x=fa[x][i];
    }
    RepD(i,20) if (fa[x][i]!=fa[y][i]) {
        x=fa[x][i],y=fa[y][i];
    }
    if (x^y) x=fa[x][0],y=fa[y][0];
    return x;

}
ll dis(int x,int y) {
    return deep[x]+deep[y]-deep[lca(x,y)]*2;
}
int x[MAXN],y[MAXN],d[MAXN];
bool check(int root) {
    For(i,m) if (dis(x[i],root)+ dis(y[i],root)>d[i]) return 0; return 1;
}
bool inside(int a,int b) {  
    return dfn[b]<=dfn[a] &&dfn[a]<=r[b] ;
}
int main()
{
//  freopen("bzoj4151.in","r",stdin);
    int t=read();
    while(t--) {
        cin>>n>>m;
        For(i,n) Rep(j,21) fa[i][j]=0;
        For(i,n) e[i].clear();
        For(i,n-1) {
            int u=read(),v=read(),w=1;
            e[u].pb({v,w});
            e[v].pb({u,w});
        }
        MEM(dfn)
        t_cnt=0;
        dfs(1,-1,0,1);
        int root=1;
        For(i,m) {
            x[i]=read(),y[i]=read(),d[i]=read();
            if (!(inside(x[i],root) && inside(y[i],root))) continue;
            while (dis(x[i],root) + dis(y[i],root)>d[i] && inside(x[i],root) &&inside(y[i],root) ) {
                bool fl=0;
                Rep(j,SI(e[root])) {
                    edge v=e[root][j];
                    if (inside(x[i],v.v) &&inside(y[i],v.v) && fa[v.v][0]==root ){
                        root=v.v;fl=1;break;
                    }
                }
                if (!fl) {
                    break;
                }
            }
        }
        if (check(root)) {
            printf("TAK %d\n",root);
        }else {
            puts("NIE");
        }
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值