• CF1119F Niyaz and Small Degrees


    一、题目

    点此看题

    二、解法

    (dp[u][0/1]) 表示解决 (u) 子树内所有问题,(u) 的父边选不选的方案数,转移的时候把 (dp[v][1]+w-dp[v][0]) 从小到大排序,然后取一个前缀让 (u) 满足限制即可。

    难点就是要对所有 (x) 求出答案,首先发现 (d[u]leq x) 的点 (u) 是没有用的,因为它一定合法。

    那么我们从小到大枚举 (x),每次找出这样的点 (u),因为它已经合法所以可以把它直接删除,但它连的边可能还有用,我们直接把这条边的边权塞进 (v) 的大根堆中,这个堆就维护最优的删边方案,如果大小足够就把堆顶弹出即可。

    然后原图就分成了若干个连通块,我们对于每个连通块分别 (dp) 即可,考虑 (v) 的转移时也把选择边 ((u,v)) 的代价塞进堆中,然后用堆决策即可。注意 (dp) 完了之后还要还原堆,因为要支持删除所以我手写了,挺好玩的

    (dp) 的时候注意只能访问度数大于等于 (x) 的点,所以我们要把每个点的边按终点的度数大小排序。

    因为每个点只会被 (dp) 度数次,所以总时间复杂度是度数的累加,时间复杂度 (O(nlog n))

    三、总结

    把时间复杂度和某些量扯上关系,考虑和原问题紧密相关的量。

    #include <cstdio>
    #include <vector>
    #include <algorithm>
    using namespace std;
    #define int long long
    const int M = 250005; 
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int n,sum,t,d[M],id[M],vis[M],dp[M][2];
    struct node
    {
    	int v,c;
    	bool operator < (const node &b) const
    	{
    		return d[v]>d[b.v];
    	}
    };vector<node> g[M];
    struct heap
    {
    	vector<int> v;
    	heap() {v.clear();}
    	void push(int x) {v.push_back(x);push_heap(v.begin(),v.end());}
    	void pop() {pop_heap(v.begin(),v.end());v.pop_back();}
    	int top() {return v[0];}
    	int sz() {return v.size();}
    };
    struct zxy
    {
    	heap a,b;int sum,s;
    	zxy() {sum=s=0;}
    	void push(int x) {a.push(x);sum+=x;s++;}
    	void del(int x) {b.push(x);sum-=x;s--;}
    	void work() {while(a.sz() && b.sz() && a.top()==b.top()) a.pop(),b.pop();}
    	void pop() {work();s--;sum-=a.top();a.pop();}
    	int top() {work();return a.top();}
    	int sz() {return s;}
    }h[M];
    bool cmp(int x,int y)
    {
    	return d[x]<d[y];
    }
    void era(int u)
    {
    	for(node x:g[u])
    	{
    		if(d[x.v]<=t) break;
    		h[x.v].push(x.c);
    	}
    }
    void dfs(int u)
    {
    	dp[u][0]=dp[u][1]=0;vis[u]=t;
    	int nd=d[u]-t,tmp=0;vector<int> v1,v2;
    	while(h[u].sz()>nd) h[u].pop();
    	for(node x:g[u])
    	{
    		if(d[x.v]<=t) break;
    		if(vis[x.v]==t) continue;
    		dfs(x.v);
    		int c=dp[x.v][1]+x.c-dp[x.v][0];
    		if(c<=0) {nd--;tmp+=dp[x.v][1]+x.c;continue;}
    		h[u].push(c);tmp+=dp[x.v][0];v1.push_back(c);
    	}
    	for(;h[u].sz() && h[u].sz()>nd;h[u].pop()) v2.push_back(h[u].top());
    	dp[u][0]=tmp+h[u].sum;
    	for(;h[u].sz() && h[u].sz()>nd-1;h[u].pop()) v2.push_back(h[u].top());
    	dp[u][1]=tmp+h[u].sum;
    	while(v2.size()) h[u].push(v2.back()),v2.pop_back();
    	while(v1.size()) h[u].del(v1.back()),v1.pop_back();
    }
    signed main()
    {
    	n=read();
    	for(int i=1;i<n;i++)
    	{
    		int u=read(),v=read(),w=read();
    		g[u].push_back(node{v,w});
    		g[v].push_back(node{u,w});
    		d[u]++;d[v]++;sum+=w;
    	}
    	for(int i=1;i<=n;i++)
    		id[i]=i,sort(g[i].begin(),g[i].end());
    	sort(id+1,id+1+n,cmp);
    	printf("%lld",sum);int i=1;
    	for(t=1;t<n;t++)
    	{
    		while(i<=n && d[id[i]]<=t) era(id[i++]);
    		int ans=0;
    		for(int j=i;j<=n;j++)
    		{
    			if(vis[id[j]]==t) continue;
    			dfs(id[j]);
    			ans+=dp[id[j]][0];
    		}
    		printf(" %lld",ans);
    	}
    }
    
  • 相关阅读:
    idea 导入maven项目各种红
    基于 Spring Security 的开源统一角色访问控制系统 URACS
    MySQL读写分离又一好办法 使用 com.mysql.jdbc.ReplicationDriver
    [转]CVS SVN VSS 使用对比
    推荐一个免费开源的虚拟机VBox(VirtualBox)
    JavaScript的对象属性的反射
    [转]需求分析的目的
    尝鲜安装iOS6及新特性
    EXP00003: 未找到段 (4,571) 的存储定义
    QQ邮箱里可以定阅博客园的文章了
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/15068256.html
Copyright © 2020-2023  润新知