zoukankan      html  css  js  c++  java
  • ZR1153

    ZR1153

    首先我们可以发现一个比较简单的容斥做法

    直接暴力枚举(2^m)个限制强制不合法,算贡献

    注意如果两个限制冲突那么答案为0

    直接暴力差分就好了

    这样就有了快乐的(30)分了

    接下来考虑对容斥进行DP

    把所有点区间按照右端点排序,如果出来两个颜色相同的区间一个包含了另外一个,那么大区间是没有用的,因为小区间满足条件大区间一定满足

    我们设(f_{i})表示满足第(i)个限制的带容斥系数的方案数

    那么转移我们就枚举上一个没有交的区间

    [f_{i} =g_{;_i -1} + sum_{co_i = co_j,l_jge r_i}f_j ]

    其中(g_x)表示(1-x)位置满足(1-x)的所有容斥之后的限制的前缀和

    也就是说

    [g_i = g_{i - 1} imes s + sum_{r_j = i}f_j ]

    就是看看第(i)位置上的限制满足还是不满足综合考虑的前缀和

    继续回到求(f_i)的式子

    既然(i)的这个限制要容斥,那么强制他不被满足,前面就是对所有和他没有交的限制求一个总的容斥

    后面算有交的部分的贡献,必须满足和当前限制的颜色相同,

    我们排序之后,有交的集合是一个区间,我们二分找到对应位置维护前缀和即可

    #include<cstdio>
    #include<iostream>
    #include<queue>
    #include<algorithm>
    #include<cstring>
    #include<cctype>
    #include<vector>
    #include<ctime>
    #include<cmath>
    #include<set>
    #include<map>
    #define LL long long
    #define pii pair<int,int>
    #define mk make_pair
    #define fi first
    #define se second
    using namespace std;
    const int N = 4e5 + 3;
    const LL mod = 998244353;
    struct seg{
    	int li,ri;
    	int xi;	
    }a[N],b[N];
    vector <pii> co[N];
    vector <LL> h[N];
    LL f[N],g[N];
    int n,m,s,cnt;
    inline int read(){
    	int v = 0,c = 1;char ch = getchar();
    	while(!isdigit(ch)){
    		if(ch == '-') c = -1;
    		ch = getchar();
    	}
    	while(isdigit(ch)){
    		v = v * 10 + ch - 48;
    		ch = getchar();
    	}
    	return v * c;
    }
    inline bool cmp(seg x,seg y){
    	return x.ri < y.ri || (x.ri == y.li && x.li > y.li);	
    }
    inline LL find(int x,int rr){
    //	printf("%d %d
    ",x,rr);
    	int l = 0,r = h[x].size() - 1,ans = -1;
    	if(r < 0) return 0;
    	while(l <= r){
    		int mid = (l + r) >> 1;
    		if(co[x][mid].se < rr) l = mid + 1,ans = mid;
    		else r = mid - 1;
    	}
    //	printf("%d %d %d %lld
    ",l,r,ans,ans == -1 ? h[x].back() : h[x].back() - h[x][ans]);
    	return ans == -1 ? h[x].back() : h[x].back() - h[x][ans];
    }
    inline LL mo(LL x){
    	if(x >= mod) x-= mod;
    	return x; 
    }
    int main(){
    	n = read(),m = read(),s = read();
    	for(int i = 1;i <= m;++i){
    		a[i].li = read();
    		a[i].ri = read();
    		a[i].xi = read();	
    	}
    	sort(a + 1,a + m + 1,cmp);
    	for(int i = 1;i <= m;++i){
    		bool flag = 0;
    		if(!co[a[i].xi].size()) co[a[i].xi].push_back(mk(a[i].li,a[i].ri));
    		else{
    			pii x = co[a[i].xi].back();
    			if(x.fi >= a[i].li && x.se <= a[i].ri) flag = 1;
    			else co[a[i].xi].push_back(mk(a[i].li,a[i].ri));
    		}
    		if(!flag) b[++cnt] = a[i];
    	}
    	m = cnt;
    	memcpy(a,b,sizeof(a));
    //	puts("new::");
    //	for(int i = 1;i <= m;++i) cerr << a[i].li << " " << a[i].ri << " " << a[i].xi << endl; 
    //	puts("next::");
    	int now = 1;
    	f[0] = g[0] = 1;
    	for(int i = 1;i <= m;++i){
    	//	cerr << "dsdas::"<< a[i].li << " " << a[i].ri << " " << a[i].xi << endl; 
    		for(;now < a[i].ri;now++) g[now] = (g[now] + g[now - 1] * s) % mod;	
    		f[i] = (-g[a[i].li - 1] + mod);
    		f[i] -= find(a[i].xi,a[i].li);
    		if(f[i] < 0) f[i] += mod;	
    		LL gg = h[a[i].xi].empty() ? 0 : h[a[i].xi].back();
    		h[a[i].xi].push_back(mo(gg + f[i]));
    		g[a[i].ri] = mo(g[a[i].ri] + f[i]);
    	}
    	for(;now <= n;++now) g[now] = (g[now] + g[now - 1] * s) % mod;
    	printf("%lld
    ",g[n]);
    	return 0;
    }
    
    
  • 相关阅读:
    BZOJ 3611: [Heoi2014]大工程 [虚树 DP]
    BZOJ 3991: [SDOI2015]寻宝游戏 [虚树 树链的并 set]
    BZOJ 2286: [Sdoi2011消耗战 [DP 虚树]
    BZOJ 4767: 两双手 [DP 组合数]
    BZOJ 1426: 收集邮票 [DP 期望 平方]
    转「服务器运维」如何解决服务器I/O过高的问题
    iostat查看linux硬盘IO性能
    Linux前台、后台、挂起、退出、查看命令汇总
    Linux虚拟内存的作用
    -bash: iostat: command not found解决办法
  • 原文地址:https://www.cnblogs.com/wyxdrqc/p/11688505.html
Copyright © 2011-2022 走看看