Test 2022.10.12

今天是紫黑专场

T1 GreedyChange

分析

说实话我并没有太搞懂这道黑题,要我解释的话也并不能太清楚地说出来,只是对着题解老老实实整理了一遍,迷迷糊糊地打出来,

大概就是对于贪心的算法想办法去构造一组hack数据,想办法对于当前的面值M,让dp可以从大小适中的数中选几个出来达成目标,然而贪心选了一个很大的(几乎接近M)数之后,必须花更多的代价去选数量更多的零碎面值,然后达成hack的效果

注意还需要用到一个学术论文里提到的一个结论来过

Code

#include<bits/stdc++.h>
#define re register
using namespace std;
int n,ans=-1;
int a[1000];
signed main()
{
scanf("%d",&n);
for(re int i=1;i<=n;++i)scanf("%d",&a[i]);
for(re int i=1;i<=n;++i)
{
for(re int j=i+1;j<=n;++j)
{
int ans1=0,ans2=1,S=a[i]-1,s;
for(re int k=i+1;k<=j;++k)
{
ans2=ans2+S/a[k];
S%=a[k];
}
s=S=a[i]+a[j]-1-S;
for(re int k=1;k<=n;++k)
{
ans1=ans1+S/a[k];
S%=a[k];
}
if((ans==-1||s<ans)&&ans2<ans1)ans=s;
}
}
printf("%d",ans);
return 0;
}

T2 qmh的美食盛宴

又是一道人类智慧题了,说起来比较麻烦,但是这是我第一道写完的非裸三分题,还是得好好写一写纪念一下

分析

很容易想到对于给出的两个矩阵,我们作差,得到一个差量矩阵,根据题意就可以在这一个矩阵上面对每一行或者每一列进行+1的操作了,先分析样例吧:

[111111111322211211]

对于这一组样例,我们写出来的差量矩阵是这样的(上减下或者下减上都可):

[211100100]

我们把每一次的操作记录在数组里面,对应列的操作记为x[j],行的操作记为y[i]。那么很容易写出样例的一组解

[x[]=2,1,1y[]=011]

为了方便理解,我们再写一个差量矩阵出来:

[123456789]

同样的可以写出来我们的操作数组

[x[]=1,2,3y[]=036]

Point1

显而易见的是,我们当前构造出来的答案就是:

j=1m|x[j]|+i=1n|y[i]|

那么如果通过当前的这组解不能构造出来一组可行解了,就证明一定无解吗?

Point2

显而易见的是,我们写出来的操作数组不一定就是当前的最优解,那么就像exgcd一样,我们考虑怎么通过当前的一组解构造出另外一组解。

很容易想到的是,我们对所有的x[j]+1,那么所有的y[i]必须1,这时候我们的答案就变成了

j=1m|x[j]+1|+i=1n|y[i]1|

这里的答案和变化前可能一样,也可能不一样,具体怎么样要涉及到初中的数学知识了,你可以这样把答案展开(这里把1泛化成k了)

f(k)=|x[1]+k|+|x[2]+k|+......+|x[m]+k|+|y[1]k|+|y[2]k|+......+|y[n]k|

对于所能够被构造出来的答案,我们一定都能这样变化它,很容易被证明的是,无论是我们构造出来的答案,还是最后的最优解,一定都是上式的一个特例,所以如果按照我们的构造方法没有答案,那就一定是无解了

很明显的就是一个零点讨论了,这里记操作变化值为k的时候答案为f(k)

你可以很清楚地知道在实数域(kR是非常显而易见的)上面,f(k)一定不是一个单调的函数,所以不能使用二分了,那么还有什么方法解决这道题呢?

Solution1:模拟退火

很明显的是,对于任意一个函数,退火都有找出正确答案的可能性,我们不知道这个函数是单峰还是多峰,但我们完全可以使用退火来求解,只是可能调参会有一些麻烦

Solution2:三分

言下之意就是我们需要证明这是一个单峰函数了

我们把以k为自变量画出来的零点分别投影到数轴上面:x[1],x[2]...x[m]......y[1],y[2]...y[n]

image

假设A,B,C,D,E,F,G,H是我们的零点,而I,K,J是我们的自变量所在的点,我们把零点按照坐标绝对值大小从小到大依次两两分组成若干个区间[A,H],[B,G],[C,F],[D,E],根据绝对值的几何意义,当一个点处于一个区间之内,它到两个端点的距离是不变的,所以当K,J处于[D,E]区间时,他们被所有区间包含,所以无论在这个区间内怎么移动,最后的答案都是不会变的。

现在我们开始把自变量所在的点往[D,E]之外的区间移动,比如我们现在的I点(可能被遮住了,反正是蓝色的线)所在的位置,明显的看到,I[A,H],[B,G],[C,F]的距离是没有发生变化的,但是到[D,E]的距离变远了,所以一定不如刚刚被所有区间包括的情况优秀,同理可证,当这个点越远离最中间的区间,最后的答案就会越来越大,向左和向右移动都是如此(显而易见),所以最后画出来的图像应该是这样的:
image

注意到这里[D,E]的答案是不变的,图中斜率也相对表示出来了

有人会问:如果零点个数是奇数个,是不是就不能这么做了?

实际上也是很简单的,我们可以把排序后,所有零点的中位数当成两个点,组成区间时把它当成一个[MIDNUM,MIDNUM]的长度为0的区间,那么最后的答案就必须在这个点上面了

这种单峰函数就可以用三分来解决了。

Solution3:直接排序

实际上我们或许用不到三分这种算法,如我们刚刚提到,把所有零点排序后的中间那个点取出(或者是中间那个区间),我们最优的答案

就是取出的点(或者是区间),言下之意,我们只需要用一个排序统计所有零点就行了,最后再算一下所有区间的长度和。

Code

T3 恶魔之树

听说是一个特别恐怖的状压,我打了个基于质因数分解的20pts搜索就跑路了,当然还可以特判全部相同的点,这个时候答案为:

ans=1+a[1]×i=1nCni=a[1]×(2n1)+1

Code

T4 大魔法师

题意

简单来说给出一个区间,每个区间内的元素有A,B,C三种值,给出七种操作:

opt1:Ai=Ai+Biopt2:Bi=Bi+Ciopt3:Ci=Ci+Ai,i[l,r]opt4:Ai=Ai+vopt5:Bi=Bi×vopt6;Ci=vopt7:[l,r]A,B,C

分析

说实话要我来写纯线段树的话我肯定写不出来,因为光加一个乘法,线段树的操作就多了一大堆,更别说这么多tag

我想不到,但有人能想到用线段树套矩阵

对于每一个元素,我们定义一个向量

[AiBiCi1]

写出对应的转移矩阵:

opt1:[1000110000100001]opt2:[1000010001100001]opt3:[1010010000100001]opt4:[100001000010V001]opt5:[10000V0000100001]opt6:[10000100000000V1]

剩下的就是线段树区间修改的板子了,注意这里矩阵乘法之所以正确,是因为矩阵乘法是有结合律的,就像我们的lazytag一样,对区间进行延时操作

Code

posted @   Hanggoash  阅读(5)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
动态线条
动态线条end
点击右上角即可分享
微信分享提示