2021CCPC網絡選拔賽 (unrated)部分題目


1002-Time-division Multiplexing

題意:
給定你\(n\)個長度小於\(12\)的字符串,然后循環的構造一個無限長都的字符串\(s\),構造方式為依次從\(1-n\)的串中取出一個一個字符,如兩個字符串長度為\(3,2\),那么\(s\) = \(t_{11}t_{21}t_{12}t_{22}t_{13}t_{21}t_{11}t_{22}t_{12}t_{21}t_{13}t_{22}\ \ \ t_{11}t_{21}\),問你能包含\(n\)個字符串中出現的所有字符的最小長度的子串長度為多少

思路:
會發現經過某個長度后\(s\)就會重復,那么這個長度是多少呢,手寫幾個例子會發現等於\(n\)個字符串的\(LCM*n\),但是這樣考慮還不行,因為串是無限長度,所以需要把串長再乘\(2\)才會得到不遺漏的\(s\)的一個模式串,然后我們就只需要在循環節長度\(*2\)的這個串上做尺取就可以得到我們想要的答案了。

復雜度因為\(lcm(2,3,4,7,8,9,10,11,12) = 27720\),所以串長最多也就是\(5.5e6\),再加上雙指針\(O(n)\)的掃一遍,時間正好。

\(hdoj\)惡心壞了,寫完了交不上去......

#include <bits/stdc++.h>

using namespace std;

#define pb push_back
#define eb emplace_back
#define MP make_pair
#define pii pair<int,int>
#define pll pair<ll,ll>
#define lson rt<<1
#define rson rt<<1|1
#define CLOSE std::ios::sync_with_stdio(false)
#define sz(x) (int)(x).size()
typedef long long ll;
typedef double db;
const int INF = 0x3f3f3f3f;
const db eps = 1e-6;
const int N = 2e7 + 10;
char s[N];
char t[110][20];
int len[110];
int n,cnt[30],tot,vis[30];

int gcd(int a,int b) {
	return b == 0 ? a : gcd(b,a%b);
}

void solve() {
	tot = 0;
	for(int i = 0;i < 26;i ++) vis[i] = cnt[i] = 0;
	scanf("%d",&n);
	getchar();
	for(int i = 1;i <= n;i ++) {
		scanf("%s",t[i]+1);
		len[i] = strlen(t[i]+1);
	}
	for(int i = 1;i <= n;i ++) {
		for(int j = 1;j <= len[i];j ++) {
			if(!vis[t[i][j]-'a']) {
				vis[t[i][j]-'a'] = 1;
				tot++;
			}
		}
	}
	int lcm = len[1];
	for(int i = 2;i <= n;i ++) {
		lcm = lcm * len[i] / gcd(lcm,len[i]);
	}
	lcm *= n;
	int id = 1,round = 1;
	for(int i = 1;i <= lcm;i ++) {
		if(id > n) {
			id = 1; round++;
		}
		s[i] = t[id][(round-1)%len[id]+1];
		id++; 
	}
	for(int i = lcm + 1;i <= lcm * 2;i ++) {
		s[i] = s[i-lcm];
	}
	lcm *= 2;
	s[lcm+1] = '\0';
	int mi = INF;
	int now = 0;
	for(int st = 1,ed = 1;st <= lcm;) {
		if(ed <= lcm && now < tot) {
			while(ed <= lcm && now < tot) {
				if(cnt[s[ed]-'a'] == 0) {
					now++;
				}
				cnt[s[ed]-'a']++;
				ed++;
			}
		}
		if(now == tot) {
			mi = min(mi,ed - st);
		}
		cnt[s[st]-'a']--;
		if(cnt[s[st]-'a'] == 0) now--;
		st++;
	}
	printf("%d\n",mi);
}

int main() {
	int T;scanf("%d",&T);
	while(T--) solve();
	return 0;
}

1007-Function

題意:
image

思路:
可以發現\(g(x)_{max} = g(99999) = 54\),所以\(g(x)\)至多\(54\)個不同的值,那么當\(g(x)的值確定之后\),這個函數的圖形就是一個二次函數的一些不連續的點(因為不是所有的\(x \ g(x)\) = 當前枚舉的值)
所以直接枚舉\(g(x)\)的值,然后分類討論這個二次函數的圖像的樣子,\(a=0,a<0,a>0\)即可確定函數的最小值。
預處理出來\(g(x)\)=枚舉值的\(x\)有哪些,每次找最值只需要在其中二分即可,但是對稱軸位置處可能不存在\(x\),所以我們只需要二分第一個\(\ge \ x\)的位置,和它前面的位置去這兩個位置處的最小值即可(\(a>0\)的情況)

