A题 普通的模拟就不多说了 放个代码
#include<bits/stdc++.h>
using namespace std;
int T;
int main()
{
cin >> T;
while(T --)
{
string str;
cin >> str;
bool flag = true;
for (int i = 0; i < str.length() && flag; i = i+2){
if(str[i] == 'm' && str[i + 1] == 'q') continue;
else flag = false;
}
if(flag) puts("Yes");
else puts("No");
}
}B题
自己想的方法加上巨佬们的方法,大概有三种。
1:也是标程的方法,背包求方案数目。具体来说,就是设每个盒子是一个背包。然后dp[i][j]表示前i个背包且总和为j的方案数。
转移方程如下:
for (int i = 0; i < n; i ++) //n表示盒子的种类
for (int j = 0; j < n[i]; j ++) //n[i]表示每一个盒子内的宝物的数目
for (int s = ma; s >= a[i][j]; s --)
dp[i][s] += dp[i - 1][s - a[i][j]];
完整代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 105, M = N * N;
int n, k;
int m[N], a[N][N];
int dp[N][M];
int main()
{
cin >> n >> k;
int ma = 0;
for (int i = 1; i <= n; i ++)
{
cin >> m[i];
int cur = 0;
for (int j = 1; j <= m[i]; j ++)
{
cin >> a[i][j];
cur = max(cur, a[i][j]);
}
ma += cur;
}
dp[0][0] = 1;
for (int i = 1; i <= n; i ++) //n表示盒子的种类
for (int j = 1; j <= m[i]; j ++) //m[i]表示每一个盒子内的宝物的数目
for (int s = ma; s >= a[i][j]; s --)
if(dp[i - 1][s - a[i][j]])
dp[i][s] += dp[i - 1][s - a[i][j]];
int nu = 0, sum = 0;
for (int i = 1; i <= ma; i ++)
{
while(dp[n][i])
{
sum += i;
nu++;
dp[n][i]--;
if(nu == k) return printf("%d\n", sum), 0;
}
}
}
第二种方法:
维护一个最小堆:首先把第一个盒子内的宝石加入堆中,然后把堆中的元素从小到大取出存入另外一个数组b,与第二个盒子中的数组a进行求和,放入堆中,依次做下去即可。可以减枝的地方:因为是固定数组a的某个宝石,而且数组b是从小到大进行枚举,所以如果a[i] + b[j] >= que.top() 就可以break
代码:
#include <bits/stdc++.h>
using namespace std;
inline int read() {
int x = 0, neg = 1; char op = getchar();
while (!isdigit(op)) { if (op == '-') neg = -1; op = getchar(); }
while (isdigit(op)) { x = 10 * x + op - '0'; op = getchar(); }
return neg * x;
}
inline void print(int x) {
if (x < 0) { putchar('-'); x = -x; }
if (x >= 10) print(x / 10);
putchar(x % 10 + '0');
}
const int N = 10001;
priority_queue <int> pq;
int n, m, k, o, oo;
int a[N], b[N];
int main() {
n = read(), k = read();
pq.push(0);
while (n--) {
m = read();
for (int j = 1; j <= m; j++) {
a[j] = read();
}
sort(a + 1, a + m + 1);
o = 0;
while (!pq.empty()) {
b[++o] = pq.top();
pq.pop();
}
oo = 0;
for (int i = 1; i <= m; i++) {
for (int j = o; j >= 1; j--) {
if (oo < k) oo++, pq.push(a[i] + b[j]);
else if (pq.top() <= a[i] + b[j]) break;
else pq.pop(), pq.push(a[i] + b[j]);
}
}
}
o = 0;
while (!pq.empty()) {
b[++o] = pq.top();
pq.pop();
}
ll ans = 0;
for (int i = o; i >= 1; i--) {
ans += b[i];
}
printf("%lld\n", ans);
return 0;
}方法3: 暴力fft 把每个盒子当作一个多项式,宝石的数目当作指数,100个多项式相乘,取前k项(最小的k个幂次)的系数和即可。
代码暂时不会写。(留坑)
C题:
标程的做法没理解精髓,暂时先不写。
D题:
两元应该都会把。
三元的话,开两个树状数组,一个维护比某个数a[i]先出现并且比他小的数的个数n1。另外一个维护比他后出现且比他大的数的个数n2。这样对于这个数a[i]对答案的贡献就是n1 * n2。但这样做不好拓展,之前是算每个值得贡献(用比他小的个数 * 比他大的个数),我们换一个思路,也是开两个树状数组,第一个同样维护比某个数a[i]先出现并且比他小的数的个数n1,与此同时把第一个树状数组的区间(1,a[i]-1)的和sum 加到第二个树状数组的a[i]所对应的位置。什么意思呢?其实就是二元逆序对的逆序对个数。把第一个逆序对看成一个整体也就是把一对逆序对看成一个数。而第二个树状数组就是去算出每个a[i]之前有几对逆序对,这样就和二元一样了。
拓展到n元的话,就开n个树状数组就好了。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9+7;
const int maxn = 1e5+5;
int a[maxn],b[maxn];
ll sum[maxn][55];
void update(int x,int y,int n,int k)
{
while(x<=n)
{
sum[x][y]+=k;
sum[x][y]%=mod;
x+=x&-x;
}
}
ll query(int x,int y)
{
if(y==0)return 1ll*1;
ll ans = 0;
while(x)
{
ans+=sum[x][y];
x-=x&-x;
ans%=mod;
}
return ans;
}
int main()
{
int n,k;
scanf("%d%d", &n, &k);
for(int i = 1;i <= n; ++i)
scanf("%d",&a[i]),b[i] = a[i];
sort(b + 1,b + 1 + n);
int m = unique(b + 1,b + 1 + n) - b - 1;
for(int i = 1;i <= n;++i)
a[i] = lower_bound(b + 1,b + 1 + m, a[i]) - b;
ll ans = 0;
for(int i = 1;i <= n;++i)
{
for(int j = 1; j <= k; ++j)
{
ll p = query(a[i] - 1, j - 1);
if(j == k)ans+=p,ans%=mod;
update(a[i], j, n, p);
}
}
cout << ans << endl;
return 0;
}
京公网安备 11010502036488号