• 【Luogu】 P4649 [IOI2007] training 训练路径


    题目链接

    点击打开链接

    题目解法

    好神仙的题啊!!!
    首先一个合法的选路径方案等价于没有偶环出现
    我们先判掉和树边能组成偶环的非树边
    然后考虑一个结论是:如果有一条边被两个偶环都经过了一次,那么这个方案不合法
    为什么?考虑把这两条路径的交去掉这两条路径的并,剩下的是一个偶环

    考虑把删边变为加边,需要加上权值和最大的边使得方案合法
    可以发现每个点的度数很小,于是考虑状压
    f u , S f_{u,S} fu,S 为在 u u u 的子树中, u u u 的儿子集合 S S S 不考虑在内的方案数
    这样只需要枚举每一条 l c a ( x , y ) = u lca(x,y)=u lca(x,y)=u 的非树边,然后转移即可
    时间复杂度 O ( 2 10 m ) O(2^{10}m) O(210m)

    #include 
    using namespace std;
    const int N=1100,M=5100;
    int n,m,dp[N][1<<10],depth[N],fa[N];
    int e[N<<1],ne[N<<1],h[N],idx;
    int id[N][N],rv[N][20];
    struct Node{ int x,y,z;}E[M];
    vector<Node> qry[N];
    inline int read(){
    	int FF=0,RR=1;
    	char ch=getchar();
    	for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1;
    	for(;isdigit(ch);ch=getchar()) FF=(FF<<1)+(FF<<3)+ch-48;
    	return FF*RR;
    }
    void add(int x,int y){ e[idx]=y,ne[idx]=h[x],h[x]=idx++;}
    void dfs(int u){
    	depth[u]=depth[fa[u]]+1;
    	for(int i=h[u];~i;i=ne[i]) if(e[i]!=fa[u]) fa[e[i]]=u,dfs(e[i]);
    }
    int get_lca(int x,int y){
    	if(depth[x]>depth[y]) swap(x,y);
    	while(depth[y]>depth[x]) y=fa[y];
    	while(x!=y) x=fa[x],y=fa[y];
    	return x;
    }
    void dfs2(int u){
    	int cnt=0;
    	for(int i=h[u];~i;i=ne[i]) if(e[i]!=fa[u]){
    		id[u][e[i]]=1<<cnt,rv[u][cnt]=e[i],cnt++;
    		dfs2(e[i]);
    	}
    	for(int S=0;S<1<<cnt;S++)
    		for(int i=0;i<cnt;i++) if(!(S>>i&1)) dp[u][S]+=dp[rv[u][i]][0];
    	for(Node t:qry[u]){
    		int x=t.x,y=t.y,res=t.z,exc=0;
    		if(x!=u){
                res+=dp[x][0];
    			while(fa[x]!=u) res+=dp[fa[x]][id[fa[x]][x]],x=fa[x];
    			exc|=id[fa[x]][x];
    		}
    		if(y!=u){
                res+=dp[y][0];
    			while(fa[y]!=u) res+=dp[fa[y]][id[fa[y]][y]],y=fa[y];
    			exc|=id[fa[y]][y];
    		}
    		for(int S=0;S<1<<cnt;S++) if((S|exc)==S+exc) dp[u][S]=max(dp[u][S],dp[u][S+exc]+res);
    	}
    }
    int main(){
    	n=read(),m=read();
    	memset(h,-1,sizeof(h));
        int cnt=0;
    	for(int i=1;i<=m;i++){
    		int x=read(),y=read(),z=read();
    		if(!z) add(x,y),add(y,x);
    		else E[++cnt]={x,y,z};
    	}
    	dfs(1);
    	int tot=0;
    	for(int i=1;i<=cnt;i++){
    		int lca=get_lca(E[i].x,E[i].y);tot+=E[i].z;
            if(~(depth[E[i].x]+depth[E[i].y]-2*depth[lca])&1) qry[lca].push_back(E[i]);
    	}
    	dfs2(1);
    	printf("%d\n",tot-dp[1][0]);
    	fprintf(stderr,"%d ms\n",int(1e3*clock()/CLOCKS_PER_SEC));
    	return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
  • 相关阅读:
    Vue3中的pinia使用,入门教程
    如何把pdf拆分成多个文件?
    jQuery常用API--效果
    PyTorch搭建RNN联合嵌入模型(LSTM GRU)实现视觉问答(VQA)实战(超详细 附数据集和源码)
    java代码审计-某酒店管理系统
    FlinkSQL自定义UDTF使用的四种方式
    ArduPilot开源飞控之RC_Channels
    使用C++代码保存深度相机D435i的RGB图片,包含C++代码以及CMakeLists.txt
    【Android】-- 向上个和下个Activity页面发送数据
    【高级测试工程师必会系列】常用测试用例设计方法之状态迁移法
  • 原文地址:https://blog.csdn.net/djx2021/article/details/133437085