A
問題:長度爲n的字符串,是否完全由多個mq連接組成
思路:模擬就型了。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 1e5 + 11;
const int M = 1e6 + 11;
const int MOD = 1e9 + 7;
char s[N];
int main(int argc, char **args){
// freopen("in.txt", "r", stdin);
int q; cin >> q;
while(q--){
scanf("%s", s);
int cur = 0;
bool flag = true;
for(int i = 0; s[i]; i++){
if( (cur == 1 && s[i] == 'q' )|| (cur == 0 && s[i] == 'm'))
cur = cur ^ 1;
else
flag = false;
}
puts((flag && !cur) ? "Yes":"No");
}
return 0;
}
B
思路:賽時 只想到了爆搜+剪枝,這樣肯定不對,只過了50%數據。賽後又想了想,發現可以考慮DP,因爲n個寶物的和最大也就是10000,取前k個最小值就行,這時候,發現,如果可以提前處理出來和爲 i 的方案數,問題就解決了。
所以考慮DP,dp[i][j] 表示前 i 個寶物 的和爲 j 的方案數。遞推方程:
發現時間複雜度正好爲o(1e8),感覺時間上會過不去,但是因爲這個代碼的框架簡單,很簡潔,所以常數會非常小,所以實際上時間上還是能夠過去的(而且牛客評測機1s應該很跑多了把,至少1e8常數內應該都夠)。 空間上,因爲第 i 層只用第 i - 1 層的值,可以利用滾動數組來優化一維。
爆搜代碼:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 1e5 + 11;
const int M = 1e6 + 11;
const int MOD = 1e9 + 7;
int n, k;
priority_queue <int,vector<int>,less<int> > qu;
vector<int>ve[N];
void DFS(int now, int sum){
if(qu.size() >= k && sum >= qu.top()) return; // 剪枝
// cout << now <<" " <<sum <<"\n";
if(now == n + 1) {
if(qu.size() < k)
qu.push(sum);
else {
if(qu.top() > sum) {
qu.pop(); qu.push(sum);
}
}
return;
}
for(int i = 0; i < ve[now].size(); i++){
int v = ve[now][i];
DFS(now + 1, sum + v);
}
}
int main(int argc, char **args){
// freopen("in.txt", "r", stdin);
scanf("%d%d", &n, &k);
for(int i = 1; i <= n ;i++){
int m; scanf("%d", &m);
while(m--){
int a; scanf("%d", &a); ve[i].push_back(a);
}
sort(ve[i].begin(), ve[i].end());
}
DFS(1, 0);
int ans = 0;
while(k--){
// cout << qu.top() <<"\n";
ans += qu.top(); qu.pop();
}
printf("%d\n", ans);
return 0;
}
DP 代碼
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 1e5 + 11;
const int M = 1e6 + 11;
const int MOD = 1e9 + 7;
ll dp[N][2];
int main(int argc, char **args){
// freopen("in.txt", "r", stdin);
int n, k; scanf("%d%d", &n, &k);
int cur = 0; dp[0][cur ^ 1] = 1;
for(int i = 1; i <= n; i++){
int m; scanf("%d", &m);
for(int j = 0; j <= 10000; j++) dp[j][cur] = 0;
while(m--){
int a; scanf("%d", &a);
for(int j = 0; j <= 10000; j++){
if(dp[j][cur ^ 1] > 0)
dp[j + a][cur] += dp[j][cur ^ 1];
}
}
cur ^= 1;
}
cur ^= 1;
int ans = 0;
for(int i = 1; i <= 10000; i++){
if(k > 0) {
if(dp[i][cur] >= k) {
ans += k * i;
}else {
ans += dp[i][cur] * i;
}
k -= dp[i][cur];
}else break;
}
printf("%d\n", ans);
return 0;
}
D
思路:賽時思路對了,可惜沒有發現bug。
其實很簡單,從二元祖到三元組的過程中,我們可以得到啓發。後面擴展到m元祖,其實本質都是一樣的。考慮 dp[i][j] 表示以位置 j 開始的 i 元祖個數。轉移方程:
如何求得右側式子,這裏可以考慮用樹狀數組。
因爲要用樹狀數組,所以要將 值當做數組下標,但是原本 的值很大,發現本題和 本來值無關,只要元素間相對大小不變就行,所以用離散化。
同時觀察式子,發現可以當前層只用前一層的值,所以利用滾動數組來優化 空間。
代碼:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 1e5 + 11;
const int M = 1e6 + 11;
const int MOD = 1e9 + 7;
int n, m, nn;
int sum[N];
int c[N * 4], a[N];
void add(int pos, int x){
for(pos; pos > 0; pos -= (pos & -pos)){
c[pos] = (c[pos] + x) % MOD;
}
}
int query(int pos){
int sum = 0;
for(pos ; pos <= nn; pos += (pos & -pos)){
sum = (sum + c[pos]) % MOD;
}
return sum;
}
vector<int>ve;
int main(int argc, char **args){
// freopen("in.txt", "r", stdin);
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++){
scanf("%d", &a[i]); ve.push_back(a[i]);
}
sort(ve.begin(), ve.end());
ve.erase(unique(ve.begin(), ve.end()), ve.end());
for(int i = 1; i <= n; i++){
a[i] = lower_bound(ve.begin(), ve.end(), a[i]) - ve.begin() + 1; // 離散化,重新賦值
sum[i] = 1;// 賦初值
}
nn = n * 2;
for(int j = 2; j <= m; j++){
memset(c, 0, sizeof(c));
for(int i = n; i > 0; i--){
add(a[i] , sum[i]);
sum[i] = query(a[i] + 1);
// cout << sum[i] <<" ";
}
}
if(m == 1){
cout << n <<"\n";
}else {
int ans = 0;
for(int i = 1; i <= n; i++) {
ans = (ans + sum[i]) % MOD;
}
printf("%d\n", ans);
}
return 0;
}