Codeforces Round #622 (Div. 2) D. Happy New Year

Problem Link:CF 1313D
Solution:
 无语辽,看了一遍题想了半天都不会,看着 k ≤ 8 k \leq 8 k8总感觉是要状压,想了半天也压不了啊,一个点可能有非常多线段覆盖,所以一直想能不能贪心。后面看了官方题解发现不太对劲,回头仔细看了看题发现是数据保证每个点最多有 k k k条线段覆盖,而不是让你选每个点最多只能选 k k k条线段。。。
 害,知道了正确题意题解也相当明显了, k ≤ 8 k \leq 8 k8明显就是要让你暴力记录的,所以就直接 d p dp dp暴力选择线段就好了。
 大致的方向确定了,思考具体要怎么写。首先肯定是离散化坐标,统计答案的时候展开加上。

  • 设计 d p dp dp状态: d p [ i ] [ j ] dp[i][j] dp[i][j]表示到了第 i i i个点,选择线段的状态为 j j j的答案。
  • 考虑转移:枚举所有合法的状态(即还覆盖在当前点的线段),如果当前点线段数为奇数就可以加上贡献,记为 v a l val val v a l val val为当前端点和上一个端点的距离。再考虑哪些状态需要转移,先考虑当前点是左端点的时候(加入线段):对于每一个合法的状态,更新加入当前线段后的状态,取最大值。当前点是右端点也是类似的(删去线段):对于每一个拥有此线段的状态,更新去掉这个线段后的状态,取最大值,然后把拥有此线段的状态变为不合法状态。
  • 考虑转移之后发现每次的转移不是一定取决于后面的元素就是一定取决于前面的元素,所以直接把第一维压缩了就可。当然不压缩也可以,空间是足够的。

 考虑如何维护当前的线段。开一个数组用来记录每个左端点在二进制中的位置,这是为了右端点的时候删去相应的位置。那么如何知道当前的左端点应该放在哪里?其实也简单,放在当前最低位的 0 0 0就行了,这个可以用一个整数维护,也可以用一个 s e t set set维护。
 另外还有个细节,读入线段的时候把右端点加一,这样做是为了排序的时候对于相同位置的情况,左端点应该先处理。举个例子就一个点的情况,虽然左右端点相同,但显然应该先处理左端点。这样子计算答案的方式也要换一下,直接当前端点 − - 上一个端点就行了。
 这题真是挺难搞的,对我来说,搞了好久好久。虽然最后写出来的代码比较简单,但是8太好理解,尤其自己想完全不知道该怎么设计这个很明显的 D P DP DP

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define endl "\n"
#define fi first
#define se second
#define db double
#define gcd __gcd
#define pb push_back
#define mp make_pair
#define lowbit(x) (x & (-x))
#define PII  pair 
#define all(x) x.begin(), x.end()
#define debug(x) cout << #x << " = " << x << endl
#define rep(i, a, b) for(__typeof(b) i = a; i <= (b); i++)
#define Rep(i, a, b) for(__typeof(a) i = a; i >= (b); i--)
#define FAST ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
template<class T> inline T qmin(T a, T b) { return a < b ? a : b; }
template<class T> inline T qmax(T a, T b) { return a > b ? a : b; }
typedef long long ll;
typedef unsigned long long ull;
const db eps = 1e-9;
const db PI = acos(-1);
const int inf = 0x3f3f3f3f;
const int mod = 998244353;
const int maxn = (int)2e5 + 5;//remember to modify it, No RE or MLE 
const ll INF = 0x3f3f3f3f3f3f3f3f;
using namespace std;

int n, m, k;
PII p[maxn];
int tot = 0;
set<int> s;
int dp[1<<10], bit[1<<10], pos[maxn];

int lg[10];
void init(){
	rep(i, 0, 8) lg[i] = 1 << i;
}

int main()
{
	init();
	scanf("%d %d %d", &n, &m, &k);
	rep(i, 1, n){
		int l, r; scanf("%d %d", &l, &r);
		p[++tot] = mp(l, i);
		p[++tot] = mp(r + 1, -i);
	}
	sort(p + 1, p + tot + 1);
	rep(i, 0, k - 1) s.insert(i);
	rep(i, 0, lg[k]-1) dp[i] = -inf, bit[i] = bit[i>>1] ^ (i & 1);
	dp[0] = 0;
	int last = 0, now, x;
	rep(i, 1, tot){
		now = p[i].fi, x = p[i].se;
		rep(j, 0, lg[k]-1) dp[j] += bit[j] * (now - last);
		last = now;
		if(x > 0){
			pos[x] = *s.begin(); s.erase(pos[x]);
			rep(j, 0, lg[k]-1){
				if(j & lg[pos[x]]) dp[j] = qmax(dp[j], dp[j^lg[pos[x]]]);
			}
		} else {
			x = -x;
			s.insert(pos[x]);
			rep(j, 0, (1<<k)-1){
				if(j & lg[pos[x]]){
					dp[j^lg[pos[x]]] = qmax(dp[j^lg[pos[x]]], dp[j]), dp[j] = -inf;
				}
			}
		}
	}
	printf("%d\n", dp[0]);
	return 0;
}

你可能感兴趣的:(DP)