首页 > 其他分享 >NOIP2018 day1 题解

NOIP2018 day1 题解

时间:2022-09-24 21:57:03浏览次数:51  
标签:int 题解 mid NOIP2018 mp curres include day1 dp

心血来潮看了看18年的联赛,感觉自己进步很大= =

T1
对于一个“波”来说,显然需要的次数为最大的数(波峰),对于多个“波”,就每次记录一下从波底到波峰的高度差即可,这可以用差分简单实现

// by SkyRainWind
#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
#define mpr make_pair
#define debug() cerr<<"Madoka"<<endl
#define rep(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
#define pii pair<int,int>

using namespace std;

typedef long long LL;

const int inf = 1e9, INF = 0x3f3f3f3f, maxn=100005;

int a[maxn], n;

signed main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
	int ans = a[1];
	for(int i=2;i<=n;i++){
		if(a[i] > a[i-1]){
			ans += a[i] - a[i-1];
		}
	}
	printf("%d\n",ans);

	return 0;
}

T2
显然两种系统\(a,b\)等价当且仅当\(a\)中的数字都能被\(b\)中的数字通过线性组合得到,对1~25000的数跑一个类似于完全背包的dp即可

// by SkyRainWind
#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
#define mpr make_pair
#define debug() cerr<<"Madoka"<<endl
#define rep(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
#define pii pair<int,int>

using namespace std;

typedef long long LL;

const int inf = 1e9, INF = 0x3f3f3f3f;

int n;
int dp[25005],a[105];

void solve(){
	memset(dp,0,sizeof dp);
	scanf("%d",&n);
	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
	sort(a+1,a+n+1);
	int ans = 0;
	for(int i=1;i<=n;i++){
		if(dp[a[i]])continue;
		++ ans;
		dp[a[i]] = 1;
		for(int j=1;a[i]+j<=25000;j++)dp[a[i] + j] |= dp[j];
	}
	printf("%d\n",ans);
}

signed main(){
	int te;scanf("%d",&te);
	while(te --)solve();

	return 0;
}

T3
给一个带边权的树,要划分出\(m\)个边不相交的路径,使得路径权值的最小值最大
首先二分答案mid,考虑树形dp,需要维护一个当前结点为根的子树的答案,以及在这种情况下能够向上传的路径权值的最大值
设\(dp[i]\)表示以 i 为根的答案,\(mx[i]\)表示 i 结点的子树能向上传的路径权值的最大值(dfs时顺便加上(i,fa[i])的权值)
\(dp[i] = \sum dp[u] + C\),其中 C 需要我们将每个儿子向上传的权值两两配对,使权值达到mid,以及在答案最优时向上传的权值要最大
首先想到的是暴力枚举向上传的边,这里有一个精妙的实现:for(int i=-1;i<(int)mp.size();i++),这样就涵盖了恰好两两匹配的情况。注意(int)必加!!因为\(vector<>.size()\)是unsigned,不能判断负数,时间复杂度是非常不满的O(n^2logn),结合树的直径可以拿到90pts的好成绩
发现枚举向上传的边这一步是有单调性的,很明显如果上传了边权为w的边,则比w小的边也一定可以上传,于是可以二分上传的边,时间复杂度大概是两个log,可以过
注意vector a(100) 如果再push_back,a.size() 会变成 101

// by SkyRainWind
#include <cstdio>
#include <vector>
#include <set>
#include <cstring>
#include <iostream>
#include <algorithm>
#define mpr make_pair
#define debug() cerr<<"Yoshino"<<endl
#define rep(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
#define pii pair<int,int>

using namespace std;

typedef long long LL;

const int inf = 1e9, INF = 0x3f3f3f3f,maxn=50005;

int n,m;
vector<pii>g[maxn];
int dp[maxn], mx[maxn];