有一個小細節就是我們預處理的是\(1 - 1000000\)的值,但是每次會給出一個N來固定上限,所以每次對於一個特定的\(N\)來說,要確定一個上界,二分或者直接取邊界的操作都不能直接超過這個上界

#include <bits/stdc++.h>

using namespace std;

#define pb push_back
#define eb emplace_back
#define MP make_pair
#define pii pair<int,int>
#define pll pair<ll,ll>
#define lson rt<<1
#define rson rt<<1|1
#define CLOSE std::ios::sync_with_stdio(false)
#define sz(x) (int)(x).size()
typedef long long ll;
typedef double db;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const db eps = 1e-6;
const int N = 60;
std::vector<ll>vec[N];

ll cal(ll x) {
	ll sum = 0;
	while(x) {
		sum += x % 10;
		x /= 10;
	}
	return sum;
}

void solve() {
	ll ans = INF;
	ll A,B,C,D,N;
	scanf("%lld%lld%lld%lld%lld",&A,&B,&C,&D,&N);
	//f(x) = Ax^2g(x) + Bx^2 + Cxg(x)^2 + Dxg(x)
	//(Ag(x) + B)x^2 + (Cg(x)^2 + Dg(x))x
	for(ll i = 1;i <= 54;i ++) {
		if(sz(vec[i]) == 0 || vec[i][0] > N) continue;
		ll a = A * i + B,b = C * i * i + D * i;
		ll r = upper_bound(vec[i].begin(),vec[i].end(),N) - vec[i].begin();
		//因為預處理的是1-1e6,這是一個wa點
		if(a <= 0) {
			ll j1 = vec[i][0];
			ll j2 = vec[i][r-1];
			ans = min(ans,min(a * j1 * j1 + b * j1,a * j2 * j2 + b * j2));
		}
		else if(a > 0) {
			ll mid = b / (-2 * a);
			std::vector<ll>::iterator j1 = lower_bound(vec[i].begin(),vec[i].begin() + r,mid),j2;
			if(j1 == vec[i].begin() + r) {//處理一個找不到的情況
				j1 = j2 = prev(j1);
			}
			else {
				if(j1 == vec[i].begin()) j2 = j1;
				else j2 = prev(j1);
			}
			ll x1 = *j1,x2 = *j2;
			ans = min(ans,min(a * x2 * x2 + b * x2,a * x1 * x1 + b * x1));
		}
	}
	printf("%lld\n",ans);
}

int main() {
	for(ll i = 1;i <= 1000000;i ++) {
		vec[cal(i)].pb(i);
	}
	int T;scanf("%d",&T);
	while(T--) solve();
	return 0;
}

1011-Shooting Bricks

題意:
\(n\)\(m\)列的矩形,每個格子的內部都有一個磚塊,現在有一把手槍和\(k\)發子彈,一發子彈可以打碎一個磚塊,你只能從每一列的最下面一行開始打,每個磚塊被擊碎后都會有相應的獎勵分數\(f_{i,j}\),同時有些磚塊被打碎后再獎勵分數的基礎上還會獎勵一發子彈,\(c_{i,j} = Y\)代表打碎后會獎勵一發子彈,\(c_{i,j} = N\)代表打碎后不會獎勵子彈。
讓你求出初始有\(k\)發子彈能獲得的最大的分數是多少。

思路:
因為每塊磚有\(Y/N\)的兩種狀態,要考慮最后一顆子彈打到這個磚塊的兩種不同的情況,\(dp[i][j][0]\)表示\(1-i\)列中,消耗了\(j\)發子彈,且最后一發子彈打在了\(N\)磚塊上的情況,\(dp[i][j][1]\)表示\(1-i\)列中,消耗了\(j\)發子彈,且最后一發子彈打在了\(Y\)磚塊上的情況。
注意這里用的是消耗這個詞,既可以認為打到\(Y\)磚塊上時不消耗子彈的。

我們還需要提前處理一個\(w\)數組,\(w[i][j][0/1]\)表示第\(i\)列消耗了\(j\)發子彈,且最后一發子彈打到了\(N/Y\)上。

