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]) ;
}