Codeforces Round #701(div2 A~E) 补题

链接

http://codeforces.com/contest/1485

A. Add and Divide

(rating:1000)
题意:
给两个数a,b;可以进行如下两个操作:
1) a = [a/b] (除法,下取整)
2) b = b+1;
要求用最小的操作次数使a = 0;
思路:
当b=1时,必须先+1;
算出只做操作1,也就是a一直除以b的情况,得到一个操作数ans。
由于时间复杂度允许,那么最简单的处理方法就是遍历 b —> b+ans ,取最小操作数。

B. Replace and Keep Sorted

(rating:1200)
题意:
给一段长度为n的递增数组 a1 , a2 , a3 … an 和 一个上界k 。
再给一个询问次数 q,每次询问给定a数组的一个区间 [l,r] , 要求统计与给定区间满足k-similar的总数组个数。
对于构造出的数组,规定如果它与数组a的[l,r]区间满足以下4点:
1)满足严格单增 。
2)长度相同
3)所有的数都在1~k之间
4)有且仅有1处不同。
就称之为k-silimar数组。
思路:
一开始写根本没看数据范围导致T了一发23333。 (n,q <= 1e5 )
由于只能改变一处,且为了满足单调递增 , 那么我们枚举数组a 在 [l,r]上的每一个元素 , 每个元素可以替换成 a[i-1]~a[i+1] 之间的任何值,求下和即可 。

但是由于时间复杂度限制,我们不能去枚举区间每个点,否则时间是O(n*q)
既然要o(n)解决 , 那么做下预处理 。 sum[i]表示区间[1,i]的k-similar个数。
每次询问时,对于区间[l,r] ,如果改变的位置是 [l+1,r-1] , 那么总个数就是sum[r-1] - sum[l] 。
如果改变的是端点l,r,那么显然,个数为(a[l]-1)+(k-a[r])+(a[l+1]-a[l]-1)+(a[r]-a[r-1]-1)) 。

代码段:

while(q--){
		ll l,r;
		scanf("%lld %lld",&l,&r);
		ll cnt = 0;
		if(l==r){
			printf("%lld\n",k-1);
		}
		else if(l+1==r){
			printf("%lld\n",k-2+a[r]-a[l]-1);
		}
		else{
			printf("%lld\n",sum[r-1]-sum[l]+(a[l]-1)+(k-a[r])+(a[l+1]-a[l]-1)+(a[r]-a[r-1]-1));
		}
	}

C. Floor and Mod(数论+分块问题)

(rating:1700)
在这里插入图片描述

题意:
给两个数x,y . 找出所有满足1<=a<=x , 1<=b<=y,使得[a/b] = a mod b 的个数 .
即 a除以b的商与余数相同 。 其中 x,y<=109
思路:
例如 , 当 b = 6且x很大时,显然满足条件的a有:
a = 7 , a =14 , a = 21,a = 28, a = 35 这5个 。

从这里我们可以发现几个问题:
1) 满足条件的a之间相差 b+1 .
2)对于b , 满足条件的a最多有 b-1个
3) 当 x >= b*b - 1 时 , 这b-1个都可以取到 。

那么,首先找到一个最大的b,使得b-1个都可以取到 。 (就叫mid吧)
总数 ans += 1+2+3+4+ … + mid-1 ;
对于mid < b < y的这些值 , 是无法取完 b-1 个的,从上面的问题1可以知道,此时的b可以取 [x/(b+1)]个 。 但是我们也不能一个一个地去遍历求每个mid<b<y的[x/(b+1)] 。 但是,对于mid<b<y来讲 , 总有一个连续的区间 [bi,bj] , 它们可以取到的a的个数是相同的 。
我们发现 , 对于 “能取到cnt个a” 的b ,最大的b就是取第cnt个时的a恰好离上界x最近的情况 。 也就是last = [x / cnt] - 1 ; 因此,当我们找到 “能取到cnt个a” 的第一个b时,就可以通过这样直接跳到“能取到cnt个a” 的最后一个b 。 ans += cnt * (last -first +1).

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
int main(){
	int t;
	cin>>t;
	while(t--){
		ll a,b;
		cin>>a>>b;
		a = min(a,b*b);   
		b = min(a,b);
		ll mid = 0;
		for(ll i=1;i<=b;i++){
			if((ll)i*i-(ll)1>a)
			break;
			else
			mid = i;
		}
		ll ans = 0;
		ans += (mid)*(mid-1)/2;
		mid++;
		while(mid<=b){
			ll cnt = a/(mid+1);
			if(cnt==0) break;
			ll last = min(a/cnt-1,b);
			ans += cnt*(last-mid+1);
			mid = last+1;
		}
		cout<<ans<<endl;
	}
}

D. Multiples and Power Differences(构造)

(rating:2200)
在这里插入图片描述

题意:
给一个n行m列的矩阵a (aij <= 16) , 要求构造出一个矩阵b,满足以下条件:
1)大小与a相同
2)bij是aij的倍数
3)b中每一对相邻元素之差的绝对值是某个数的四次方 。

