zoukankan      html  css  js  c++  java
  • 题解 CF1314B Double Elimination

    感觉,是我做过最神的DP题之一了。

    观察题目给出的比赛结构图。可以发现,除了最开始的那一轮胜者组比赛和最后一场决赛外,剩余的比赛构成了两棵相同的树形结构(而且是满二叉树)。其中,胜者组的一个节点代表一场比赛,败者组的一个节点代表两场比赛

    让参赛队伍标号为([0,2^n-1])。类似于建线段树的方法,我们从([0,2^n-1])开始递归,每次把序列分成两半,除去长度为(1)的区间,共能得到(2^{n}-1)个区间。其中,每个区间形如([a2^b,(a+1)2^b-1])。可以发现,上一段所说的“树形结构”中,树的节点和我们分出的这些区间一一对应。

    也就是说,每个节点所代表的的比赛的两支参赛队伍,均来自这个节点所对应的区间。且对于胜者组败者组的第一场比赛,比赛的两支参赛队伍,一定是一支来自区间的左半边,另一支来自区间的右半边。而对于败者组的第二场比赛,一定是该节点中胜者组的输者败者组第一场的赢者去比。这三场比赛全部结束后,这个节点的胜者组、败者组会分别向该节点的父亲提供一支队伍,也就是该节点上两个组别的赢家。

    而对于父亲节点来说,我们关心的,就是这“两个赢家”是否是我们喜欢的队伍。

    于是想到一个DP。设(dp[ldots r][up][lo])表示标号在([l,r])这个区间内队伍之间,也就是在树上([l,r])这个节点的子树内,最多有多少场我们喜欢的队伍参加的比赛。(upin{0,1})表示这个节点的胜者组的赢家,是否是我们喜欢的队伍;(loin{0,1})表示这个节点的败者组的赢家,是否是我们喜欢的队伍。

    转移时,枚举左右区间分别的(up,lo),再枚举这个节点上三场比赛的结果,复杂度(O(2^7))

    总时间复杂度(O(2^n2^7))

    参考代码:

    //problem:CF1314B
    #include <bits/stdc++.h>
    using namespace std;
    
    #define pb push_back
    #define mk make_pair
    #define lob lower_bound
    #define upb upper_bound
    #define fst first
    #define scd second
    
    typedef unsigned int uint;
    typedef long long ll;
    typedef unsigned long long ull;
    typedef pair<int,int> pii;
    
    namespace Fread{
    const int MAXN=1<<20;
    char buf[MAXN],*S,*T;
    inline char getchar(){
    	if(S==T){
    		T=(S=buf)+fread(buf,1,MAXN,stdin);
    		if(S==T)return EOF;
    	}
    	return *S++;
    }
    }//namespace Fread
    #ifdef ONLINE_JUDGE
    	#define getchar Fread::getchar
    #endif
    inline int read(){
    	int f=1,x=0;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;
    }
    inline ll readll(){
    	ll f=1,x=0;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;
    }
    /*  ------  by:duyi  ------  */ // myt天下第一
    const int MAXN=1<<20;
    int n,m,dp[MAXN*4+5][2][2];
    bool fan[MAXN];
    #define forbit(i) for(int i=0;i<=1;++i)
    inline void ckmax(int &x,int y){x=x>y?x:y;}
    void solve(int id,int l,int r){
    	if(l+1==r){
    		//区间长度为2,递归边界
    		if(!fan[l] && !fan[r])dp[id][0][0]=0;
    		else if(!fan[l] || !fan[r])dp[id][1][0]=dp[id][0][1]=1;
    		else dp[id][1][1]=1;
    		return;
    	}
    	int mid=(l+r)>>1;
    	solve(id<<1,l,mid);
    	solve(id<<1|1,mid+1,r);
    	forbit(l_up)forbit(l_lo)forbit(r_up)forbit(r_lo){
    		if(dp[id<<1][l_up][l_lo]>=0&&dp[id<<1|1][r_up][r_lo]>=0){
    			int res=dp[id<<1][l_up][l_lo]+dp[id<<1|1][r_up][r_lo]+(l_up||r_up)+(l_lo||r_lo);
    			forbit(x)forbit(y)forbit(z){
    				//枚举当前节点上的三场比赛的结果
    				//x,y: 1表示左边赢,0表示右边赢
    				//z:   1表示上边赢,0表示下边赢
    				ckmax(dp[id][x?l_up:r_up][z?(x?r_up:l_up):(y?l_lo:r_lo)],res+((x?r_up:l_up)||(y?l_lo:r_lo)));
    			}
    		}
    	}//O(2^7)
    }
    int main() {
    	n=read();m=read();
    	for(int i=1,x;i<=m;++i)x=read()-1,fan[x]=1;
    	memset(dp,0xcf,sizeof(dp));
    	solve(1,0,(1<<n)-1);
    	int ans=0;
    	forbit(i)forbit(j)ckmax(ans,dp[1][i][j]+(i||j));
    	cout<<ans<<endl;
    	return 0;
    }
    
  • 相关阅读:
    总结几个 webpack 打包优化的方法,前端项目必备
    vue-cli 3.0 axios 跨域请求代理配置及生产环境 baseUrl 配置
    React之MobX使用
    Couldn't load this key (OpenSSH SSH-2 private key(old PEM format))的解决办法
    HTML基础篇(一,认识HTML)
    Angular学习之路-一、配置项目
    小程序开发日志-3、调用相机竖屏拍照,并将照片转横屏显示
    自定义handsome主题默认文章头图
    DruidDataSource无限重连(mybatis数据源)
    mysql获取表字段信息(字段名,字段长度,字段类型,精度,小数点位)
  • 原文地址:https://www.cnblogs.com/dysyn1314/p/12505326.html
Copyright © 2011-2022 走看看