您的位置:首页 > 其它

[BZOJ 1407][NOI 2002]Savage(中国剩余定理+扩展欧几里得)

2015-04-27 10:02 573 查看

题目链接

http://www.lydsy.com/JudgeOnline/problem.php?id=1407

思路

由于此题里n n的范围很小,因此可以直接从小到大暴力枚举洞穴个数m m(注:此题并不满足二分的性质),那么问题转变为一个判定性问题:已知m m,问所有野人在它们的有生之年里是否会出现冲突。直接暴力枚举野人i i和j j,我们要求他们俩第一次相遇的时间x x,容易构造一个同余式(step i −step j )x≡pos j −pos i (modm) (step_i-step_j)x\equiv pos_j-pos_i(\mod m)

我们要求的就是这个同余式的最小解x min x_{min}

可以转换为下面的式子

(step i −step j )x+my=pos j −pos i (step_i-step_j)x+my=pos_j-pos_i

由于上面的x,y x,y的系数并不是两两互质,因此需要先对所有的系数和常数除以gcd gcd。可以通过扩展欧几里得找到一个(x 0 ,y 0 ) (x_0,y_0),显然step i −step j gcd(step i −step j ,m) x 0 +mgcd(step i −step j ,m) y 0 =pos j −pos i gcd(step i −step j ,m) \frac{step_i-step_j} {gcd(step_i-step_j,m)}x_0+\frac m {gcd(step_i-step_j,m)} y_0=\frac {pos_j-pos_i} {gcd(step_i-step_j,m)}

显然一个可行解是(gcd(step i −step j ,m)x 0 ,gcd(step i −step j ,m)y 0 ) (gcd(step_i-step_j,m)x_0,gcd(step_i-step_j,m)y_0)

x x的通解满足x=gcd(step i −step j ,m)x 0 +kmgcd(step i −step j ,m) x=gcd(step_i-step_j,m)x_0+k\frac m {gcd(step_i-step_j,m)},若最小的解x min ≤min{age i ,age j } x_{min}\leq \min \{ age_i,age_j\}则表明当前的m m不合法,若枚举完所有的点对(i,j) (i,j)都合法,则表明当前的解m m是合法的。

代码

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>

#define MAXN 80

using namespace std;

struct Node
{
int val; //键值
int weight; //权值
int frequence; //频率
}node[MAXN];

int n,K;

bool cmp(Node a,Node b)
{
return a.val<b.val;
}

int f[MAXN][MAXN][MAXN],stack[MAXN],top=0; //边界:f[i][i-1][w]=0,表示点i为根结点,下面无左儿子(i-1为根节点,下面无右儿子),显然f值为0
int sum[MAXN]; //频率的前缀和

int main()
{
scanf("%d%d",&n,&K);
for(int i=1;i<=n;i++)
scanf("%d",&node[i].val);
for(int i=1;i<=n;i++)
scanf("%d",&node[i].weight),stack[++top]=node[i].weight;
for(int i=1;i<=n;i++)
scanf("%d",&node[i].frequence);
sort(stack+1,stack+top+1);
for(int i=1;i<=n;i++)
node[i].weight=lower_bound(stack+1,stack+top+1,node[i].weight)-stack;
sort(node+1,node+n+1,cmp);
for(int i=1;i<=n;i++)
sum[i]=sum[i-1]+node[i].frequence;
memset(f,0x3f,sizeof(f));
for(int i=1;i<=n+1;i++)
for(int w=0;w<=n;w++)
f[i][i-1][w]=0;
for(int w=n;w>=1;w--)
for(int i=n;i>=1;i--)
for(int j=i;j<=n;j++)
for(int k=i;k<=j;k++) //枚举[i,j]这段区间的点对应的子树,该子树以点k作为根节点
{
f[i][j][w]=min(f[i][j][w],f[i][k-1][w]+f[k+1][j][w]+K+sum[j]-sum[i-1]); //k本来权值小于K,将其权值变为k
if(node[k].weight>=w) f[i][j][w]=min(f[i][j][w],f[i][k-1][node[k].weight]+f[k+1][j][node[k].weight]+sum[j]-sum[i-1]); //k的权值本身就大于等于K,因此可以不变
}
int ans=0x3f3f3f3f;
for(int w=0;w<=n;w++)
ans=min(ans,f[1]
[w]);
printf("%d\n",ans);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: