2022ICPC杭州 - C D F K
目录
C DP
D 打表规律题
F 签到题
K trie树
The 2022 ICPC Asia Hangzhou Regional Programming Contest
C. No Bug No Game
自己的题解有空再补啦 qwq
参考题解1
此题解的主要思路是枚举左右完全选择,再枚举不完全选择的位置,取最大即为答案
注意完全选择,所以不是普通的 01 背包,必须要从0开始一步一步选上去,所以 dp 初值应该是-1或者-无穷大
const int N = 3e3 + 5, inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f, mod = 998244353;
int n, k, dp[N][N][2];
vector<int> p[N];
void solve(){
cin >> n >> k;
int sum = 0;
for(int i = 1; i <= n; ++ i){
int c, t;
cin >> c;
for(int j = 0; j < c; ++ j){
cin >> t;
p[i].push_back(t);
}
sum += c;
}
if(sum <= k){
sum = 0;
for(int i = 1; i <= n; ++ i) sum += p[i].back();
cout << sum << '\n';
return ;
}
mem(dp, -1);
dp[0][0][0] = 0;
for(int i = 1; i <= n; ++ i){
int c = p[i].size();
for(int j = 0; j <= k; ++ j){
dp[i][j][0] = dp[i - 1][j][0];
if(j >= c && dp[i - 1][j - c][0] != -1)
dp[i][j][0] = max(dp[i][j][0], dp[i - 1][j - c][0] + p[i].back());
}
}
dp[n + 1][0][1] = 0;
for(int i = n; i > 0; -- i){
int c = p[i].size();
for(int j = 0; j <= k; ++ j){
dp[i][j][1] = dp[i + 1][j][1];
if(j >= c && dp[i + 1][j - c][1] != -1)
dp[i][j][1] = max(dp[i][j][1], dp[i + 1][j - c][1] + p[i].back());
}
}
int ans = 0;
for(int i = 1; i <= n; ++ i){
for(int j = 1; j <= p[i].size(); ++ j){
int res = k - j;
for(int l = 0; l <= res; ++ l){
int r = res - l;
if(dp[i - 1][l][0] != -1 && dp[i + 1][r][1] != -1){
ans = max(ans, dp[i - 1][l][0] + dp[i + 1][r][1] + p[i][j - 1]);
}
}
}
}
cout << ans << '\n';
return ;
}
参考题解2
此题解的主要思路是同时维护从前往后的完全选择和不完全选择即可
const int N = 3e3 + 5, inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f, mod = 998244353;
int n, k, dp[N][N][2];
vector<int> p[N];
void solve(){
cin >> n >> k;
int sum = 0;
for(int i = 1; i <= n; ++ i){
int c, t;
cin >> c;
for(int j = 0; j < c; ++ j){
cin >> t;
p[i].push_back(t);
}
sum += c;
}
mem(dp, -inf);
dp[0][0][0] = 0;
for(int i = 1; i <= n; ++ i){
for(int j = 0; j <= k; ++ j){
dp[i][j][0] = dp[i - 1][j][0];
dp[i][j][1] = dp[i - 1][j][1];
int c = p[i].size();
if(j >= c){// 完全选择时
dp[i][j][0] = max(dp[i][j][0], dp[i - 1][j - c][0] + p[i].back());
dp[i][j][1] = max(dp[i][j][1], dp[i - 1][j - c][1] + p[i].back());
}
for(int l = 1; l <= j && l < c; ++ l){// 不完全选择时
dp[i][j][1] = max(dp[i][j][1], dp[i - 1][j - l][0] + p[i][l - 1]);
}
}
}
if(sum <= k) cout << dp[n][sum][0] << '\n';
else cout << max(dp[n][k][0], dp[n][k][1]) << '\n';
return ;
}
D. Money Game
足够大次数后出结果,盲猜一手最后都一样啦,打表就出结果了,随便随机几个
void solve(){
int n;
double a, s = 0;
cin >> n;
for(int i = 0; i < n; ++ i){
cin >> a;
s += a;
}
s /= (n + 1);
cout << fixed << setprecision(10) << s * 2 << ' ';
for(int i = 1; i < n; ++ i) cout << s << ' ';
return ;
}
F. Da Mi Lao Shi Ai Kan De
void solve(){
int n;
cin >> n;
map<string, bool> q;
for(int i = 0; i < n; ++ i){
int m;
cin >> m;
vector<string> t;
for(int j = 0; j < m; ++ j){
string ss;
cin >> ss;
if(ss.find("bie") != string::npos){
if(!q[ss]) t.push_back(ss), q[ss] = true;
}
}
if(t.size() == 0) cout << "Time to play Genshin Impact, Teacher Rice!\n";
else for(auto s : t) cout << s << '\n';
}
return ;
}
K. Master of Both
每次询问暴力求解逆序对个数显然不可能
如何优化?其实对于两个字符串的字典序比较,关键在于两个字符的比较,或者前缀的关系,而这个关系可以通过预处理得出
所以先用 trie树 存下所有字符串,同时统计所有的字符比较关系和前缀关系,每次询问时统计即可
详见代码
const int N = 1e6 + 5, inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f, mod = 998244353;
int son[N][26], idx = 1;
ll mp[26][26], cnt[N][26], ini;
void insert(string s){
int cur = 0;
for(int i = 0; i < s.size(); ++ i){
if(!son[cur][s[i] - 'a']) son[cur][s[i] - 'a'] = idx ++ ;
for(int j = 0; j < 26; ++ j){
if(j == s[i] - 'a') continue;
mp[s[i] - 'a'][j] += cnt[cur][j];
}
++ cnt[cur][s[i] - 'a'];
cur = son[cur][s[i] - 'a'];
}
for(int i = 0; i < 26; ++ i) ini += cnt[cur][i];
return ;
}
void solve(){
int n, q;
string s;
cin >> n >> q;
for(int i = 0; i < n; ++ i){
cin >> s;
insert(s);
}
for(int i = 0; i < q; ++ i){
cin >> s;
ll ans = ini;
for(int i = 0; i < 26; ++ i){
for(int j = i + 1; j < 26; ++ j){
ans += mp[s[i] - 'a'][s[j] - 'a'];
}
}
cout << ans << '\n';
}
return ;
}