首页 > 其他分享 >DTOJ 6316 沙丘 题解

DTOJ 6316 沙丘 题解

时间:2022-11-13 18:55:44浏览次数:61  
标签:ch frac gcd int 题解 sum 沙丘 6316 DTOJ

DTOJ 6316 沙丘 题解

题面:

http://59.61.75.5:8018/p/P6316

在满天的星光下,灰大狼一人孤独地堆起了小沙丘。有 \(n\) 堆沙丘,每堆沙丘有相对高度 \(h_i\),每次灰大狼可以选择一段连续的沙丘并将它们高度增加或减少 \(a\) 或 \(b\),其中 \(a,b\) 给定,灰大狼想知道最少要几次才能把所有沙丘的相对高度都变成 \(0\)。如果无解请输出 -1

题解

首先看到是 “一段连续的沙丘”,果断考虑差分.

差分完增加或减小 \(a\) 或 \(b\) 可以被转化成两个位置上分别 \(+a, -a\) 或 \(+b, -b\)

最后每个位置上一定是 \(xa+yb\) 的形式,记差分数组为 \(\{d_i\}\),则 \(xa+yb=d_i\)

显然是可以 exgcd 求出一组特解 \(x_0,y_0\)

于是通解可以表示成 \(\begin{cases}x=x_0+\frac{b}{\gcd(a,b)}\cdot k\\ y=y_0-\frac{a}{\gcd(a,b)}\cdot k\end{cases}\)

最后答案即 \(\frac{1}{2}\sum(|x|+|y|)\)

测试的时候我直接调整每个 \(x,y\) 使 \(|x|+|y|\) 最小.

\(|x|+|y|=|x_0+\frac{b}{\gcd(a,b)}\cdot k|+ |y_0-\frac{a}{\gcd(a,b)}\cdot k|\)

根据高一数学,这一定是三个一次函数组成的分段函数.

于是最小值一定只有四种情况, \(x\) 最小正值,\(x\) 最大负值, \(y\) 最小正值,\(y\) 最大负值.

但是我们发现这么做有个问题,就是差分数组要满足 \(\sum x = 0\) 且 \(\sum y = 0\)

上次数竞比赛也是没注意到限制条件少了50分ww

考虑继续调整,注意到 \(\sum x =0 \Rightarrow \sum y =0\) 所以只需要调整 \(\sum x = 0\)

不妨设 \(\sum x > 0\) 我们对于一个位置 \(i\),若 \(\sum x\) 变化 \(1\), 则 \(|x|+|y|\) 变化 \(|x-\frac{b}{gcd(a,b)}|-|x|\)

把所有的变化存进堆里,从小到大取出来.

嗯这就做完了 \((\and \omega\and)\)

代码好难写(码力不够

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e5+5;
int rd()
{
	int x,f=0; char ch;
	while(!isdigit(ch=getchar())) if(ch=='-') f=1;
	for(x=(ch^48); isdigit(ch=getchar()); x=(x<<1)+(x<<3)+(ch^48));
	return f?-x:x;
}
int n,a,b,c[N],x[N],y[N];
priority_queue<pair<int,int> > Q;
int exgcd(int a,int b,int &x,int &y)
{
	if(!b) { x=1,y=0; return a; }
	int d=exgcd(b,a%b,y,x); y-=(a/b)*x;
	return d;
}
int main() 
{
	n=rd(),a=rd(),b=rd();
	for(int i=1; i<=n; i++) c[i]=rd();
	for(int i=++n; i; --i) c[i]=c[i]-c[i-1];
	int tx,ty,d=exgcd(a,b,tx,ty); a/=d,b/=d;
	for(int i=1; i<=n; i++)
	{
		if(c[i]%d) { puts("-1"); return 0; }
		int X=((ll)tx*c[i]/d%b+b)%b,Y=(c[i]/d-(ll)a*X)/b;
		x[i]=X,y[i]=Y;
		X-=b,Y+=a;
		if(abs(X)+abs(Y)<abs(x[i])+abs(y[i])) x[i]=X,y[i]=Y;
		Y=((ll)ty*c[i]/d%a+a)%a,X=(c[i]/d-(ll)b*Y)/a;
		if(abs(X)+abs(Y)<abs(x[i])+abs(y[i])) x[i]=X,y[i]=Y;
		Y-=a,X+=b;
		if(abs(X)+abs(Y)<abs(x[i])+abs(y[i])) x[i]=X,y[i]=Y;
	}
	ll s=0;
	for(int i=1; i<=n; i++) s+=x[i];
	s/=b;
	if(s<0) s=-s,swap(a,b),swap(x,y);
	auto f = [&](const int &p){ return abs(x[p]-b)+abs(y[p]+a)-abs(x[p])-abs(y[p]); };
	for(int i=1; i<=n; i++) Q.push(make_pair(-f(i),i));
	while(s--)
	{
		int p=Q.top().second; Q.pop();
		x[p]-=b,y[p]+=a;
		Q.push(make_pair(-f(p),p));
	}
	ll ans=0;
	for(int i=1; i<=n; i++) ans+=abs(x[i])+abs(y[i]);
	printf("%lld\n",ans/2);
	return 0;
}

标签:ch,frac,gcd,int,题解,sum,沙丘,6316,DTOJ
From: https://www.cnblogs.com/copper-carbonate/p/16886594.html

相关文章

  • CF1748E Yet Another Array Counting Problem 题解
    可能更好的阅读体验题目传送门题目大意给定一个长度为\(n\)的序列\(a\)和\(m\),定义\([l,r]\)的最左边的最大值为最小的\(l\lei\ler\)满足\(x_i=\max\{a_......
  • CF1748D ConstructOR 题解
    可能更好的阅读体验题目传送门题目大意给定\(a,b,d\),要求找到一个\(0\lex\le2^{60}\),满足\(a|x,b|x\)都是\(d\)的倍数(\(|\)代表按位或)。\(T\le10^4\),\(0\le......
  • 能力提升综合题单-模拟,前缀和差分 题解
    好久没有写题解了,今天回来了!!A-铺地毯在luogu,享受coding的快乐见到题以后,一道水题直接模拟二位数组。。。《真·ACcode》:点击查看代码#include<bits/stdc++.h>u......
  • CF1746D题解
    很好的一道贪心题。首先对于每条路径,由于要最大化权值,每条路径肯定要延伸到叶子节点。切入点肯定在\(|c_u-c_v|\leq1\),也就是说由节点\(i\)延伸下去的路径要均匀分配......
  • DTOJ 2022.11.02 图论专题
    题单P1117无序字母对P5240「NOIP2020」排水系统P4042「NOIP2018」旅行P5169「CSP-S2020」函数调用P4563「NOIP2017」逛公园题解A题面:给定\(n\)个各不相同的......
  • [CEOI2016] kangaroo题解
    P5999[CEOI2016]kangaroo一类插入式的dp。对于这道题,我们得先做出一个转化,依次考虑每个数插到哪个位置,于是变成了求\(1\)~\(n\)的排列同时满足每个位置上的元素要么......
  • P1858 多人背包 题解
    本题解灵感来源于题解P1858【多人背包】sto顾zorz本篇题解仅仅是对该题解的解释和说明。主要对原题解的解析部分加以补充:该文章中刷表的地方,是通过两个值去更新新......
  • 【题解】CSP-S2022 T2策略游戏
    简要题意有两串数A[1 n],B[1 m]A[1 n],B[1 m],有两个人小L和小QL和小Q,给出q组l1,r1,l2,r2q组l1,r1,l2,r2,对于每组,小L在A[l1 r1]A[l1 r1]中取一数x,小Q在B[l2 r2]B[l2......
  • Codeforces Round #833 (Div. 2) A-C题解
    比赛链接A、手摸不难发现,能做出的正方形大小就是当前的最大长度。所以直接输出向上取整即可。点击查看代码#include<bits/stdc++.h>usingnamespacestd;#defineN......
  • CSP-J2022题解
    CSP-J2022题解T1乘方思路非常简单,直接for循环上就行了。为什么不会炸呢?因为就算a=1e9,乘两次也炸不了longlong。代码#include<cstdio>longlonga,n,ans=1;intmai......