题目链接:http://codeforces.com/gym/101669
A - Concerts(dp)
给出k( ),n( ),代表一个人要参加k场演唱会,一共有n场演唱会会举办。接下来给出每场演唱会要歇息的天数(一共26个乐队用ABC…Z代表),接下来一行给出n天演唱会举办的顺序。问这个人按顺序参加这k场演唱会有几种方案。
思路:
sum[i][j]:第i天的时候已经参加到了第j场演唱会的方案数。
pre[i][j]:
可以推得方程:sum[i][j] = (sum[i][j] + pre[idx][j-1]) % mod;(idx代表回滚到参加第j-1场演唱会的日期)
然后考虑到内存的限制,无法开两个三千万的数组,所以可以滚一下sum数组。
#include <bits/stdc++.h>
using namespace std;
//k 300 n 1e5
const int maxn = 1e5 + 5;
int sum[2][300 + 5], pre[maxn][300+5];
const int mod = 1e9 + 7;
char s1[300 + 5], s2[maxn];
int cost[30];
int main()
{
int k, n;
scanf("%d%d", &k, &n);
for(int i = 0; i < 26; i++) scanf("%d", &cost[i]);
scanf("%s%s", s1 + 1, s2);
for(int i = 0; i < n; i++)
{
if(i) pre[i][1] = pre[i-1][1];
if(s1[1] == s2[i]) pre[i][1]++;
}
long long ans = 0;
for(int i = 0; i < n; i++)
{
for(int j = 2; j <= k; j++)
{
sum[i&1][j] = 0;
if(i) pre[i][j] = pre[i - 1][j];
if(s1[j] != s2[i]) continue;
char ch = s1[j-1];
int c = cost[ch-'A'];
int idx = i - (c + 1);
if(idx < 0) continue;
sum[i&1][j] = (sum[i&1][j] + pre[idx][j-1]) % mod;
pre[i][j] = (sum[i&1][j] + pre[i][j]) % mod;
if(j == k)ans = (ans + sum[i&1][k]) % mod;
}
}
printf("%lld\n", ans);
return 0;
}
D - Harry Potter and The Vector Spell (并查集)
给出一个m*n的01矩阵(每列恰有两个非零),问它在模2意义(异或xor)下的秩是几。
思路:转换一下输入以后,可以考虑到每次询问u和v两列的时候,只要两者在一个并查集内,就代表这一行是无效的,否则计数,最后输出即可
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 5;
int pa[maxn];
int uf_find(int x){return x == pa[x] ? x : pa[x] = uf_find(pa[x]);}
void join(int x, int y)
{
int fx = uf_find(x);
int fy = uf_find(y);
if(fx != fy)
{
pa[fx] = fy;
}
}
vector<int>row[maxn];
int main()
{
int m, n;
scanf("%d%d", &m, &n);
for(int i = 1; i <= m; i++)
{
int k;
scanf("%d", &k);
for(int j = 0; j < k; j++)
{
int x;
scanf("%d", &x);
row[x].push_back(i);
}
}
for(int i = 1; i <= m; i++) pa[i] = i;
int ans = 0;
for(int i = 1; i <= n; i++)
{
if(row[i].size() == 0) continue;
int u = row[i][0], v = row[i][1];
int fx = uf_find(u), fy = uf_find(v);
if(fx != fy)
{
ans++;
join(u, v);
}
}
printf("%d\n", ans);
return 0;
}
F - Binary Transformations (思维题)
给出n( ),接下来给出每一位的cost[i],接下来给出s1串和s2串,让你把s1串转变为s2串,转变s1串中第i位的时候,所需的花费是,第i位转变后,s1串中所有s1[k] = 1的k的cost加和,问最小花费是多少。
思路:一开始考虑的策略是贪心的去做,先考虑1到0的转变,转变肯定从cost大到小转变,再考虑0到1的转变,转变肯定从cost小到大。wa了以后考虑到,1到1的时候,如果cost很大,可能做了1到0的转变,再做0到1的转变,反而会使得答案更小。所以将1到1的按cost排序,从大到小排序,枚举一遍从大到小有几个拿去做1to0,0to1。然后再套上之前的贪心即可。
#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <set>
#include <vector>
#include <string.h>
using namespace std;
typedef long long LL;
const int maxn = 5005;
int c[maxn];
char a[maxn], b[maxn];
multiset<int> decr, incr;
vector<int> ori;
int s1[maxn], s2[maxn];
int main()
{
int n;
scanf("%d", &n);
for (int i = 1; i <= n; i++)
{
scanf("%d", &c[i]);
}
scanf("%s%s", a + 1, b + 1);
int k = 0, p = 0, q = 0;
for (int i = 1; i <= n; i++)
s1[i] = a[i] - '0', s2[i] = b[i] - '0';
for (int i = 1; i <= n; i++)
{
if (s1[i] == 1 && s2[i] == 0)
decr.insert(c[i]), k++;
else if (s1[i] == 0 && s2[i] == 1)
incr.insert(c[i]), p++;
else if (s1[i] + s2[i] == 2)
ori.push_back(c[i]), q++;
}
sort(ori.begin(), ori.end());
LL res = 0;
LL ans = 0;
ans += LL(k + p);
LL cost = 0;
for (auto o : ori)
cost += o;
ans *= cost;
int idx = k - 1;
for (auto o : decr)
{
ans += LL(idx) * o;
idx--;
}
idx = p;
for (auto o : incr)
{
ans += LL(idx) * o;
idx--;
}
//cout << ans << endl;
int sz = ori.size();
for (int i = sz - 1; i >= 0; i--)
{
LL cur = 0;
cost -= ori[i];
k++;p++;
decr.insert(ori[i]);
incr.insert(ori[i]);
cur += cost * (k + p);
idx = k - 1;
for (auto o : decr)
{
//cout << idx << ' ' << o << endl;
cur += LL(idx) * o;
idx--;
}
//cout << cur << ' ';
idx = p;
for (auto o : incr)
{
cur += LL(idx) * o;
idx--;
}
//cout << cur << endl;
ans = min(ans, cur);
}
printf("%I64d\n", ans);
return 0;
}
G - Robots (water)
水题不表。
K - Escape Room (思维)
让你给出一个满足条件的最小字典序的一个1到n的排列。条件:给出一个数组代表着这个排列从第i位开始的最长递增子序列长度为a[i]
思路:可想而知,从左往右扫,a[i]=1的可以考虑到是从这一位至末尾最大的数,所以n一定是在第一个a[i]=1的位置,所以从左往右扫a[i]=1,a[i]=2依次铺放n,n-1,…,1。考虑到复杂度的原因,可以用一个set来维护一下即可。
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n;
scanf("%d", &n);
set<pair<int, int>>s;
for(int i = 0; i < n; i++)
{
int x;
scanf("%d", &x);
s.insert({x, i});
}
vector<int>ans;
ans.resize(n);
int cnt = n;
for(auto o : s)
{
int idx = o.second;
int val = o.first;
ans[idx] = cnt--;
}
for(int i =0 ;i < n; i++)
printf("%d%c", ans[i], i + 1 == n ?'\n':' ');
return 0;
}