寒假集训day5

T1:修建牛棚
看 看 数 据 范 围 就 知 道 一 定 是 O ( n ) 的 , 仔 细 观 察 发 现 最 小 值 肯 定 是 关 键 点 取 中 位 数 的 时 候 看看数据范围就知道一定是O(n)的,仔细观察发现最小值肯定是关键点取中位数的时候 On
分 奇 偶 讨 论 一 下 中 位 数 在 哪 就 行 了 分奇偶讨论一下中位数在哪就行了

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const int N=1e5+5;
ll ans=0;
int n;
ll x[N],y[N];
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%lld%lld",&x[i],&y[i]);
	}
	if(n&1)
	  {
	ll sum1,sum2;
	sort(x+1,x+n+1);
	int ansn=(n+1)/2;
	sum1=0,sum2=0;
	for(int i=1;i<ansn;i++){
		sum1=1ll*(sum1+x[i]);
	  }
	  ans=1ll*(ans+1ll*(ansn-1)*x[ansn]-sum1);
	for(int i=ansn+1;i<=n;i++){
		sum2=1ll*(sum2+x[i]);
	}
	ans=1ll*(ans+sum2-1ll*(ansn-1)*x[ansn]);
	sort(y+1,y+n+1);
	sum1=0,sum2=0;
	for(int i=1;i<ansn;i++){
		sum1=1ll*(sum1+y[i]);
	  }
	  ans=1ll*(ans+1ll*(ansn-1)*y[ansn]-sum1);
	for(int i=ansn+1;i<=n;i++){
		sum2=1ll*(sum2+y[i]);
	}
	  ans=1ll*(ans+sum2-1ll*(ansn-1)*y[ansn]);
    }
    if((n%2==0)){
    	int ansn=n/2;
    	ll sum1=0,sum2=0;
    	sort(x+1,x+n+1);
    	for(int i=1;i<ansn;i++){
		sum1=1ll*(sum1+x[i]);
	    }
	    ans=1ll*(ans+1ll*(ansn-1)*x[ansn]-sum1);
	    for(int i=ansn+1;i<=n;i++){
		sum2=1ll*(sum2+x[i]);
	    }
	    ans=1ll*(ans+sum2-1ll*(ansn)*x[ansn]);
	    sum1=0,sum2=0;
		sort(y+1,y+n+1);
    	for(int i=1;i<ansn;i++){
		sum1=1ll*(sum1+y[i]);
	    }
	    ans=1ll*(ans+1ll*(ansn-1)*y[ansn]-sum1);
	    for(int i=ansn+1;i<=n;i++){
		sum2=1ll*(sum2+y[i]);
	    }
	    ans=1ll*(ans+sum2-1ll*(ansn)*y[ansn]);
    }
    printf("%lld",ans);
}

T2:卖奶牛
看 看 数 据 范 围 , 先 想 到 的 就 是 d p 了 , d p [ s ] [ j ] [ k ] 表 示 s 子 树 中 最 大 值 为 j , 最 小 值 为 k 的 方 案 数 , 但 始 终 不 知 道 怎 么 合 并 , 更 不 用 说 d p 优 化 了 看看数据范围,先想到的就是dp了,dp[s][j][k]表示s子树中最大值为j,最小值为k的方案数,但始终不知道怎么合并,更不用说dp优化了 dpdp[s][j][k]sjkdp
上 述 算 法 的 瓶 颈 在 于 最 大 值 和 最 小 值 都 不 知 道 , 所 以 不 妨 钦 定 一 个 最 大 值 , 为 了 保 证 不 重 复 , 我 们 只 算 大 的 包 含 小 的 方 案 , 相 同 则 只 算 编 号 较 小 的 方 案 上述算法的瓶颈在于最大值和最小值都不知道,所以不妨钦定一个最大值,为了保证不重复,我们只算大的包含小的方案,相同则只算编号较小的方案
即 先 对 v [ i ] 从 大 到 小 排 序 , 对 于 每 个 i , 做 一 次 d f s ( 即 钦 定 i 要 选 ) , 看 看 有 多 少 种 方 案 , 一 旦 遇 到 v [ i ] − 最 小 值 比 d 大 的 就 返 回 即先对v[i]从大到小排序,对于每个i,做一次dfs(即钦定i要选),看看有多少种方案,一旦遇到v[i]-最小值比d大的就返回 v[i]idfsi,v[i]d

注意点:v[i]相同时会重复计算,要特判

#include<bits/stdc++.h>
using namespace std;

const int N=2010,mod=1e9+7;
int n,d,fa[N],fv[N];
int cnt=0,head[N];
struct node1{
	int v,id;
}q1[N];
struct edge{
	int link,v;
}q[N<<1];
void put(int x,int y){
	q[++cnt].v=y;
	q[cnt].link=head[x];
	head[x]=cnt;
}
bool cmp(node1 x,node1 y){
	return x.v>y.v;
}
void dfs(int s,int fath){
	for(int i=head[s];i;i=q[i].link){
		int v=q[i].v;
		if(v==fath) continue;
		fa[v]=s;
		dfs(v,s);
	}
}
int mx,sum[N],hd;//
void solve(int rt,int fath){
	sum[rt]=1;
	for(int i=head[rt];i;i=q[i].link){
		int v=q[i].v;
		if(v==fath) continue;
		if(mx-fv[v]>d||fv[v]>mx||(fv[v]==mx&&v<hd)) continue;//mx-fv[c]>d是判断题目条件,f[v]>mx,(f[v]==mx&&v<hd)是判重
		solve(v,rt);
		sum[rt]=1ll*((sum[v]+1)%mod)*sum[rt]%mod;
	}
}
int main(){
	scanf("%d%d",&n,&d);
	for(int i=1;i<=n;i++){
		scanf("%d",&q1[i].v);
		fv[i]=q1[i].v;
		q1[i].id=i;
	}
	for(int i=1;i<n;i++){
		int u,v;
		scanf("%d%d",&u,&v);
		put(u,v),put(v,u);
	sort(q1+1,q1+n+1,cmp);
	dfs(q1[1].id,0);
	int ans=0;
	for(int i=1;i<=n;i++){
		int rt=q1[i].id;
		mx=q1[i].v;
		hd=rt;
		solve(rt,0);
		ans=1ll*(ans+sum[rt])%mod;
	}
	printf("%d",ans);
} 

T3:3D农场
上 来 一 看 便 是 最 小 生 成 树 , 但 是 无 论 怎 么 调 精 度 都 还 是 不 对 上来一看便是最小生成树,但是无论怎么调精度都还是不对 便
其 实 ∑ c o s t [ i ] [ j ] ∑ d i s [ i ] [ j ] , ( 其 中 d i s 表 第 i 个 点 与 第 j 个 点 连 边 的 距 离 , c o s t 表 花 费 ) , 像 这 种 带 分 母 的 式 子 , 其 实 最 好 用 0 / 1 分 数 规 划 , 即 二 分 出 一 个 值 , 判 断 是 否 合 法 其实\frac{\sum cost[i][j]}{\sum dis[i][j]},(其中dis表第i个点与第j个点连边的距离,cost表花费),像这种带分母的式子,其实最好用0/1分数规划,即二分出一个值,判断是否合法 dis[i][j]cost[i][j],(disijcost)0/1
但 出 题 人 专 门 卡 了 二 分 , 要 用 d i n k e l b a c h 但出题人专门卡了二分,要用dinkelbach dinkelbach

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值