A
链接:https://www.nowcoder.com/acm/contest/131/A
来源:牛客网
题目描述
ZZT 创造了一个队列 Q。这个队列包含了 N 个元素,队列中的第 i 个元素用 Q
i 表示。Q
1 表示队头元素,Q
N 表示队尾元素。队列中的元素是 N 的一个全排列。
ZZT 需要在这个队列上执行 P 次操作,操作分两种:
FIRST X: 将元素 X 移到队头。
LAST X: 将元素 X 移到队尾。
在 P 次操作之后,ZZT 想知道队列中的元素的排列方式,由于他最近很忙,因此需要请你帮他解决这个问题。
ZZT 需要在这个队列上执行 P 次操作,操作分两种:
FIRST X: 将元素 X 移到队头。
LAST X: 将元素 X 移到队尾。
在 P 次操作之后,ZZT 想知道队列中的元素的排列方式,由于他最近很忙,因此需要请你帮他解决这个问题。
输入描述:
第一行输入一个正整数 N,表示队列的大小。 第二行输入 N 个正整数,Q1, Q2, Q3, ... ..., QN,Qi 表示队列中的第 i 个元素。保证这 N 个数是 N 的一个全排列。 第三行输入一个正整数 P,表示接下来要进行的操作次数。
接下来 P 行,第 i 行输入一个字符串 S
i 以及一个正整数 X
i,表示一次操作。
1 ≤ N ≤ 10
5. 1 ≤ Q
i ≤ N. 1 ≤ P ≤ 10
5. S
i
{ “FIRST”, “LAST” }. 1 ≤ X
i ≤ 10
5.
![](https://i-blog.csdnimg.cn/blog_migrate/8e6a1e4ef7fb2601312554d1e9086482.png)
输出描述:
输出 N 个正整数,表示 P 次操作之后的队列。
思路:
比较容易想到的是直接模拟链表的操作。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef vector<int> vi;
typedef vector<vi> vii;
typedef vector<ll> vll;
const int MAXN = 1e6 + 10;
const ll INF = 0x3f3f3f3f;
const ll MOD = 1e9 + 7;
const double eps = 1e-8;
int n, m, k;
vi loc, p, le, rg;
void connect(int u, int v){
rg[u] = v;
le[v] = u;
}
int main(void)
{
ios::sync_with_stdio(false);
cin.tie(0);
cout << setprecision(10) << fixed;
cin >> n;
p.resize(n + 1);
le.resize(n + 2);
rg.resize(n + 2);
loc.resize(n + 1);
for(int i = 1; i <= n; i++){
cin >> p[i];
loc[p[i]] = i;
connect(i - 1, i);
}
rg[n] = n + 1;
le[n + 1] = n;
cin >> m;
while(m--){
string op;
int v;
cin >> op >> v;
if(op[0] == 'F'){
if(rg[0] == loc[v])
continue;
int l = le[rg[0]];
int r = rg[0];
connect(le[loc[v]], rg[loc[v]]);
connect(l, loc[v]);
connect(loc[v], r);
}
else{
if(le[n + 1] == loc[v])
continue;
int l = le[n + 1];
int r = rg[le[n + 1]];
connect(le[loc[v]], rg[loc[v]]);
connect(l, loc[v]);
connect(loc[v], r);
}
}
for(int i = rg[0], j = 0; i <= n; i = rg[i], j++){
if(j)
cout << " ";
cout << p[i];
}
cout << endl;
cerr << "execute time : " << (double)clock() / CLOCKS_PER_SEC << endl;
return 0;
}
B
链接:https://www.nowcoder.com/acm/contest/131/B
来源:牛客网
题目描述
矩阵 M 包含 R 行 C 列,第 i 行第 j 列的值为 M
i,
j。
请寻找一个子矩阵,使得这个子矩阵的和最大,且满足以下三个条件:
子矩阵的行数不能超过 X 行。
子矩阵的列数不能超过 Y 列。
子矩阵中 0 的个数不能超过 Z 个。
请输出满足以上条件的最大子矩阵和。
请寻找一个子矩阵,使得这个子矩阵的和最大,且满足以下三个条件:
子矩阵的行数不能超过 X 行。
子矩阵的列数不能超过 Y 列。
子矩阵中 0 的个数不能超过 Z 个。
请输出满足以上条件的最大子矩阵和。
输入描述:
第一行输入五个整数 R,C,X,Y,Z。
接下来 N 行,每行输入 M 个整数,第 i 行第 j 列的整数表示 M
i,
j。
1 ≤ R,C ≤ 500.
1 ≤ X ≤ R. 1 ≤ Y ≤ C. 1 ≤ Z ≤ R x C. -10
9 ≤ M
i,j ≤ 10
9
输出描述:
输出满足以上条件的最大子矩阵和。
思路:
枚举矩阵的上下边界使其满足行数限制,对每个上下边界用单调队列O(n)处理列数限制和0的个数限制。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef vector<int> vi;
typedef vector<vi> vii;
typedef vector<ll> vll;
const int MAXN = 500 + 10;
const ll INF = 0x3f3f3f3f;
const ll MOD = 1e9 + 7;
const double eps = 1e-8;
int n, m, x, y, k;
int mat[MAXN][MAXN], cnt[MAXN][MAXN], pre_cnt[MAXN];
ll sum[MAXN][MAXN], pre_sum[MAXN];
int main(void)
{
ios::sync_with_stdio(false);
cin.tie(0);
cout << setprecision(10) << fixed;
cin >> n >> m >> x >> y >> k;
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
cin >> mat[i][j];
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++){
sum[i][j] = sum[i - 1][j] + mat[i][j];
cnt[i][j] = cnt[i - 1][j] + !mat[i][j];
}
ll res = 0;
for(int i = 1; i <= n; i++)
for(int j = i; j <= n && j - i + 1 <= x; j++){
memset(pre_sum, 0, sizeof(pre_sum));
memset(pre_cnt, 0, sizeof(pre_cnt));
for(int t = 1; t <= m; t++)
pre_cnt[t] = pre_cnt[t - 1] + cnt[j][t] - cnt[i - 1][t];
for(int t = 1; t <= m; t++)
pre_sum[t] = pre_sum[t - 1] + sum[j][t] - sum[i - 1][t];
deque<ll> q;
q.push_front(0);
for(int t = 1; t <= m; t++){
while(!q.empty() && (t - q.front() > y || pre_cnt[t] - pre_cnt[q.front()] > k))
q.pop_front();
res = max(res, pre_sum[t] - pre_sum[q.empty() ? t - 1 : q.front()]);
if(pre_cnt[t] - pre_cnt[t - 1] <= k){
while(!q.empty() && pre_sum[q.back()] >= pre_sum[t])
q.pop_back();
q.push_back(t);
}
}
}
cout << res << endl;
cerr << "execute time : " << (double)clock() / CLOCKS_PER_SEC << endl;
return 0;
}
C
链接:https://www.nowcoder.com/acm/contest/131/C
来源:牛客网
题目描述
有一棵树包含 N 个节点,节点编号从 1 到 N。节点总共有 K 种颜色,颜色编号从 1 到 K。第 i 个节点的颜色为 A
i。
F i 表示恰好包含 i 种颜色的路径数量。请计算:
F i 表示恰好包含 i 种颜色的路径数量。请计算:
![](https://i-blog.csdnimg.cn/blog_migrate/b1f5a63f3f732ea3fe6831f59a5e5617.png)
输入描述:
第一行输入两个正整数 N 和 K,N 表示节点个数,K 表示颜色种类数量。 第二行输入 N 个正整数,A1, A2, A3, ... ..., AN,Ai 表示第 i 个节点的颜色。
接下来 N - 1 行,第 i 行输入两个正整数 U
i 和 V
i,表示节点 U
i 和节点 V
i 之间存在一条无向边,数据保证这 N-1 条边连通了 N 个节点。
1 ≤ N ≤ 50000. 1 ≤ K ≤ 10. 1 ≤ A
i ≤ K.
输出描述:
输出一个整数表示答案。
思路:
看到k较小,状压枚举包含某些颜色的结果,要想获得正好包含某些颜色的结果还需要暴力容斥一下。
最后不要忘了加上n * 131(这个也是1种颜色的路径)。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef vector<int> vi;
typedef vector<vi> vii;
typedef vector<ll> vll;
const int MAXN = 1e6 + 10;
const ll INF = 0x3f3f3f3f;
const ll MOD = 1e9 + 7;
const double eps = 1e-8;
int n, m, k;
vi cor, pick, cnt, vis;
vii g;
vll dp, fin, f;
ll recur(int u){
vis[u] = 1;
ll res = 1;
for(int i = 0; i < (int)g[u].size(); i++){
int v = g[u][i];
if(vis[v] || !pick[cor[v]])
continue;
res += recur(v);
}
return res;
}
int main(void)
{
ios::sync_with_stdio(false);
cin.tie(0);
cout << setprecision(10) << fixed;
cin >> n >> k;
cor.resize(n);
g.resize(n, vi(0));
for(int i = 0; i < n; i++){
cin >> cor[i];
cor[i]--;
}
for(int i = 1; i < n; i++){
int u, v;
cin >> u >> v;
u--;
v--;
g[u].push_back(v);
g[v].push_back(u);
}
dp.resize(1 << k);
cnt.resize(1 << k);
for(int i = 1; i < (1 << k); i++){
pick.clear();
pick.resize(k);
for(int j = 0; j < k; j++){
if(i & (1 << j)){
pick[j] = 1;
cnt[i]++;
}
}
vis.clear();
vis.resize(n);
for(int j = 0; j < n; j++){
if(vis[j] || !pick[cor[j]])
continue;
ll count = recur(j);
dp[i] = (dp[i] + ((count * (count - 1)) >> 1)) % MOD;
}
}
fin.resize(1 << k);
for(int i = 1; i < (1 << k); i++)
fin[i] = dp[i];
f.resize(k + 1);
for(int i = 1; i < (1 << k); i++){
for(int j = 1; j < i; j++){
if((i | j) != i)
continue;
if((cnt[i] - cnt[j]) & 1)
fin[i] = (fin[i] - dp[j] + MOD) % MOD;
else
fin[i] = (fin[i] + dp[j]) % MOD;
}
f[cnt[i]] = (f[cnt[i]] + fin[i]) % MOD;
}
ll res = 0, c = 131;
for(int i = 1; i <= k; i++, c = c * 131 % MOD)
res = (res + f[i] * c) % MOD;
cout << res + n * 131 << endl;
cerr << "execute time : " << (double)clock() / CLOCKS_PER_SEC << endl;
return 0;
}
未来可期。