• Codeforces Round #791 (Div. 2)


    读错 F 题,痛失 AK(悲!)

    前三道五发罚时,排名掉麻了(悲!)

    CF1679A AvtoBus

    解二元一次方程组,但要特判 \(n < 4\) 的情况,可能会解出负数。(罚时 * 1)

    CF1679B Stone Age Problem

    对于每个元素,维护两个信息,一个是当前值,一个是最近一次被修改的时间。

    对于 2 操作,对全局打一个懒标记,表示在第 \(i\) 个时刻我进行一次全局赋值。

    对于 1 操作,先下传全局懒标记,然后修改值和最后修改时间。

    因为阴间人阴间我,出现了大量手误(罚时 * 3)。

    CF1679C Rooks Defenders

    横纵坐标显然毫无关联,直接分开考虑。

    记录 行/列 有几个棋子,用树状数组维护 行/列 是否存在棋子。

    判断一下就好了。

    读错题(罚时 * 1)。

    阴间人回家去咯,机房变得清净了。

    CF1679D Toss a Coin to Your Graph...

    二分答案 ans,我们只走点权不超过 ans 的点,考虑这些点组成的联通子图是否有环或者最长链是否足够长。

    具体地,check 可以用拓扑排序实现。

    const int MAXN = 2e5 + 5;
     
    LL n , m , k , a[MAXN] , b[MAXN] , dp[MAXN] , deg[MAXN];
    pii E[MAXN];
    vector <int> G[MAXN];
    int vis[MAXN];
     
    bool check(LL x) {
    	for (int i = 1; i <= n; ++i) {
    		if(a[i] > x) vis[i] = 0;
    		else vis[i] = 1;
    		dp[i] = deg[i] = 0;
    		G[i].clear();
    	} 
    	for (int i = 1; i <= m; ++i) if(vis[E[i].fs] && vis[E[i].sc]){
    		G[E[i].sc].push_back(E[i].fs);
    		deg[E[i].fs] ++;
    	} 
    	
    	queue <int> q;
    	for (int i = 1; i <= n; ++i) if(!deg[i]) q.push(i);
    	
    	int num = 0;
    	LL mx = 0;
    	while(!q.empty()) {
    		int x = q.front();
    		q.pop();
    		num ++;
    		mx = max(mx , dp[x]);
    		for (auto v:G[x]) {
    			deg[v] --;
    			dp[v] = max(dp[v] , dp[x] + 1);
    			if(!deg[v]) q.push(v);
    		}
    	}
    	return (num != n || mx >= k - 1);
    }
     
    int main() {
    	read(n),read(m),read(k);
    	for (int i = 1; i <= n; ++i) read(a[i]) , b[i] = a[i];
    	
    	for (int i = 1; i <= m; ++i) {
    		int u , v;
    		read(u),read(v);
    		E[i] = mp(u , v);
    	}
    	
    	sort(b + 1 , b + 1 + n); 
    	LL l = 1 , r = n , res = -1;
    	while(l <= r) {
    		LL mid = (l + r) >> 1;
    		if(check(b[mid])) r = mid - 1 , res = b[mid];
    		else l = mid + 1;
    	}
    	write(res);
    	
    	return 0;
    }
    

    CF1679E Typical Party in Dorm

    字符集 17,一眼不对劲,应该会状压啥的。

    好,开始读题。要求统计所有补全问号方式中,回文子串的总数。

    发现 \(n \le 1000\) , 就是说我们可以把每个子段拿出来分别计算他是回文串的补全问号总方式。

    发现询问是给一个可填字符集,那么做法显而易见了。我们需要状压字符集,并对他们预处理统计答案。

    因此,考虑一个子段,依次考虑每一位,如果一组对称位置确定了一个,另一个问号则可确定。

    考虑完毕后,发现必须要满足我们的字符集至少要包含一些特定字符才可以使其成为回文串。

    同时,由于我们不知道字符集大小,因此我们不能完全计算每段方案数。

    但模仿子集卷积的思路,我们枚举字符集大小,依次考虑每个子段,算出这个子段成为回文串的总次数和必要字符集,于是接下来就只用对每种字符集大小做依次高维前缀和就好了。

    #include <map>
    #include <set>
    #include <queue>
    #include <cmath>
    #include <ctime>
    #include <bitset>
    #include <cstdio>
    #include <cassert>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #define pii pair <int , int>
    #define pll pair <LL , LL>
    #define mp make_pair
    #define fs first
    #define sc second
    using namespace std;
    typedef long long LL;
    typedef unsigned long long ULL;
    
    //const int Mxdt=100000; 
    //static char buf[Mxdt],*p1=buf,*p2=buf;
    //#define getchar() p1==p2&&(p2=(p1=buf)+fread(buf,1,Mxdt,stdin),p1==p2)?EOF:*p1++;
    
    template <typename T>
    void read(T &x) {
    	T f=1;x=0;char s=getchar();
    	while(s<'0'||s>'9') {if(s=='-') f=-1;s=getchar();}
    	while(s>='0'&&s<='9') {x=(x<<3)+(x<<1)+(s-'0');s=getchar();}
    	x *= f;
    }
    
    template <typename T>
    void write(T x , char s='\n') {
    	if(!x) {putchar('0');putchar(s);return;}
    	if(x<0) {putchar('-');x=-x;}
    	T tmp[25]={},t=0;
    	while(x) tmp[t++]=x%10,x/=10;
    	while(t-->0) putchar(tmp[t]+'0');
    	putchar(s); 
    }
    
    const int mod = 998244353;
    
    inline int Add(int x , int y) {x += y;return x >= mod?x - mod:x;}
    inline int Sub(int x , int y) {x -= y;return x < 0?x + mod:x;}
    inline int Mul(int x , int y) {return 1ll * x * y % mod;}
    
    inline int qpow(int a , int b) {
    	int res = 1;
    	while(b) {
    		if(b & 1) res = Mul(res , a);
    		a = Mul(a , a);
    		b >>= 1;
    	}
    	return res;
    }
    
    int n , power[1005] , st[1005][1005] , val[1005][1005] , num[1005][1005] , f[20][(1 << 17) + 5] , Tot;
    char s[1005];
    
    void Pre(int k) {
    	power[0] = 1;
    	for (int i = 1; i <= n; ++i) power[i] = Mul(power[i - 1] , k);
    	for (int i = 1; i <= n; ++i) {
    		val[i][i] = st[i][i] = val[i][i + 1] = st[i][i + 1] = 0;
    		if(s[i] == '?') val[i][i] = k , f[k][0] = Add(f[k][0] , Mul(power[num[i][i]] , k));
    		else val[i][i] = 1 , f[k][0] = Add(f[k][0] , Mul(power[num[i][i]] , 1));
    		if(i < n) {
    			if(s[i] == '?' && s[i + 1] == '?') val[i][i + 1] = k;
    			else if(s[i] == s[i + 1]) val[i][i + 1] = 1;
    			else if(s[i] == '?' || s[i + 1] == '?') {
    				int t = s[i] + s[i + 1] - '?' - 'a';
    				val[i][i + 1] = 1;
    				st[i][i + 1] = (1 << t);
    			}
    			f[k][st[i][i + 1]] = Add(f[k][st[i][i + 1]] , Mul(power[num[i][i + 1]] , val[i][i + 1]));
    		}
    	}
    	for (int len = 3; len <= n; ++len) {
    		for (int i = 1; i + len - 1 <= n; ++i) {
    			int j = i + len - 1;
    			st[i][j] = 0 , val[i][j] = 0;
    			int v = 0 , sa = 0;
    			if(s[i] == '?' && s[j] == '?') v = k;
    			else if(s[i] == s[j]) v = 1;
    			else if(s[i] == '?' || s[j] == '?') {
    				int t = s[i] + s[j] - '?' - 'a';
    				v = 1;
    				sa = (1 << t);
    			}
    			v = Mul(v , val[i + 1][j - 1]);
    			sa |= st[i + 1][j - 1];
    			f[k][sa] = Add(f[k][sa] , Mul(power[num[i][j]] , v));
    			st[i][j] = sa , val[i][j] = v;
    		}
    	}
    }
    
    void FWT(int *f) {
    	for (int i = 0; i < 17; ++i) for (int j = 1; j < (1 << 17); ++j) if(j & (1 << i)){
    		f[j] = Add(f[j] , f[j ^ (1 << i)]);
    	}
    }
    
    int cnt[(1 << 17) + 5];
    
    int main() {
    	read(n);
    	scanf("%s" , s + 1);
    	for (int i = 1; i <= n; ++i) Tot += (s[i] == '?');
    	for (int i = 1; i <= n; ++i) num[i][i] = (Tot - (s[i] == '?'));
    	for (int len = 2; len <= n; ++len) {
    		for (int i = 1; i + len - 1 <= n; ++i) {
    			int j = i + len - 1;
    			num[i][j] = num[i + 1][j] - (s[i] == '?');
    		}
    	}
    	
    	for (int i = 1; i <= 17; ++i) Pre(i);
    	
    	for (int i = 1; i <= 17; ++i) FWT(f[i]);
    	for (int i = 1; i < (1 << 17); ++i) cnt[i] = cnt[i >> 1] + (i & 1);
    	
    	int Q;
    	read(Q);
    	while(Q -- > 0) {
    		scanf("%s" , s + 1);
    		int l = strlen(s + 1) , st = 0;
    		for (int i = 1; i <= l; ++i) st |= (1 << (s[i] - 'a'));
    		write(f[cnt[st]][st]);
    	}
    	
    	return 0;
    }
    

    CF1679F Formalism for Formalism

    为啥读错题了 40min 啊?为啥 5月15日看题解的时候还是没意识到读错题了啊?

    痛失 AK。

    遇到本质不同,相信中国 OIer 都会有想把方案化标的想法。

    于是我们尝试化标,可以用大小关系来定义。即不存在 \(x < y\)\(d_x > d_y\)\(x\) 可以与 \(y\) 对调位置。

    介于我们对这个标准的定义,是右边的一些值不能换到左边的一些位置,因此我们考虑倒序 \(dp\)

    考虑从后往前填位置,考虑添加字符 \(c\) ,则 \(c\) 不能和比他小的字符交换位置。

    假设可以,则该字符一定可以被交换的上一个位置,且和 \(c\) 有边。

    \(c\) 有边很好处理。考虑能换到上一个位置有哪些,发现字符集很小,直接状压。

    那么可以判断一个字符能不能插,考虑建立新的状态。当然,能换到这个位置必须满足能换到上一个位置,即必须存在于上一个状态,同时与 \(c\) 有边。

    int f[MAXN][(1 << 10) + 5] , r[25] , e[25] , cnt[(1 << 10) + 5] , n , m;
     
    int main() {
    	read(n),read(m);
    	for (int i = 1; i <= m; ++i) {
    		int u , v;
    		read(u),read(v);
    		if(u > v) swap(u , v);
    		r[u] |= (1 << v); 
    		e[u] |= (1 << v) , e[v] |= (1 << u); 
    	}
    	
    	for (int s = 1; s < (1 << 10); ++s) cnt[s] = (cnt[s] >> 1) + (s & 1);
    	
    	for (int i = 0; i < 10; ++i) f[1][(1 << i)] = 1;
    	
    	for (int i = 1; i < n; ++i) {
    		for (int s = 0; s < (1 << 10); ++s) if(f[i][s]) {
    			for (int j = 0; j < 10; ++j) {
    				int ns = (s & e[j]) | (1 << j);
    				if((ns & (-ns)) != (1 << j)) continue;
    				ns = (s & r[j]) | (1 << j);
    				f[i + 1][ns] = Add(f[i + 1][ns] , f[i][s]);
    			}
    		}
    	}
    	
    	int ans = 0;
    	for (int i = 0; i < (1 << 10); ++i) ans = Add(ans , f[n][i]);
    	
    	write(ans);
    	return 0;
    }
    
  • 相关阅读:
    C#读取系统信息
    C# 读取驱动器盘符及信息
    for循环里的break,continue和return有什么差别
    monkey
    ZXing
    python中os模块的常用接口和异常中Exception的运用
    python中的字典应用实例
    python中的列表和字典
    python中如何单独测试一个函数的作用
    数据挖掘概念与技术PDF
  • 原文地址:https://www.cnblogs.com/Reanap/p/16278789.html
Copyright © 2020-2023  润新知