[关闭]
@AkamemakA 2022-08-20T00:33:19.000000Z 字数 2264 阅读 141

NOIP选讲


P2822 [NOIP2016 提高组] 组合数问题


给定两个整数和一个整数,求对于所有的二元组中,有多少对二元组满足组数据。
对于组数据永远不变。


首先一个十分简单的方法:枚举所有,计算,并判断。
这种做法对于的时候绰绰有余,但我们发现,因此,我们不得不来想一些优化。

我们要解决的就是如何优化掉的答案统计。由于对于所有数据是不变的,因此我们可以预处理出的所有组合数。然后我们可以用前缀和统计出答案数组。若,则。然后用前缀和统计数组。最有求答案就是的,直接输出即可。

另外就是的话,组合数算出来会非常大,根本就存不下。因此,我们不能用直接阶乘算组合数的方法(会有除法操作,不好取模)。这里就要用到另一种高级的组合数求法:组合数递推求法:

这样,取模操作就很好完成,此题也完美解决了。


P1314 [NOIP2011 提高组] 聪明的质监员

一个质监员要对一批货物进行质监。一共个矿石,每个矿石有两个属性,表示矿石的重量以及价值。质监流程如下:

最后这批矿产的总质监值为

另外给出一个参数,求一个参数,使得最小。求出这个最小值。


不难想到二分。对于每一个值,我们枚举暴力求出值。

然后就来想一想如何优化这个暴力。

看到这个符号,你能想到什么吗?

对了!那就是前缀和

我们对于每个mid,求出部分与的前缀和
然后对于每一个区间,直接便可以求出值:

然后对于这两个数组的求法也非常简单。判断是否。若是,则

若否,则

最后求出答案即可。


P1073 [NOIP2009 提高组] 最优贸易

一张边的有向图,一个商人会从点走到点。这个商人会利用不同城市之间物价不同来赚取差价。每个城市的物品价格为。商人会在某个城市买入物品,并在另一个城市卖出。这样他会赚得一些差价。一个点可以被多次通过,且不要求走完所有城市。问这个商人从点走到点的过程中,进行一次买卖能获得的最大差价是多少?


由于一个点可以反复被经过,因此不难想到利用缩点。那么一个连通分量里的点,一定可以取到最大值和最小值,里面走成什么样子可以不关心。

缩完点后,我们需要知道每一个联通分量与点所在的联通分量所连成的路径中的最小值,记为。然后对于每一个连通分量,用该连通分量中的最大值减去,在所有值中取即为答案。

那么为了做到这一点,我们就要使用拓扑。拓扑过程中可以顺便将数组更新出来,并同步计算:

数组初始化为每个联通分量的最小值。

另外要注意的是,在进行更新时,不是每个点都可以进行更新。要判断从这个点能否走到点。这可以通过建反图并简单的就可以做到。代码如下:

  1. inline void dfs(int u){
  2. if(now[u]) return ;now[u]=1; // u点可以走到n点
  3. for(int i=fh[u];i;i=fne[i])
  4. dfs(fgo[i]);
  5. }

然后拓扑:

  1. inline void topsort(){
  2. int f[N];
  3. memset(f,0x3f,sizeof f);
  4. for(int i=1;i<=scc;i++){
  5. if(!degin[i]) q.push(i);
  6. f[i]=minn[i];//初始化f数组
  7. }
  8. while(!q.empty()){
  9. int u=q.front();q.pop();
  10. if(now[u])ans=max(ans,maxn[u]-f[u]); //可以到达n,才更新
  11. for(int i=rh[u];i;i=ne[i]){
  12. int v=go[i];
  13. f[v]=min(f[u],f[v]);
  14. if(!--degin[v]) q.push(v);
  15. }
  16. }
  17. cout<<ans;
  18. }

代码有点长,看这里:点我

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注