• 2019杭电多校第一场


    2019杭电多校第一场

    有点自闭,本来应该昨天写的,拖到了今天

    1001. Blank

    upsolved

    题意是在(n)个位置上填数,只能填(0,1,2,3)这四种,然后有(m)个限制条件,限制的是区间不同数的个数,求填数方案数(1<=n,m<=100)

    看着官方题解一下就明白了

    (dp[i][j][k][t])代表填完前(t)个数之后四种数的出现位置从小大大排序分别是(i,j,k,t)的方案数,限制条件加到右端点里判断就好了,(t)那一维可以滚动掉,时间复杂度(O(Tn^4))(因为是(i,j,k,t)有序的所以常数比一小得多),空间复杂度(O(n^3))

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N = 101, mod = 998244353;
    
    int dp[N][N][N][2], T, n, m, l, r, x, ans;
    
    vector<pair<int, int>> cons[N];
    
    void addmod(int &x, int y) {
    	x += y;
    	if(x >= mod)
    		x -= mod;
    }
    
    int main() {
    	scanf("%d", &T);
    	while(T--) {
    		ans = 0;
    		scanf("%d%d", &n, &m);
    		for(int i = 1; i <= m; ++i) {
    			scanf("%d%d%d", &l, &r, &x);
    			cons[r].push_back({l, x});
    		}
    		for(int i = 1; i <= n; ++i)
    			cons[i].push_back({i, 1});
    		memset(dp, 0, sizeof(dp));
    		dp[0][0][0][0] = 1;
    		for(int cur = 1; cur <= n; ++cur) {
    			int o = cur & 1;
    			for(int i = 0; i <= cur; ++i) 
    				for(int j = i; j <= cur; ++j)
    					for(int k = j; k <= cur; ++k)
    						dp[i][j][k][o] = 0;
    			for(int i = 0; i <= cur; ++i) 
    				for(int j = i; j <= cur; ++j)
    					for(int k = j; k <= cur; ++k) {
    						addmod(dp[j][k][cur - 1][o], dp[i][j][k][o ^ 1]);
    						addmod(dp[i][k][cur - 1][o], dp[i][j][k][o ^ 1]);
    						addmod(dp[i][j][cur - 1][o], dp[i][j][k][o ^ 1]);
    						addmod(dp[i][j][k][o], dp[i][j][k][o ^ 1]);
    					}
    			for(int i = 0; i <= cur; ++i) {
    				for(int j = i; j <= cur; ++j)
    					for(int k = j; k <= cur; ++k) 
    						for(auto c : cons[cur]) {
    							l = c.first, x = c.second;
    							if((i >= l) + (j >= l) + (k >= l) + (cur >= l) != x)
    								dp[i][j][k][o] = 0;
    						}
    			}
    		}
    		for(int i = 0; i <= n; ++i)
    			for(int j = i; j <= n; ++j) 
    				for(int k = j; k <= n; ++k)
    					addmod(ans, dp[i][j][k][n & 1]);
    		printf("%d
    ", ans);
    		for(int i = 1; i <= n; ++i)
    			cons[i].clear();
    	}
    	return 0;
    }
    

    1002. Operation

    upsloved

    强制在线,每次往数列末加数或询问区间子集异或最大值

    究极自闭,cf1100F原题(有在线做法),这题我寒假的时候还做过。。。然后看了五个小时没看出来。。。

    就是记录(n)个前缀线性基,线性基额外维护当前位置的数的插入位置,插入的时候贪心地取最大值,具体可以搜cf1100F的题解

    1004. Vacation

    solved at 02:40

    (n+1)辆车,每辆车有长度最大速度以及离终点的距离,不能超车,可以贴着前面车走,问离终点最远的车到达终点的时间

    队友想出了二分答案的做法

    有线性做法:离终点最远的车肯定最后是和其他若干辆车(可能只有它自己)连在一起过终点的,(O(n))枚举取最大值就好了

    1005. Path

    solved at 02:06

    有一个有向图,拆除一条边的代价是这条边的长度,要求使用最小的代价使得从(1)(n)的最短路变长

    把最短路上的边拿出来建一张新图跑最小割就行了

    队友建新图的时候点没标记疯狂({ m MLE})...

    1006. Typewriter

    upsolved

    用最小的代价构建目标字符串,你一开始有一个空串,有两种操作

    (1):花费(p)的代价往你的串最后加一个字符

    (2):花费(q)的代价往你的串最后加一个你的串的任意子串

    比赛上来读了个假题。。。

    维护两个位置(i, j)

    (s[1:j-1])插入({ m SAM}),然后看(s[j:i])是不是(s[1:j-1])的子串

    不是就插入(s[j], j++)

    是就(dp[i] = min(dp[i - 1] + p, dp[j-1]+q]))

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N = 2e5 + 10;
    
    int nxt[N << 1][26], len[N << 1], par[N << 1];
    int sz, last;
    char s[N];
    int n, p, q;
    long long dp[N];
    
    int newnode(int l) {
    	len[sz] = l;
    	memset(nxt[sz], 0, sizeof(nxt[sz]));
    	return sz++;
    }
    
    void init() {
    	sz = last = 0;
    	par[sz] = -1;
    	newnode(0);
    }
    
    void add(int x) {
    	int p = last, np = newnode(len[last] + 1);
    	for(; ~p && !nxt[p][x]; p = par[p]) nxt[p][x] = np;
    	if(p == -1) {
    		par[np] = 0;
    	}
    	else {
    		int q = nxt[p][x];
    		if(len[q] == len[p] + 1) {
    			par[np] = q;
    		}
    		else {
    			int nq = newnode(len[p] + 1);
    			memcpy(nxt[nq], nxt[q], sizeof(nxt[nq]));
    			par[nq] = par[q];
    			par[q] = par[np] = nq;
    			for(; ~p && nxt[p][x] == q; p = par[p])
    				nxt[p][x] = nq;
    		}
    	}
    	last = np;
    }
    
    
    int main() {
    	while(~scanf("%s%d%d", s + 1, &p, &q)) {
    		init();
    		n = strlen(s + 1);
    		int cur = 1, now = 1, pos = 0;
    		while(cur <= n) {
    			int x = s[cur] - 'a';
    			if(nxt[pos][x]) {
    				pos = nxt[pos][x];
    				dp[cur] = min(dp[cur - 1] + p, dp[now - 1] + q);
    				cur++;
    			}
    			else if(cur == now) {
    				add(x);
    				dp[cur] = dp[cur - 1] + p;
    				cur++;
    				now++;
    			}
    			else {
    				add(s[now++] - 'a');
    				int tlen = cur - now;
    				while(~pos && ~par[pos] && len[par[pos]] >= tlen)
    					pos = par[pos];
    			}
    		}
    		printf("%lld
    ", dp[n]);
    	}
    	return 0;
    }
    
    

    1009. String

    upsolved

    赛中队友说可做,只是我一直在1002...我真菜

    选择一个给定字符串的一个长度为(k)的子序列构成目标串,要求字典序最小,同时给出了(26)种字母在新串中出现次数的上界和下界

    直接贪心就完事了。。。

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N = 1e5 + 10, knd = 26;
    
    char s[N];
    int L[knd], R[knd], cnt[N][knd], c[knd], n, k, nxt[N][knd], pos, tmp, tot;
    char ans[N];
    
    bool check(int p, int now) {
    	if(p < 0) return false;
    	int x = s[p] - 'a', res = 0, ret = 1;
    	if(c[x] == R[x]) return false;
    	c[x]++;
    	for(int i = 0; i < knd; ++i) {
    		if(L[i] > c[i]) res += L[i] - c[i];
    		if(cnt[p][i] - (x == i) < L[i] - c[i]) {
    			ret = 0;
    			break;
    		}
    	}
    	if(res > k - now) 
    		ret = 0;
    	c[x]--;
    	return ret;
    }
    
    int main() {
    	while(~scanf("%s%d", s, &k)) {
    		tot = 0;
    		n = strlen(s);
    		for(int i = 0; i < knd; ++i) {
    			scanf("%d%d", &L[i], &R[i]);
    			tot += R[i];
    		}
    		memset(nxt[n], -1, sizeof(nxt[n]));
    		memset(cnt[n], 0, sizeof(cnt[n]));
    		memset(c, 0, sizeof(c));
    		for(int i = n - 1; ~i; --i) {
    			memcpy(nxt[i], nxt[i + 1], sizeof(nxt[i + 1]));
    			memcpy(cnt[i], cnt[i + 1], sizeof(cnt[i + 1]));
    			nxt[i][s[i] - 'a'] = i;
    			cnt[i][s[i] - 'a']++;
    		}
    		pos = -1;
    		for(int i = 0; i < knd; ++i) {
    			if(check(nxt[0][i], 1)) {
    				c[i]++;
    				pos = nxt[0][i] + 1;
    				ans[0] = 'a' + i;
    				break;
    			}
    		}
    		if(pos == -1 || tot < k) {
    			puts("-1");
    			continue;
    		}
    		for(int i = 1; i < k; ++i) {
    			for(int j = 0; j < knd; ++j) {
    				if(c[j] == R[j]) continue;
    				if(check(nxt[pos][j], i + 1)) {
    					c[j]++;
    					pos = nxt[pos][j] + 1;
    					ans[i] = 'a' + j;
    					break;
    				}
    			}
    		}
    		ans[k] = '';
    		printf("%s
    ", ans);
    	}
    	return 0;
    }
    

    1012. Sequence

    NTT

    参考博客

    1011,1013都是队友负责的方向。。

    这场打的真是菜,全场没什么贡献,以后学了什么新东西还是应该记录下来啊。。。

  • 相关阅读:
    7月24日学习
    7月23日学习
    7月22日学习
    7月21日学习
    【编程技巧】applicationContext.xml 里面可配置bean和数据库地址
    【编程技巧】 输入框只能输入中文
    【开发技术】Beyond Compare
    【编程技巧】Ext.QuickTips.init();
    【开发技术】常用正则表达式大全!(例如:匹配中文、匹配html)
    【编程技巧】addSubview和insertSubview的区别
  • 原文地址:https://www.cnblogs.com/tusikalanse/p/11234313.html
Copyright © 2020-2023  润新知