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(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
題意:
思路:
可以發現\(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\)
有一個小細節就是我們預處理的是\(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;
}