A题
☠速度要upup!
**题意** 给出n,求长度为n的一个数组 这个数组满足: 对于p的每个子数组,其中所有元素的**异或**均不小于该子数组中元素的数量题解
显示长度为len的任何子数组至少具有一个大于或等于len的元素即可
只要一大一小穿插在数组里,就好了
eg. 6 ==> 1 6 2 5 4 3
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAX = 1e5 + 10;
int main() {
int T;
scanf("%d", &T);
while(T--) {
int n;
scanf("%d", &n);
int cnt = 1;
for(int i = 1;; i++) {
if(cnt > n) break;
printf("%d ", i);
cnt++;
if(cnt > n) break;
printf("%d ", n - i + 1);
cnt++;
}
}
}
B题
☠思维卡住啦,怎么能没有想到呢!
题意
使用由n行和m列组成的网格表示的传送带,R代表向右,D代表向下。
如果行李在任何时候从网格中移出,则视为丢失。
确定更改的最小单元数,使无论其最初放置在哪个单元中,都可以最终进入该单元格(n,m)。
题解
放在(n - 1) * (m - 1) 的无论怎么移动(只能向下或者向右),都会移动到最后一列和最后一行
所以只要关注最后一列为’R’,最后一行为’D’
#include <bits/stdc++.h>
using namespace std;
char field[102][102];
int main() {
int T;
scanf("%d", &T);;
while(T--) {
int n, m;
scanf("%d%d", &n, &m);
memset(field, 0, sizeof(field));
for(int i = 0; i < n; i++) scanf("%s", field[i]);
int ans = 0;
for(int i = 0; i < n - 1; i++)
if(field[i][m - 1] == 'R') ans++;
for(int i = 0; i < m - 1; i++)
if(field[n - 1][i] == 'D') ans++;
printf("%d\n", ans);
}
}
C题
☠题意最关键,新的知识点牢记!
题意
对于每一个i∈{1,2,3,…,n},分别在它左右两侧找到距离最近且比pi大的元素pj在i和j中间连一
条无向边。如果找不到就不连。(注意是按下标连边,不是按值连边)
题解
对于一个元素i,如果它的两侧均有大于i的元素,那么必然会成环.
比如3 1 2,因为有两个峰,且这两个峰不相等,所以会成环。
那么显然只有在单峰的时候不满足。
答案为 全排列个数 = 单峰排列个数
即:n!-2^(n-1)
单峰排列
首先明确一点:n必须是“山峰”的最高点,然后依次是n-1,n-2,…,1。其中,n-1这个数可以放在左边的“山”,也可以放在右边的“山”,然后n-2亦是如此……直到1,都有两种放置方案,故最后的结果就是2(n-1)。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int MOD = 1e9 + 7;
ll qpow(ll x, ll n) {
ll ans = 1LL;
while(n) {
if(n & 1) ans = (ans * x) % MOD;
x = (x * x) % MOD;
n >>= 1;
}
return ans;
}
int main() {
int n;
scanf("%d", &n);
ll ans = 1;
for(int i = 1; i <= n; i++) {
ans *= i;
ans %= MOD;
}
ans -= qpow(2, n - 1);
ans %= MOD;
if(ans < 0) ans += MOD;
printf("%lld\n", ans);
}
D题
☠体会思维吧! + dp学习
题意
如果每个偶数长的平方子矩阵都具有奇数个1,则该二进制矩阵称为good。
修改矩阵满足good,如果不可以作为输出-1,否者输出改变的次数
题解
//问题转变(思维):
首先,如果min(n,m)> 3,则不存在解,因为这意味着网格包含至少一个4×4子矩阵,该子矩阵可以进一步分解为四个2×2子矩阵。
由于所有这两个2×2子矩阵都假定具有奇数个,因此它们的并集将具有偶数个。
现在,问题减少到改变单元的最少数量,以使每个2×2子矩阵具有奇数个子矩阵-这对于每个有效网格都可以实现。
所以,n,m大于1,小于等于4。(n==1时需要改变0个)
即矩阵形式:2*k, 3*k, k*\2, k*3(k >= 2)
又因为n <= m,那么只有 2*k1(k1 >= 2), 3*k2(k2 >= 3)
也就是说对应的我们只有两行的2 × 2矩阵,我们把这些矩阵包含的1的个数统计出来,按照是否需要改变分类为0和1两种情况,需要改变的矩阵是1,不需要改变的矩阵是0。
//dp
把每一列的状态存在num[]数组里,我们的目的是算出把整个num数组变为0的最小操作次数
假如num[i] = 2 = 10(二进制)说明第二列的2*2矩阵1的个数为偶数,需要改变
转移方程dp:
dp[i][j]代表到第i列 矩阵状态为j 时要进行的操作数
矩阵状态j: eg, j = 3 = 11(二进制),说明第一、二行开始的2*2矩阵都需要改变状态
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int MAX = 1e6 + 10;
const int INF = 1e9 + 7;
string a[MAX];
int field[3][MAX];
int dp[MAX][4];
int num[MAX];
int main() {
int n, m;
scanf("%d%d", &n, &m);
for(int i = 0; i < n; i++) cin >> a[i];
if(n == 1 || m == 1) cout << 0 << endl;
else if(n >= 4 && m >= 4) cout << -1 << endl;
else {
for(int i = 0; i < n; i++) {
for(int j = 0; j < m; j++)
field[i][j] = a[i][j] - '0';
}
for(int i = 0; i < n - 1; i++) {
for(int j = 0; j < m - 1; j++) {
int cnt = 0;
if(field[i][j] == 1) cnt++;
if(field[i + 1][j] == 1) cnt++;
if(field[i][j + 1] == 1) cnt++;
if(field[i + 1][j + 1] == 1) cnt++;
if(cnt % 2 == 0) num[j + 1] += (1 << i); //从第一列开始
}
}
int len = 1 << n;
//从第一列开始,第0列都为0
dp[0][0] = dp[0][1] = dp[0][2] = dp[0][3] = 0;
//第1 ~ m-1 列
for(int i = 1; i < m; i++) {
for(int j = 0; j < len; j++) { //j 是改变的方式
// eg, j = 0,不改变; j = 3, 改变一、二列
if(!j) dp[i][0 ^ num[i]] = dp[i - 1][0];
else dp[i][j ^ num[i]] = dp[i - 1][j] + 1;
}
}
int ans = INF;
//dp[m - 1][i] + 1,因为改变后面几列,第一列也会变+1
for(int i = 1; i < len; i++) {
ans = min(ans ,dp[m - 1][i] + 1);
}
ans = min(ans, dp[m - 1][0]);
cout << ans << endl;
}
}