Contents

2019牛客第十场F题详解_算法日常[13/100]

2019牛客第十场F题详解

题目

2019牛客第十场F题详解

https://cdn.jsdelivr.net/gh/wolfdan666/BlogPic/%E7%AE%97%E6%B3%95/2019%E5%B9%B4%E5%A4%9A%E6%A0%A1/%E7%89%9B%E5%AE%A2/%E7%AC%AC%E5%8D%81%E5%9C%BA/F_1.png https://cdn.jsdelivr.net/gh/wolfdan666/BlogPic/%E7%AE%97%E6%B3%95/2019%E5%B9%B4%E5%A4%9A%E6%A0%A1/%E7%89%9B%E5%AE%A2/%E7%AC%AC%E5%8D%81%E5%9C%BA/F_2.png

题解

我的分析请看代码 https://cdn.jsdelivr.net/gh/wolfdan666/BlogPic/%E7%AE%97%E6%B3%95/2019%E5%B9%B4%E5%A4%9A%E6%A0%A1/%E7%89%9B%E5%AE%A2/%E7%AC%AC%E5%8D%81%E5%9C%BA/F_an.png

本来以为发现了更快更简单的做法,结果想写成思路给大家看,然后不断想怎么表述这个算法,想着想着发现这是一个假的超快AC算法,然后大家有兴趣的话可以看我的AC代码二的分析(果然写blog做搬运工也是有收获的)

AC代码

 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>
#define pb push_back
using namespace std;
const int maxn=6e5+5;
const int N=6e5+3;
int a[maxn],b[maxn],cnt[maxn],ans,n,r;
vector <int> h[maxn];
multiset <int> s;
void add(int x){
    auto p=s.find(cnt[x]);
    s.erase(p); cnt[x]++;
    s.insert(cnt[x]);
}
void del(int x){
    auto p=s.find(cnt[x]);
    s.erase(p); cnt[x]--;
    s.insert(cnt[x]);
}
int main(){
    cin >> n >> r;
    for (int i=0;i<n;i++){
        cin >> a[i] >> b[i];
        /* 统一右移2*r */
        a[i]+=r*2; b[i]+=r*2;
    }
    for (int i=0;i<n;i++){
        /*打a[i]的时候,把a[i]-r,a[i]+r也叠加上a[i]上面的值,
        这样就能使得a[i]表示打a[i]能获得的总值*/
        h[a[i]-r].pb(b[i]);
        h[a[i]].pb(b[i]);
        h[aa[i]+r].pb(b[i]);
        cnt[b[i]]++; cnt[b[i]-r]++; cnt[b[i]+r]++;
    }
    for (int i=r;i<=N-r;i++) s.insert(cnt[i]);
    for (int i=r;i<=N-r;i++){
        /*得到中间打这里能够得到的个数*/
        int ret=(int)h[i].size();
        /*暂时删除相关的所有列值*/
        for (auto x:h[i]) del(x),del(x-r),del(x+r);
        /*然后得到当下最大的3列值*/
        auto p=s.rbegin();
        ans=max(ans,ret+(*p));
        /*再把列值插回去*/
        for (auto x:h[i]) add(x),add(x-r),add(x+r);
    }
    cout << ans << endl;
}

能更快AC的假算法

 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
48
49
50
51
52
53
/*直接行列序列化排序的做法,真滴是好想法!*/

/*2019年8月18日20:26:30 发现这是一个假做法!靠着数据水才过的!因为这里只比较了最大的3个
行值,就去比较列值了!

因为可能有一种情况就是选第4大行,但是取到的列值更多,这应该是有可能出现的,
总之这种做法虽然能过   但是是有可能遗漏情况的假算法!

必须要枚举到所有的行
*/
#include<bits/stdc++.h>
using namespace std;
const int M =1e5 + 10;
int num[M *3];
int  n , r , ans;
int cnt[M * 3];
struct node{
    int x , y;
    int id;
    bool operator<(node d)const{
        return x > d.x;
    }
}a[M] , b[M];

int main(){
    ios::sync_with_stdio(0); cin.tie(0);
    cin >> n >>r;
    for(int i =1;i <= n;i++){
        cin >> a[i].x >> a[i].y;
        a[i].x++;
        a[i].y++;
        num[a[i].x]++;
    }
    /*枚举每个行坐标,相加从上到下3行的值-->二维降到一维*/
    for(int i = 1;i <= 100000;i++){
        /*之所以放在最左边是因为i是从最左边开始,免去判断*/
        b[i].x = num[i] + num[i +r] + num[i +r +r];
        b[i].id = i;
    }
    /*行值获利大到小排序*/
    sort(b + 1 ,b + 100001);
    for(int i = 1;i <= 3;i++){
        memset(cnt , 0 ,sizeof(cnt));
        for(int j = 1;j <= n;j++){
            /*除去原来的行(3次行值取max)以外的其他列的值的统计*/
            if(a[j].x != b[i].id && a[j].x != b[i].id +r  && a[j].x != b[i].id +r * 2) cnt[a[j].y]++;
        }
        int sum = 0;
        for(int j = 1; j < M;j++) sum = max( sum , cnt[j] + cnt[j +r] +cnt[j + r + r]);
        ans = max(ans , sum + b[i].x);
    }
    cout << ans <<endl;
}

每天叨叨一句

天将降大任于斯人也,必先苦其心志,劳其筋骨,饿其体肤,空乏其身,行拂乱其所为,所以动心忍性,曾益其所不能