P6146 [USACO20FEB]Help Yourself G

[USACO20FEB]Help Yourself G - 洛谷

算法标签:线性DP(迭代思想)

难度:普及+/提高

先将所有线段按左端点升序排序。

设 f_i 表示前 i 条线段的所有子集的复杂度之和。

如果我们新添加了一条线段,复杂度会怎样变化呢?

1.不选这条线段。

这种情况下,复杂度没有变化,不包含这条线段的子集的复杂度仍然为  f_i

2.选这条线段。

复杂度分两部分:原来的复杂度(这部分不会因为新选一条线段而减少,因为线段已经按左端点排好顺序了)和新增加的复杂度(这条线段可能不与已有线段形成连通块)。

原来的复杂度仍然为 2^{x},而选这条线段可能会让部分子集的复杂度+1

如果之前的线段中有 x 条线段不与当前线段相交,则选这 x 条线段的一个子集加上当前线段可以让复杂度在原来子集的复杂度基础上+1

根据集合的知识,新增加的复杂度就是2^x

从而得到递推式:f_i=f_i-1+(f_i-1+2^x)=2 f_i-1+2^x

现在的问题就是计算 x

容易看出,设第 i 条线段的左端点为 l_i,右端点为 r_i,则 x 等于右端点小于 r_i 的线段数量。

我们可以利用前缀和技巧来预处理所有 x 的值。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std;
#define N 100010
const long long mod=1e9+7;
int n,s[2*N];
long long f[N];
struct Node
{
	int l,r;
}a[N];

bool cmp (Node p,Node q)
{
	return p.l<q.l;
}

void Init ()
{
	scanf ("%d",&n);
	for (int i=1;i<=n;i++)
	{
		scanf ("%d %d",&a[i].l,&a[i].r);
		s[a[i].r]++;
	}
}

long long pow2 (int x)
{
	long long ans=1,k=2;
	while (x>0)
	{
		if (x&1)
			ans=ans*k%mod;
		k=k*k%mod;
		x>>=1;
	}
	return ans;
}

void Work () 
{
	sort (a+1,a+1+n,cmp);
	for (int i=1;i<=2*n;i++)
		s[i]+=s[i-1];
	for (int i=1;i<=n;i++)
		f[i]=(2*f[i-1]%mod+pow2 (s[a[i].l-1]))%mod;
	printf ("%lld\n",f[n]);
}

int main ()
{
	Init ();
	Work ();
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值