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;
}