Codeforces 398D Instant Messanger 轻重点分类 模拟

题目大意:

给出三个数N, M, Q (1 <= N <= 5*10^4,  1 <= M <= 15*10^4, 1 <= Q <= 25*10^4)

表示现在有N个人在一组聊天系统中, 接下来给出一个O(1 <= O <= n), 然后是O个数代表着O个人初始时在线

然后是M组两个数, 代表两者初始的时候互为好友, 然后是Q次操作, 每次操作可能是某人上线或下线, 可以是两人建立好友关系或者取消好友关系, 还有可能是询问某人的好友列表当中现在在线的人的数量


大致思路:

刚开始直接模拟, 第8组test就超时了, 后来改用分块来做, 将度数高于sqrt(N)的点视为重点, 否则视为轻点, 用O[x]表示与点x相连的轻点中在线的个数, H[x]表示与x相连的重点的点的集合, 每次更新的时候根据当前点是轻点还是重点来更新, 但是还是跪在了第35组数据...(TLE)

后来学了下葱酱的写法, 因为在操作过程中点的度数一只在变化, 使用固定的sqrt(N)来作为判断标准并不太合适, 对于每个点, 用heavy[x]表示与点x相连的比点x重的点的个数, 并且将这些点用vector保存下来, 用L[x]表示与x相连的点中比x轻的染色(在线)的点的个数, 对于两个相连的点, 相对地用轻重来判断, 而不是用单纯的度数是否大于sqrt(N)来判断轻重


代码如下:

Result  :  Accepted     Memory  :  4200 KB     Time  :  249 ms

/*
 * Author: Gatevin
 * Created Time:  2015/3/3 14:10:55
 * File Name: Shana.cpp
 */
#include<iostream>
#include<sstream>
#include<fstream>
#include<vector>
#include<list>
#include<deque>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<cmath>
#include<ctime>
#include<iomanip>
using namespace std;
const double eps(1e-8);
typedef long long lint;

const int maxn = 50010;
int N, M, Q, O;
int x, y;
char op[4];
bool on[maxn];//表示某个点是否被染色(是否上线)
int heavy[maxn];//表示比该点重的相连的点数
vector <int> G[maxn];//G[x]中保存的是比x重的相连的点
int L[maxn];//L[x]表示比x轻的与x相连的点中被染色的点的数量

void add()
{
    scanf("%d %d", &x, &y);
    if(heavy[x] > heavy[y]) swap(x, y);
    heavy[x]++;//比x重的点数量+1
    G[x].push_back(y);
    L[y] += on[x];//比y轻的点如果在线L[y] += 1
    return;
}

void del()
{
    scanf("%d %d", &x, &y);
    int v = -1;
    for(unsigned int i = 0; i < G[x].size(); i++)
        if(G[x][i] == y) v = i;//找到存储的位置
    if(v == -1)//说明y比x轻
    {
        swap(x, y);
        for(unsigned int i = 0; i < G[x].size(); i++)
            if(G[x][i] == y) v = i;
    }
    if(on[x]) L[y]--;//删掉边(x, y)则比y轻的点被染色的点数量减少
    heavy[x]--;//比x重的点y删去
    G[x][v] = G[x][G[x].size() - 1];
    G[x].pop_back();
    return;
}

void online()
{
    scanf("%d", &x);
    on[x] = 1;
    for(unsigned int i = 0; i < G[x].size(); i++) L[G[x][i]]++;//所有比x重且相连的点对应的L[]加1
    return;
}

void offline()
{
    scanf("%d", &x);
    on[x] = 0;
    for(unsigned int i = 0; i < G[x].size(); i++) L[G[x][i]]--;//所有比x重且相连的店对应的L[]减1
    return;
}

void query()
{
    scanf("%d", &x);
    int ans = L[x];//比x轻的点
    for(unsigned int i = 0; i < G[x].size(); i++)
        if(on[G[x][i]]) ans++;//比x重的点
    printf("%d\n", ans);
    return;
}

int main()
{
    scanf("%d %d %d %d", &N, &M, &Q, &O);
    while(O--)
        scanf("%d", &x), on[x] = 1;
    while(M--)
        add();
    while(Q--)
    {
        scanf("%s", op);
        switch(op[0])
        {
            case 'A': add(); break;
            case 'D': del(); break;
            case 'O': online(); break;
            case 'F': offline(); break;
            case 'C': query(); break;
        }
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值