题目链接
思路
大意就是说,给你一张拓扑图,表示员工间的优劣,然后给你a,b,问能升职人数分别为a,b时,一定能升职的人有几个,还有升职人数为b时,不可能升职的人数有几个。
那么只用求出每个点的最早的可能次序,和最晚的可能次序即可。
第一种方法O(n^2),对于每个点拓扑排序一遍,在保证这个不删的情况下,看看有多少个点可以删掉,这个就是最晚的次序。然后把所有边反向,再跑一边,总点数减去答案,就是最早的次序。
代码如下,936ms险过:
//UVA Live 7272
#include <iostream>
#include <iomanip>
#include <fstream>
#include <sstream>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <functional>
#include <numeric>
#include <string>
#include <set>
#include <map>
#include <stack>
#include <vector>
#include <queue>
#include <deque>
#include <list>
//#include <unordered_map>
using namespace std;
#define CLR(x,y) memset((x),(y),sizeof(x))
typedef long long ll;
typedef unsigned long long ull;
//typedef __int128 lll;
const int N=5000+100;
vector<int>g[2][N];
int d[2][N];
int in[N];
int a,b,e,p;
int topo(int dir, int x)
{
queue<int>q;
for(int i=0 ; i<e ; ++i)
{
in[i]=d[dir][i];
if(in[i]==0) q.push(i);
}
int cnt=0;
while(!q.empty())
{
int u=q.front(); q.pop();
if(u==x)
{
if(q.empty()) return cnt;
else
{
q.push(u);
continue;
}
}
cnt++;
for(auto &v:g[dir][u])
{
in[v]--;
if(in[v]==0) q.push(v);
}
}
return cnt;
}
int main()
{
while(scanf("%d%d%d%d",&a,&b,&e,&p)!=EOF)
{
for(auto &ite:g[0])ite.clear();
for(auto &ite:g[1])ite.clear();
CLR(d,0);
for(int i=0 ; i<p ; ++i)
{
int u,v;
scanf("%d%d",&u,&v);
g[0][u].push_back(v);
g[1][v].push_back(u);
d[0][v]++;
d[1][u]++;
}
int ans_a=0,ans_b=0,ans_c=0;
for(int i=0 ; i<e ; ++i)
{
int t=topo(0,i);
if(t<a)ans_a++;
if(t<b)ans_b++;
t=topo(1,i);
t=e-t;
if(t>b)ans_c++;
}
printf("%d\n%d\n%d\n",ans_a,ans_b,ans_c);
}
return 0;
}
第二种方法,用bitset。
记忆化dfs搜出每个点的前驱和后继的点的个数。
bitset<N>pre[N], nxt[N]
,其中pre[i]
表示点i的后继的点的集合。
pre[u]|=pre[v]
,nxt把边反向,同理。
这样pre[i].count()
就表示点i最早出现的次序。n-nxt[i].count()
就表示最晚出现的次序。
复杂度为 N+M*bitset_or
bitset每次运算的复杂度大约为N/32,故总复杂度为n^2/32
代码如下,25ms:
//UVA Live 7272
#include <iostream>
#include <iomanip>
#include <fstream>
#include <sstream>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <functional>
#include <numeric>
#include <string>
#include <set>
#include <map>
#include <stack>
#include <vector>
#include <queue>
#include <deque>
#include <list>
#include <bitset>
//#include <unordered_map>
using namespace std;
#define CLR(x,y) memset((x),(y),sizeof(x))
typedef long long ll;
typedef unsigned long long ull;
//typedef __int128 lll;
const int N=5000+100;
vector<int>g[2][N];
bitset<N>st[2][N];//st[0]:nxt, st[1]:pre
int a,b,e,p;
bool vis[2][N];
void dfs(int dir, int u)
{
if(vis[dir][u])return;
vis[dir][u]=1;
st[dir][u].reset();
st[dir][u][u]=1;
for(auto &v:g[dir][u])
{
dfs(dir,v);
st[dir][u]|=st[dir][v];
}
}
int main()
{
while(scanf("%d%d%d%d",&a,&b,&e,&p)!=EOF)
{
for(auto &ite:g[0])ite.clear();
for(auto &ite:g[1])ite.clear();
CLR(vis,0);
for(int i=0 ; i<p ; ++i)
{
int u,v;
scanf("%d%d",&u,&v);
g[0][u].push_back(v);
g[1][v].push_back(u);
}
int ans_a=0,ans_b=0,ans_c=0;
for(int i=0 ; i<e ; ++i)
{
dfs(1,i);
dfs(0,i);
int t=e-st[0][i].count();
if(t<a)ans_a++;
if(t<b)ans_b++;
t=st[1][i].count();
if(t>b)ans_c++;
}
printf("%d\n%d\n%d\n",ans_a,ans_b,ans_c);
}
return 0;
}