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\n", 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\)的最短路變長

把最短路上的邊拿出來建一張新圖跑最小割就行了

隊友建新圖的時候點沒標記瘋狂\({\rm MLE}\)...

1006. Typewriter

upsolved

用最小的代價構建目標字符串,你一開始有一個空串,有兩種操作

\(1\):花費\(p\)的代價往你的串最后加一個字符

\(2\):花費\(q\)的代價往你的串最后加一個你的串的任意子串

比賽上來讀了個假題。。。

維護兩個位置\(i, j\)

\(s[1:j-1]\)插入\({\rm 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\n", 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] = '\0';
		printf("%s\n", ans);
	}
	return 0;
}

1012. Sequence

NTT

參考博客

1011,1013都是隊友負責的方向。。

這場打的真是菜,全場沒什么貢獻,以后學了什么新東西還是應該記錄下來啊。。。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM