武汉大学校赛总结 + E题+F题

4 篇文章 0 订阅

E.Your NP has been charged full

这次比赛好难看,除了抢了一个签到题的一血,又是跟去年差不多的难看。
感觉主要还是练的不够多,一个一眼dp题没有看出来,还有一个找规律题找了一整场,从开局2分钟抢一血到出第二个题隔了200分钟(Galigaygay)
总结起来就是:还是不够熟练啊!!!
E题
题意是一个游戏有n个回合,每次给出一组数,每组数里面有3个数字,ai,bi,ci我们每次可以从每组数里选出一个作为buff 此回合获得的成就值是(1+∑ai)(1 + ∑bi) (1 + ∑ci),但是每个buff只能保留三个回合,求最后获得的最大成就值是多少。

其实由于每个buff只能保留两个回合,我们只要记前两行的状态就行了,dp[i][j][k][l]表示第i个回合选了l buff,第i-1个回合选了k buff ,第i-2回合选j buff的最大值。然后就跟着状态随便推推就能得到转移方程了。

#include<cmath>
#include<algorithm>
#include<cstring>
#include<string>
#include<set>
#include<map>
#include<time.h>
#include<cstdio>
#include<vector>
#include<list>
#include<stack>
#include<queue>
#include<iostream>
using namespace std;
#define  LONG long long
const int   INF=0x3f3f3f3f;
const LONG  MOD=1e9+ 7;
const double PI=acos(-1.0);
#define clrI(x) memset(x,-1,sizeof(x))
#define clr0(x) memset(x,0,sizeof x)
#define clr1(x) memset(x,INF,sizeof x)
#define clr2(x) memset(x,-INF,sizeof x)
#define EPS 1e-10
#define lson  l , mid , rt<< 1
#define rson  mid + 1 ,r , (rt<<1)+1
#define root 1, m , 1
double dp[10010][4][4][4] ;
double num[10010][5] ;
int main()
{
    int n ;
    int sta = clock() ;
    while(cin>> n)
    {
        clr0(dp) ;
        for(int i = 1 ; i <= n ;++ i)
            for(int j = 1; j <= 3 ;++ j)
                scanf("%lf",&num[i][j]) ;
        dp[1][1][1][1] = 1 + num[1][1] ;
        dp[1][1][1][2] = 1 + num[1][2] ;
        dp[1][1][1][3] = 1 + num[1][3] ;
        for(int i = 1; i <=3 ;++i)
            for(int j = 1; j <= 3 ;++ j)
                if(i == j)
                dp[2][1][i][j] = max(dp[2][1][i][j] , dp[1][1][1][i] +  (1 + num[1][i] + num[2][j]) ) ;
                else
                dp[2][1][i][j] = max(dp[2][1][i][j] , dp[1][1][1][i] + (1 + num[1][i]) * (1 + num[2][j]) ) ;
        for( int x = 3 ; x <= n ;++ x )
        for(int i = 1; i <= 3 ;++i)
            for(int j =1; j <= 3 ; ++j)
                for(int k =1; k <=3 ;++ k)
                    for(int l =1; l <= 3 ;++ l)
                    if(i == j && j == k)
                    dp[x][i][j][k] = max(dp[x][i][j][k] , dp[x - 1][l][i][j] + ( 1 + num[x- 2][i] + num[x - 1][j] + num[x][k] )  ) ;
                    else if( i == j)
                    dp[x][i][j][k] = max(dp[x][i][j][k] , dp[x - 1][l][i][j] + ( 1 + num[x- 2][i] + num[x - 1][j] ) * (1 + num[x][k] )  ) ;
                    else if( j == k)
                    dp[x][i][j][k] = max(dp[x][i][j][k] , dp[x - 1][l][i][j]  + ( 1 + num[x - 2][i] ) * (1 + num[x - 1][j] + num[x][k] )  ) ;
                    else if(i == k)
                    dp[x][i][j][k] = max(dp[x][i][j][k] , dp[x - 1][l][i][j]  + ( 1 + num[x - 2][i] + num[x ][k] ) * ( 1 + num[x - 1][j] )  ) ;
                    else
                    dp[x][i][j][k] = max(dp[x][i][j][k] , dp[x - 1][l][i][j]  + ( 1 + num[x- 2][i] )* (1 + num[x - 1][j] ) * (1 + num[x][k] )  ) ;
    double ans =  0 ;
    for(int i = 1; i <=3 ;++i)
        for(int j =1 ; j <= 3 ; ++ j)
            for(int k = 1; k <= 3 ;++ k)
                ans = max(ans , dp[n][i][j][k]);
    printf("%.7lf\n",ans);
    }
}

F题

题意是给一棵树,每个节点有点权,初始点权是0,然后有K次操作,每次操作都是把从u点到v点这条路径上的点的权值加上一个数字,最后找出所有点中,权值最大的点。
树链剖分裸题,但是点和操作都有50万,因此毫无人性的卡了log,实际上这应该是一道树上dp题
类似于poj3417
我们可以先离线求一下所有操作的LCA,然后每次操作的点u,v加上这个数值,同时把lca(u,v)和father[lca(u,v)]减去这个数,这样dfs一次之后,每个点被加的数字都会被统计到dp[u]里进去,这样总的复杂度就是O(N+K)了

#include<cmath>
#include<algorithm>
#include<cstring>
#include<string>
#include<set>
#include<map>
#include<time.h>
#include<cstdio>
#include<vector>
#include<list>
#include<stack>
#include<queue>
#include<iostream>
using namespace std;
#define  LONG long long
const int   INF=0x3f3f3f3f;
const LONG  MOD=1e9+ 7;
const double PI=acos(-1.0);
#define clrI(x) memset(x,-1,sizeof(x))
#define clr0(x) memset(x,0,sizeof x)
#define clr1(x) memset(x,INF,sizeof x)
#define clr2(x) memset(x,-INF,sizeof x)
#define EPS 1e-10
#define lson  l , mid , rt<< 1
#define rson  mid + 1 ,r , (rt<<1)+1
#define root 1, m , 1
struct Edge
{
    int to , next ;
}edge[1100000];
int head[510000] ;
struct Query
{
    int to , next , id ;
}query[1200000];
int Qhead[1100000] ;
int tot , tt ;
LONG  dp[1200000] ;
void Add_Edge(int u ,int v )
{
    edge[++tot].to = v ;
    edge[tot].next = head[u] ;
    head[u] = tot ;
}
void Add_Q(int u , int v , int id )
{
    query[++tt].to = v;
    query[tt].id = id ;
    query[tt].next = Qhead[u] ;
    Qhead[u] = tt ;
}
int vis[550000] ;
int take[550000] ;
int fa[550000] ;
int Fa[550000] ;
LONG val[550000] ;
void Init()
{
    clr0( dp ) ;
    clrI(head) ;
    clrI(Qhead) ;
    tot = 0 ;
    tt = 0 ;
    clr0(val) ;
    clr0(vis) ;
    clrI(fa) ;
    clr0(take) ;
}
int Find(int x)
{
    if(fa[x] ==  -1) return x;
    return fa[x] == x ? x : fa[x] = Find(fa[x]) ;
}
void dfs1(int pre , int u)
{
    Fa[u] = pre ;
    for(int i = head[u] ; i != -1 ; i = edge[i].next)
    {
        int v = edge[i].to ;
        if(v == pre) continue ;
        dfs1(u , v) ;
    }
}
void dfs( int u)
{
    vis[u] = 1;
    for(int i = head[u]; i != -1 ; i = edge[i].next)
    {
        int v = edge[i].to ;
        if(vis[v] ) continue  ;
        dfs(v) ;
        fa[v] = u ;
        dp[u] += dp[v] ;
    }
    for(int i = Qhead[u] ; i != -1 ; i= query[i].next)
    {
        int v = query[i].to ;
        dp[u] += val[query[i].id ]  ;
        if( vis[v] )
        {
            if(!take[query[i].id])
            {
                dp[Find(v)] -= val[query[i].id] ;
                take[query[i].id] = 1;
                dp[Fa[Find(v)]] -= val [query[i].id] ;
            }
        }
    }
}
int main()
{
    Init() ;
    int n , m ;
    cin>> n >> m ;
    int  u , v ;
    for(int i =1; i < n ; ++ i)
    {
        scanf("%d%d" , & u, & v) ;
        Add_Edge(u , v) ;
        Add_Edge(v,  u) ;
    }
    LONG  w ;
    dfs1(-1 , 1 );
    for(int i =  1; i <= m ;++ i )
    {
        scanf("%d%d%lld",&u,&v, &w) ;
        val[i] = w;
        Add_Q(u , v , i ) ;
        Add_Q(v , u , i ) ;
    }
    dfs(1) ;
    int x = 1;
    for(int i = 1 ; i <= n ;++ i)
    {
//        printf("%lld ",dp[i]);
        if(dp[i] > dp[x])
        {
            x = i ;
        }
    }
//    cout<<endl;
    printf("%d %lld\n",x,dp[x]) ;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值