浴谷八连测-R8-B

Posted by Panda2134's Blog on November 6, 2017

题意

给定$n$个人的$m$个关系,形如:$x, y, z$,表示$x$比$y$的$z$学科成绩高。求1号学生在最坏和最好情况下的排名。
学科数目$\leq 3,2\leq n \leq 50000, 2\leq m \leq 10^6$。

思路

考场上面想到的是SPFA求最长路,于是就爆0了。这么做显然是有问题的,因为对排名的并列理解不清楚。e.g.如果2~5的分数是100,1的分数是90,那么2~5并列第一,1是第五,而非第二。
明确了这一点之后就可以建图了。我们想要知道一定比1号排名高的和一定比1号排名低的分别有多少人。先考虑$k=1$的情况。对于一个关系$(x,y)$,分别在两张图中连有向边$u\rightarrow v$和$v\rightarrow u$。再BFS就可以得知所求的量。于是最好的排名=一定比1号排名高的人数+1,最坏的排名=$n$-一定比1号排名低的人数。
对于$k>1$的情况,对每一科目一定比1号排名高的人一定比1号排名低的人分别取交集即可。
时间复杂度为$O[2k(n+m)]$。

代码

#include <bits/stdc++.h>
#define reg register
using namespace std;
const int MAXN = 5e4, MAXM = 1e6;
struct Edge{
    int v,next;
};
struct Solver {
    int e_ptr, head[MAXN+10];
    Edge E[(MAXM<<1)+10];
    bool vis[MAXN+10];
    void AddEdge(int u, int v){
        E[++e_ptr]=(Edge){v,head[u]}; head[u]=e_ptr;
    }
    void BFS() {
        queue<int> Q; 
        vis[1]=true; Q.push(1); 
        memset(vis, 0, sizeof(vis));
        while(!Q.empty()) {
            int u=Q.front(); Q.pop(); 
            for(int j=head[u];j;j=E[j].next) {
                int v=E[j].v;
                if(vis[v]) continue;
                vis[v]=true; Q.push(v);
            }
        }
    }
} sol[7];
int N, K, M;
inline int readint() {
    reg int f=1, r=0; reg char c=getchar();
    while(!isdigit(c)) { if(c=='-')f=-1; c=getchar(); }
    while(isdigit(c)) { r=r*10+c-'0'; c=getchar(); }
    return f*r;
}
void Init() {
    int u, v, z;
    N=readint(); K=readint(); M=readint();
    for(int i=1; i<=M; i++) {
        u=readint(); v=readint(); z=readint();
        sol[z].AddEdge(u, v);
        sol[z+K].AddEdge(v, u);
    }
}
void Work() {
    int Cnt1=0, Cnt2=0;
    static bool Fr[MAXN+10], Bk[MAXN+10];
    for(int i=1; i<=K*2; i++) sol[i].BFS();
    for(int i=1; i<=N; i++) {
        Fr[i]=true;
        for(int j=K+1; j<=2*K; j++) 
            Fr[i] = Fr[i] && sol[j].vis[i];
    }
    for(int i=1; i<=N; i++) {
        Bk[i]=true;
        for(int j=1; j<=K; j++) 
            Bk[i] = Bk[i] && sol[j].vis[i];
    }
    for(int i=1; i<=N; i++) {
        if(Fr[i]) Cnt1++;
        else if(Bk[i]) Cnt2++;
    }
    printf("%d %d", N-Cnt2, Cnt1+1);
}
int main() {
    Init(); Work();
    return 0;
}