南邮新生赛题解

博客介绍了南邮新生赛的几道题目解法,包括使用树形DP和贪心策略解决的A题,通过转化序列求最长非减子序列的B题,以及涉及数位DP和逆序对计算的D题。博主提供了AC代码,并评论题目结合了脑洞和算法,适合练习提高。
摘要由CSDN通过智能技术生成

话说题目好像有点难,好吧就是我的锅。

下面主要是一点提示和我自己的ac代码。


A:这是一道树形dp(勉强这么说吧)。

贪心的思路如下,如果一颗子树经过某些操作可以使其变成全0,那么无论以怎样的顺序执行这些操作,都可以使其达到全0状态。

记add[i],cut[i]为对于以i为根的子树达成全零需要加,减的操作数

那么,对于某个节点u,先递推求解子节点的add和cut,u节点的add值就是子节点add值得最大值,cut值同样

再用u本身的值+add-cut,这就是使u的子节点的子树全0后u的状态,如果这个值大于0,cut值再加上它,否则add值加上他的绝对值

具体见代码

(题目评价:树形dp+贪心思想)

<span style="font-size:14px;">#include <iostream>
#include <stdio.h>
#include <vector>
#include <queue>
#include <cstring>
#include <algorithm>
#include <assert.h>
using namespace std;
typedef long long ll;

const int maxn = 100005;

struct edge {
	int v,nex;
}e[maxn*2];

int ee,p[maxn],fa[maxn];
void addedge (int u,int v) {
	e[ee].v=v;e[ee].nex=p[u];p[u]=ee++;
	e[ee].v=u;e[ee].nex=p[v];p[v]=ee++;
}
ll add[maxn],cut[maxn],val[maxn];
void dfs(int u) {
	int v;add[u]=0;cut[u]=0;
	for(v=p[u];~v;v=e[v].nex) {
		if(e[v].v==fa[u]) continue;
		fa[e[v].v]=u;dfs(e[v].v);
		add[u]=max(add[u],add[e[v].v]);
		cut[u]=max(cut[u],cut[e[v].v]);
	}
	val[u]+=add[u]-cut[u];
	if(val[u]>=0) cut[u]+=val[u];
	else add[u]+=val[u]*-1;
}

int main() 
{
//	freopen("aa.txt","r",stdin);
	int i,n,x,y;
	scanf("%d",&n);
	ee=0;memset(p,-1,sizeof(p));
	for(i=1;i<n;i++) {
		scanf("%d%d",&x,&y);
		addedge(x,y);
	}
	for(i=1;i<=n;i++) scanf("%I64d",&val[i]);
	dfs(1);
	cout<<add[1]+cut[1]<<endl;
}</span>


B:百度之星的一题

题目要求严格递增,如果只要求非减,只要直接dp出最长非减子序列。。。

那么这题怎么写?

考虑对于每一个值,让他减去自身的序号,这样得到一个新的序列。

对于新的序列,求出他的最长非减子序列,那么只要保留这个子序列,改变其余值即可满足题意。

代码是很久之前写的,写了一个很挫的二分,其实用upper_bound即可

<span style="font-size:14px;">#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <string>
#include <cstdlib>
#include <vector>
#include <set>
#include <map>   
using namespace std;     

int dp[(int)1e5+5],a[(int)1e5+5],leng;   //dp[i]位长度为i的非减子序列最小的末尾值

int find(int len,int n)
{  
    int left=0,right=len,mid=(left+right)/2;  
    while(left<=right)  
    {  
        if(n>dp[mid]) left=mid+1;  
        else if(n<dp[mid]) { right=mid;  if(right==left) break;}
        else { while(n==dp[mid]) mid++; return mid; }
        mid=(left+right)/2;  
    }  
    return left;    
}  

int main()
{
    int T,cas;
    scanf("%d",&T);
    dp[0]=-9999999;
    for(cas=1;cas<=T;cas++) {
        leng=0;
        int n,i,j;
        scanf("%d",&n);
        for(i=1;i<=n;i++) {
            scanf("%d",&a[i]);
            a[i]=a[i]-i;
            dp[i]=9999999;
        }
        for(i=1;i<=n;i++) {
            j=find(leng+1,a[i]);     //j=upper_bound(dp,dp+leng+1,a[i])-dp;
            dp[j]=min(dp[j],a[i]);
            leng=max(leng,j);      // leng为已经求得最长的子序列长度
        }
        leng=n-leng;
        cout<<"Case #"<<cas<<":"<<endl<<leng<<endl;
    }
}</span>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值