P2058 海港

首先上来要分析复杂度。

这道题的的数据规模如下

1n105,ki3×105,1x(i,j)105,1t(i1)ti1091 \le n \le 10^5,\sum{ki} \le 3\times10^5,1\le x(i,j) \le 10^5, 1 \le t(i-1)\le ti \le 10^9

如果用纯暴力的话:

首先是空间绝对爆炸,但先不管这个

来计算一下时间复杂度:

n是小于等于10510^5,其中ki的数据规模给的很隐晦,因为是ki3×105\sum{ki} \le 3 \times 10^5,所以ki最多只能3×1053 \times 10^5

然后暴力的话时间复杂度就是O(nk)O(nk),大概就是101010^{10}

看来不仅爆空间,时间也爆

升级一小点:

用队列维护(一定得用STL的了,因为空间太大,需要动态内存)

queue里存结构体,结构体里存时间加人数加记录的国籍数组

时间超了就扔,最大储存不过86400项

额,负优化了

再看看为什么ki给的那么隐晦,绝对有出题人的小心思

还是用队列维护(当然还是用STL)

不过往队列里存人,不存船

每个人用结构体存,里面存一个国籍加时间

往队列加的时候做去重操作

估一下,因为ki3×105\sum{ki} \le 3 \times 10^5,所以在每一项都不同的情况下最多800项(1加到800等于320400)

时间超了的人照样扔

然后每次都遍历一下队列,数数在队列还有多少个国籍

算一算时间复杂度,大概是O(nk)O(nk)

k不超800,最多循环8×1078 \times 10^7

可以啊,正巧压 10810^8,perfect!

但是正好压10810^8,也不好,万一常数大一点给卡常了怎么办 毕竟众所周知CCF的老爷机子有多慢

于是乎,我们再优化

用数组来储存目前这个国籍的有多少人,属于哈希思想

用一个变量来储存每次有多少国籍

队列每增加一个人便在对应的国籍上++,并且如果加完后只有他1人,说明新添了个国籍,便在国籍数上++

在队列里筛人的时候如果目前筛掉了并且剩下只有0人的话,说明有一个国籍没人了,便在国籍数上--

每次输出国籍数即可

这下再算算时间复杂度,只有O(n)O(n)

perfect!

剩下的,只有代码了:

#include <iostream>
#include <queue>
#include <cstring>
using namespace std;

struct people{
    int t,k;
};

queue<people>q;

int a[3*100000];

int main(){
    int n,ans=0;
    scanf("%d",&n);
    for (int i=0;i<n;i++){
        int x,y;
        scanf("%d%d",&x,&y);
        while ((!q.empty()) && x-q.front().t>=86400){
            a[q.front().k]--;
            if (!a[q.front().k]) ans--;
            q.pop();
        }
        people p;
        p.t=x;
        for (int j=0,k;j<y;j++){
            scanf("%d",&k);
            p.k=k;
            q.push(p);
            a[k]++;
            if (a[k]==1) ans++;
        }
        printf("%d\n",ans);
    }
    return 0;
}