void dfs(int x,int lim,int fat = -1){
	if(~fat && g[x].size() == 1)return ;
	vector<int> mp;
	for(pii now : g[x]){
		int u = now.first, w = now.second;
		if(u == fat)continue;
		dfs(u, lim, x);
		mx[u] += w;
		if(mx[u] >= lim)++ dp[x];	// 自成一派 
		else mp.push_back(mx[u]);
		dp[x] += dp[u];
	}
	sort(mp.begin(),mp.end());
	int mpc = mp.size();
	int curid = -2, curmx = -inf;
	int le = 0, ri = mpc-1, rans;
	
	// 单独考虑全配对情况 
	int l=0;
	int r=mpc-1;
	int curres = 0;
	while(l < r){
		if(mp[l] + mp[r] >= lim)++l, -- r,++ curres;
		else ++ l;
	}
	if(curres > curmx)curmx = curres, curid = -1;
	else if(curres == curmx)curid = -1;
	
	// 二分上传哪条边 
	while(le <= ri){
		int mid = le + ri >> 1;
		int l = mid == 0 ? 1 : 0;
		int r = mid == mpc-1 ? mpc-2 : mpc-1;
		int curres = 0;
		while(l < r){
			if(l == mid){
				++l;continue;
			}
			if(r == mid){
				-- r;continue;
			}
			if(mp[l] + mp[r] >= lim)++l, -- r,++ curres;
			else ++ l;
		}
		if(curres > curmx)curmx = curres, curid = mid, le=mid+1;
		else if(curres == curmx)curid = mid, le=mid+1;
		else ri = mid-1;
	}
	
	if(curid == -2){	// 没有配对,直接取最大 
		mx[x] = mp[mp.size() - 1];
		return ;
	}
	if(curid == -1){	// 全配对 
		mx[x] = 0;
		dp[x] += curmx;
		return ;
	}
	mx[x] = mp[curid];	// 部分配对 
	dp[x] += curmx;
}

int check(int lim){
	for(int i=1;i<=n;i++)dp[i] = mx[i] = 0;
	dfs(1, lim);
	if(dp[1] >= m)return 1;
	return 0;
}

signed main(){
//	freopen("P5021_5.in","r",stdin);
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n-1;i++){
		int x,y,z;scanf("%d%d%d",&x,&y,&z);
		g[x].push_back(mpr(y,z)), g[y].push_back(mpr(x, z));
	}
	int l = 1, r = 1e9, ans;
	while(l <= r){
		int mid=l+r>>1;
		if(check(mid))l = mid+1, ans = mid;
		else r = mid-1;
	}
	printf("%d\n",ans);

	return 0;
}

标签:int,题解,mid,NOIP2018,mp,curres,include,day1,dp
From: https://www.cnblogs.com/SkyRainWind/p/16726623.html

相关文章

  • Day1:Markdown学习笔记
    Markdown学习笔记二级标题二级标题通过##+空格实现同理,三级标题为###+空格备注:最多进行到六级标题字体md主要字体为字体(斜体)字体(加粗)字体(斜体加粗)字体(......
  • SOJ1626 方珍 题解
    传送门题意给定一个\(n×n\)的方阵,其中第\(i\)行为\(A_{i,1},A_{i,2},...,A_{i,n}\)。对于\(A_i\)的所有区间,设\(f_i\)为其中第\(k_i\)大的\(mex\)值。给......
  • JOIG 2021/2022 F 题解
    链接题意:给定一张\(n\)个点,\(m\)条边的无向图(保证没有重边、自环)。边有两种,\(type=1\)时,经过后手中的数\(-1\);\(type=2\)时,经过后手中的数\(\div2\)下取整。给定......
  • CF1701E Text Editor 题解报告
    题意翻译给定两个字符串\(S,T\),初始时光标在串\(T\)尾部,你可以进行以下操作:\(\texttt{left}\):将光标向左移动一个字符,如光标在字符串最左侧则不移动。\(\texttt{ri......
  • CF Round 822 Div2 题解
    比赛链接A题SelectThreeSticks(签到)给定\(n\)根木棒,第\(i\)根木棒的长度为\(a_i\)。现在我们可以进行操作,每次操作选定一根木棒,将其长度增高或减少1。问至少需......
  • CF 教育场 135 题解
    比赛链接A题ColoredBalls:Revisited(签到)给定\(n\)种颜色的球,其中颜色\(i\)的球的数量是\(cnt_i\),保证\(\sum\limits_{i=1}^ncnt_i\)是奇数。在一次操作中,我......
  • Codeforces Round #822 Div.2 整场题解
    目前还有E,F没有更新。A.SelectThreeSticks直接对\(a\)排序,选出来的木棍一定是相邻三项,都往中间靠更优。B.Bright,Nice,Brilliant最优方案就是每一行第一个......
  • 牛客题解 装进肚子
    链接:https://ac.nowcoder.com/acm/problem/14721来源:牛客网题解作者岛田小雅这道题刚拿到手,就感觉是个很简单(事实证明并不是很简单)的贪心。虽然我不是很会判断贪心的......
  • pycharm打字卡顿问题解决
    问题描述:我在pycharm中使用的远程服务器中的环境,工程也是本机映射到远程环境中,在某次断网以后,再次使用就变得非常卡,具体现象就是我码字要等,整个pycharm就无法点击,过了5秒以......
  • P1347 排序 题解
    题干交了8次,下载了3个测点.....首先这个题,很容易想到用拓扑如果有“X$<$Y”,就建立一条从X到Y的有向边要考虑到,如果排序成立,必须满足入度为0的点只有一个并且出度为0的......