思路:
构造的关键在于aij <= 16 :
我们可以取 lcm(1,2,3, … ,16 ) ,这个数等于 720720 ,保证bij一定是aij的倍数 。
对于相邻元素 , 必然有横纵坐标之和(即 i+j) 相差1。
那么,只需要令 当i +j 为奇数时 , bij = 720720; 当i+j 为偶数时 , bij = 720720 + a[i][j]4 即可 。

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll gcd(ll a,ll b){
	return b==0?a:gcd(b,a%b);
}
int main(){
	ll ans = 1;
	ll now = 16;
	for(int i=15;i>=1;i--){
		now = i*now / gcd(i,now); 
	}
	ll n,m;
	cin>>n>>m;
	ll x = 0;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			cin>>x;
			if((i+j)%2==0)
			cout<<now<<" ";
			else
			cout<<now+x*x*x*x<<" ";
		}
		cout<<endl; 
	}
	
} 

E. Move and Swap(树形DP)

(rating:2500)
在这里插入图片描述

题意:
给一棵根为1的树 , 树上每个节点都有一个权值 。
现在有红蓝两枚硬币从根节点开始往下跳 。
每一个步骤,红硬币只可以跳到当前节点的任意孩子节点 , 而蓝硬币可以跳到深度+1的任意节点。 这个步骤的得分 = | 红硬币所在节点的权值 - 蓝硬币所在节点的权值 | 。 当前步骤完成后 , 你可以选择交换红蓝硬币的位置(也可以不交换) 。 问最终得分的最大值是多少。

思路:
挺难的树形dp ,借着题解补了一下午 。
如果不能交换红蓝硬币的话题目会简单一些:
dp[u] = max(dp[v]) + max(abs(a[u] - a[u0])) , 因为蓝硬币没有限制 . 其中v是u的子节点 , u0是与u同深度的任意节点 ,下面的v0同理 ,是与v同一深度的任意节点 。
那么,如果此时让红蓝交换,就可以有:
dp[u] = max(dp[v0]) +max(abs(a[u] - a[u0])) 。

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll maxn = 2e5+5;
struct A{
	ll val;    //节点权值 
	ll id;     //节点编号 
	ll deep;	//节点深度 
}a[maxn];
struct E{
	ll to;
	ll nxt;
}e[maxn<<1];
ll head[maxn<<1],tot;
ll add_edge(ll u,ll v){
	e[++tot].to = v;
	e[tot].nxt = head[u];
	head[u] = tot;
}
ll max_son[maxn];   // i节点的权值最大儿子 
ll max_deep[maxn];  // i深度的权值最大点 
ll min_deep[maxn];  // i深度的权值最小点 
ll fa[maxn];
ll dp[maxn];
//dfs预处理 节点的深度和每个深度最大,最小的权值 
void dfs(ll u,ll f){
	fa[u] = f;
	if(max_deep[a[u].deep]<a[u].val)
	max_deep[a[u].deep] = a[u].val;
	if(min_deep[a[u].deep]>a[u].val)
	min_deep[a[u].deep] = a[u].val;
	for(int i=head[u];i;i=e[i].nxt){
		int v = e[i].to;
		if(v==f) continue;
		a[v].deep = a[u].deep + 1;
		dfs(v,u);
	} 
}
//按深度给节点排序 
bool cmp(A x,A y){
	return x.deep>y.deep;
}
//初始化 
void clear(ll n){
	tot = 0;
	for(int i=0;i<=n;i++)
	head[i] = 0;
}
int main(){
	int t;
	cin>>t;
	while(t--){
		ll n;
		ll maxx = -1e17,minn = -1e17;
		scanf("%lld",&n);
		//输入并初始化 
		for(int i=2;i<=n;i++){
			ll u,v = i;
			scanf("%lld",&u);
			add_edge(u,v);
			add_edge(v,u);
		}
		a[1].id = 1;a[1].val = 0;a[1].deep = 1;dp[1] = 0;max_deep[1] = 0;min_deep[1] = 1e9+7;max_son[1] = 0;
		for(int i=2;i<=n;i++){ 
			scanf("%lld",&a[i].val);
			a[i].id = i;
			a[i].deep = 1;
			min_deep[i] = 1e9+7 , max_deep[i] = 0 , max_son[i] = 0 , dp[i] = 0 , fa[i] = 0;
		}
		//预处理 
		dfs(1,0);
		 
		//按深度排序 ,从大到小 , dp【1】即结果 
		sort(a+1,a+n+1,cmp);
		
		ll now = 1;
		for(ll i=1;i<=n;i++){
			if(a[i].deep!=a[i-1].deep && i!=1){
				for(now;now<i;now++){
					dp[a[now].id] = max(dp[a[now].id],maxx-a[now].val);
					dp[a[now].id] = max(dp[a[now].id],minn+a[now].val);
				}
				now = i,maxx = minn = -1e17;
			}
			ll u = a[i].id;
			max_son[u] = 0;    //max_son 当前节点dp值最大的孩子节点 
			for(int z=head[u];z;z=e[z].nxt){
				int v=e[z].to;
				if(v==fa[u]) continue;
				max_son[u]=max(max_son[u],dp[v]);
			}
			ll dep = a[i].deep;
			// 当前节点的dp值 = 最大孩子的dp值 + max(当前深度节点权值绝对值)
			dp[u]=max_son[u]+max(max_deep[dep]-a[i].val,a[i].val-min_deep[dep]);
			maxx=max(max_son[u]+a[i].val,maxx);
			minn=max(max_son[u]-a[i].val,minn);
		}
		printf("%lld\n",dp[1]);
		clear(n);
	}
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值