cf815 div2 (A题fst的一场)

下述题目中默认n为10^5级别,n*m为10^5-10^6级别,整数范围10^9级别。

A

题意:有两个分数a/b,c/d,我们一次操作可以给a,b,c,d乘上任意数(b,d不可以乘0),问最少多少次操作两个分数相等。

思路:两个分数相等即a*d=b*c,同时可以注意到我们可以给a和c都乘0,因此必然操作数最多为2。接下来只需要判断什么时候为0,1,2即可。

  • 0:显然a*d=b*c时就是0
  • 1:只需要1次操作,说明我们只能乘1次,便使得a*d=b*c。假如乘的数是k,那么最开始就有a*d = k*b*c或者b*c=k*a*d。
  • 2:剩余的情况显然就是2次

碎碎念:A题判断1次的时候我写的是a*d/b%c==0||b*c/a%d==0,蠢蠢牛马了属于是。。。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
#define pb push_back
typedef vector<int> vi;
typedef long long ll;
const int maxn=100005;

void solve(){
ll a,b,c,d;
cin >> a >> b >> c >> d;
a*=d;b*=c;
if(a==b) puts("0");
else if(a==0||b==0||a%b==0||b%a==0) puts("1");
else puts("2");
}

int main(){
int tt;
cin >> tt;
while (tt--){
solve();
}
return 0;
}

B

题意:给定有n个数的a数组,计算a数组的所有子串(连续的一段)中的最大beauty值。beauty值的定义为:max(a1,a2,…,al−1,ar+1,ar+2,…,an)−min(a1,a2,…,al−1,ar+1,ar+2,…,an)+max(al,…,ar)−min(al,…,ar).

其中al-ar为a数组的子串。

思路:读完题意第一反应就是选出数组中的第一大和第二大,还有第一小和第二小,这样一定是最优的,但是能不能保证一定能选到呢,经过分析可知,这四个数是一定可以选到的。

由上述思路,我们设这四个数的位置是i,j,m,n。无论他们相对位置如何,我们总可以选出一个子串包含一个大的(第一大或者第二大),和一个小的(第一小或者第二小)。因为我们可以先选一个大的(两个中选一个),再将范围向左或者向右延申,由于只剩下另外一个大的,所以两边一定有一边可以延申到小的,然后就停在这,这即为选出的子串。

考虑子串中的大小,显然我们为子串选的那个大的和小的,分别是这个子串的极大值和极小值。(因为就算有更大的和更小的也在子串外)。对于子串外的那部分同理,所以这四个数是一定可以选到的。

综上beauty值就是a数组从小到大排序后的a[n]+a[n-1]-a[1]-a[2]。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
#define pb push_back
typedef vector<int> vi;
typedef long long ll;
const int maxn=100005;

int a[maxn];
void solve(){
int n;
cin >> n;
rep(i,1,n){
cin >> a[i];
}
sort(a+1,a+n+1);
cout << a[n]+a[n-1]-a[1]-a[2] << '\n';
}

int main(){
int tt;
cin >> tt;
while (tt--){
solve();
}
return 0;
}

C

题意:给定n*m的矩阵,每个格子是1或者0。每次操作可以选定一个L型区域(2*2矩阵去掉一个格子),并且要求里面要有1,那么我们这次操作会将里面的1全部变成0,问最多我们能操作多少次。

思路:这题问的是最多,那么显然每个1都要倾尽其力(警觉×),一般这种矩阵题思路要么就dfs或者从小的块入手。dfs的话我们没有什么入手点,因为从开头dfs不一定最优,所以我们考虑从一些小的格子找入手点。

我们注意到当两个0能出现在一个L型中时(即相邻或者对角),选中一个包含这两个0和一个1的L型,会使得这个1最大化的利用了,同时这个1会在这次操作中变成0。因此我们又得到了新的满足条件的两个0。显然这个过程是不会中断的,因此只要我们初始化有两个相邻或对角的0,便可以使得这里面的1都被最大化的利用。即操作数opt=cnt1(矩阵中1的个数)。而开局这样的0的时候,我们大概率要开一组两个1的L型,即浪费了一次,因此opt=cnt1-1。

另外注意特判全是1的情况:这个开局会直接3个1的L型,浪费两个1,opt=cnt1-2。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
#define pb push_back
typedef vector<int> vi;
typedef long long ll;
const int maxn=100005;

int dir[8][2]={{1,0},{-1,0},{0,1},{0,-1},{1,1},{1,-1},{-1,1},{-1,-1}};
void solve(){
int n,m,cnt0=0,ok=0;
string s[505];
cin >> n >> m;
rep(i,0,n-1){
cin >> s[i];
}
rep(i,0,n-1){
rep(j,0,m-1){
if(s[i][j]=='0'){
cnt0++;
if(ok==0){
rep(k,0,7){
int ii=i+dir[k][0];
int jj=j+dir[k][1];
if(ii>=0&&ii<=n-1&&jj>=0&&jj<=m-1){
if(s[ii][jj]=='0') ok=1;
}
}
}
}
}
}
if(cnt0==n*m) cout << 0 << '\n';
else if(ok==0&&cnt0!=0) cout << n*m-cnt0-1 << '\n';
else if(ok==0&&cnt0==0) cout << n*m-cnt0-2 << '\n';
else cout << n*m-cnt0 << '\n';
}

int main(){
int tt;
cin >> tt;
while (tt--){
solve();
}
return 0;
}

D1

题意:给定有n个数的a数组,从中选出一个子序列b,这里b中的数是下标数,如选出a1,a2,a4,则b中为[1,2,4]。设b数组有m个数,若对于任意0<=i<m-1,有a[b[i]]^b[i+1]<a[b[i+1]]^b[i],则该序列叫做美丽的。求最长的美丽子序列的长度。

a[i]<=200

思路:求最长子序列,显然是dp。但是显然dp的复杂度是n^2。主要时间浪费在为每个i寻找匹配的j的地方,因此我们结合给定条件考虑可不可以缩小范围选j。

D1给定的特殊条件为a[i]<=200,这就保证了每次b[i+1](其实是一个下标)和a[b[i]](这才是a数组中的数)异或时,我们对b[i+1]的改变不会超过256。因为a[i]<=200,即它最多只有8位,因此异或最多只会改变b[i+1]的后八位。因此当b[i+1]-b[i]>256的时候,这个条件显然不成立(因为不管异或的怎么样,后8位之前的那些数b[i+1]要大于b[i])。因此我们每次为i找匹配的j时,从max(0,i-256)开始匹配就可以。剩下就是标准的最长子序列dp做法了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
#define pb push_back
typedef vector<int> vi;
typedef long long ll;
const int maxn=300005;

int a[maxn];
void solve(){
int n;
cin >> n;
rep(i,0,n-1){
scanf("%d",&a[i]);
}
vi dp(n+5,1);
rep(i,1,n-1){
rep(j,max(0,i-256),i-1){
if((a[j]^i)<(a[i]^j)){
dp[i]=max(dp[j]+1,dp[i]);
}
}
}
int ans=0;
rep(i,1,n){
ans=max(ans,dp[i]);
}
cout << ans << '\n';
}

int main(){
int tt;
cin >> tt;
while (tt--){
solve();
}
return 0;
}