這里可以把最后一發子彈打到了\(Y/N\)上換一種說法,即當前枚舉的前\(i\)列中是否包含了能夠打出的最后一發子彈,因為打到了\(Y\)上相當於我們又獲得了一枚子彈,打到了\(N\)上就相當於沒了。

我們把\(i\)\(1-m\)枚舉第\(i\)列,\(j\)\(0-k\)枚舉,當前總共花費了多少的子彈,\(nowcol\)\(0-min(j,n)\)枚舉當前第\(i\)列消耗了多少發子彈,類似於一個做背包的過程。
\(dp[i][j][1]\),即前\(i\)列均不包含最后一發子彈,那么只能從\(dp[i-1][j-nowcol][1],w[i][nowcol][1]\)這兩個狀態來轉移過來,即前\(i-1\)列不包含最后一發子彈,第i列也不包含。
\(dp[i][j][0]\)則可從兩個不同的狀態對轉移過來,\((dp[i-1][j-nowcol][0],w[i][nowcol][1]),(dp[i-1][j-nowcol][1],w[i][nowcol][1])\)
再加上一些判斷小細節,即最后一發子彈轉移的狀態一定要分配到子彈,因為最后一發子彈一定是會實實在在消耗掉一顆子彈的。

#include <bits/stdc++.h>

using namespace std;

#define pb push_back
#define eb emplace_back
#define MP make_pair
#define pii pair<int,int>
#define pll pair<ll,ll>
#define lson rt<<1
#define rson rt<<1|1
#define CLOSE std::ios::sync_with_stdio(false)
#define sz(x) (int)(x).size()
typedef long long ll;
typedef double db;
const int INF = 0x3f3f3f3f;
const db eps = 1e-6;
const int N = 210;

//dp[i][j][0]表示1-i列共消耗了j發子彈且最后一發子彈打到了N磚塊上 也就是最后一發子彈在當前列
//dp[i][j][1]表示1-i列共消耗了j發子彈且最后一發子彈打到了Y磚塊上 也就是最后一發子彈不在當前列

//w[i][j][0]表示第i列消耗了了j發子彈且最后一發子彈打到了N磚塊上能獲得的最大的價值
//w[i][j][1]表示第i列消耗了了j發子彈且最后一發子彈打到了Y磚塊上能獲得的最大的價值

int n,m,k;
char ch;
int dp[N][N][2],w[N][N][2],a[N][N],can[N][N];

void solve() {
	scanf("%d%d%d",&n,&m,&k);
	for(int i = 1;i <= n;i ++) {
		for(int j = 1;j <= m;j ++) {
			scanf("%d %c",&a[i][j],&ch);
			if(ch == 'Y') can[i][j] = 1;
		}
	}
	for(int i = 1;i <= m;i ++) {
		int used = 0;
		for(int j = n;j >= 1;j --) {
			if(can[j][i] == 1) {
				w[i][used][1] += a[j][i];
			}
			else {
				++used;
				w[i][used][1] = w[i][used-1][1] + a[j][i];
				w[i][used][0] = w[i][used-1][1] + a[j][i];
			}
		}
	}
	for(int i = 1;i <= m;i ++) {
		for(int j = 0;j <= k;j ++) {
			for(int nowcol = 0;nowcol <= min(n,j);nowcol ++) {
				dp[i][j][1] = max(dp[i][j][1],dp[i-1][j-nowcol][1] + w[i][nowcol][1]);
				if(j - nowcol > 0) 
					dp[i][j][0] = max(dp[i][j][0],dp[i-1][j-nowcol][0] + w[i][nowcol][1]);
				if(nowcol > 0)
					dp[i][j][0] = max(dp[i][j][0],dp[i-1][j-nowcol][1] + w[i][nowcol][0]);
			}
		}
	}
	printf("%d\n",dp[m][k][0]);
	for(int i = 1;i <= n;i ++) {
		for(int j = 1;j <= m;j ++) {
			can[i][j] = 0;
		}
	}
	for(int i = 1;i <= m;i ++) {
		for(int j = 0;j <= k;j ++) {
			dp[i][j][0] = dp[i][j][1] = w[i][j][0] = w[i][j][1] = 0;
		}
	}
}

int main() {
	int T;scanf("%d",&T);
	while(T--) solve();
	return 0;
}


免責聲明!

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



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