您的位置:首页 > Web前端

hihocoder #1136 : Professor Q's Software

2016-01-17 18:03 281 查看
时间限制:10000ms
单点时限:1000ms
内存限制:256MB


描述

Professor Q develops a new software. The software consists of N modules which are numbered from 1 to N. The i-th module will be started up by signal Si.
If signal Si is generated multiple times, the i-th module will also be started multiple times. Two different modules may be started up by the same
signal. During its lifecircle, the i-th module will generate Ki signals: E1,
E2, ..., EKi. These signals may start
up other modules and so on. Fortunately the software is so carefully designed that there is no loop in the starting chain of modules, which means eventually all the modules will be stoped. Professor Q generates some initial signals and
want to know how many times each module is started.



输入

The first line contains an integer T, the number of test cases. T test cases follows.
For each test case, the first line contains contains two numbers N and M, indicating the number of modules and number of signals that Professor Q generates initially.
The second line contains M integers, indicating the signals that Professor Q generates initially.
Line 3~N + 2, each line describes an module, following the format S, K, E1, E2,
... , EK. S represents the signal that start up this module. K represents the total amount of signals that are generated during the lifecircle of
this module. And E1 ... EK are these
signals.
For 20% data, all N, M <= 10

For 40% data, all N, M <= 103

For 100% data, all 1 <= T <= 5, N, M <= 105, 0 <= K <= 3, 0 <= S, E <= 105.
Hint: HUGE input in this problem. Fast IO such as scanf and BufferedReader are recommended.


输出

For each test case, output a line with N numbers Ans1, Ans2,
... , AnsN. Ansi is the number of
times that the i-th module is started. In case the answers may be too large, output the answers modulo 142857 (the remainder of division by 142857).

样例输入
3
3 2
123 256
123 2 456 256
456 3 666 111 256
256 1 90
3 1
100
100 2 200 200
200 1 300
200 0
5 1
1
1 2 2 3
2 2 3 4
3 2 4 5
4 2 5 6
5 2 6 7


样例输出
1 1 3
1 2 2
1 1 2 3 5


解题思路:这个题目的思路很简单,建立好图之后,DFS扫一遍就ok了。。。但看官方题解,这个算法还不是最优的,最优的是建立的图之后进行拓扑排序。。。仔细想想确实是这样的,只能说观察不够细致。。


解题思路

通过题意,我们首先可以确定,不同模块之间可以按照以下原则构成一个有向无环图:初始信号流,即 M 个初始信号,我们将其设定为模块
0
发出的信号
若模块
i
发出的信号能够使得模块
j
被激活,我们连接一条从
i
j
的有向边
(i,j)

比如数据:
3 2
123 256
123 2 456 256
456 3 666 111 256
256 1 90
对应的图为:

由于原题中给出的信号在0~10^5之间,因此我们可以用一个大小为10^5的数组
signList
来记录信号可以激活的模块,辅助我们构造整个图:
Input n
Input m
// 将初始数据流做为 module 0
For i = 1 .. m
Input sign
module[0].sendSign.push(sign)
End For
// 读取其他module的信息
For i = 1 .. n
Input sign
module[i].activeSign = sign
signList[ sign ].canActiveModule.push(i)
Input num
For j = 1 .. num
Input sign
module[i]sendSign.push(sign)
End For
End For
// 构造有向无环图
For i = 0 .. n
For sign in module[i].sendSign
For j in signList[ sign ].canActiveModule
addEdge(i, j)
End For
End For
End For
在得到有向无环图之后,一个简单的做法是直接在上面做一次DFS,去统计每个点被访问到的次数:
DFS(nowModule):
If (nowModule not 0) Then
activeCount[ nowModule ] = activeCount[ nowModule ] + 1
End If
For each j in (nowModule, j)
DFS(j)
End For
该算法的时间复杂度非常高,但由于本题没有设计专门针对的数据,所以在测试时也能通过所有的测试点。但是显然这不是我们要的最优算法,本题实际考察的算法为拓扑排序(toposort)。利用拓扑排序,在O(n + m)的时间内计算出所有点被访问的次数,具体的算法讲解可以参见hiho一下第48期在本题中,访问次数对应的为第48期题目中的病毒数量。因此我们在构造完图之后,可以使用同样的算法来解决:
// 在构造图时同时统计入度
For i = 0 .. n
For sign in module[i].sendSign
For j in signList[ sign ].canActiveModule
addEdge(i, j)
inDegree[j] = inDegree[j] + 1
End For
End For
End For

// 进行拓扑排序
tail = 0;
For i = 0 .. n  // 这里一定要从0开始,因为Module 0也是图中的点
If (inDegree[i] == 0) Then  // 入度为0的点
sequence[tail] = i
tail = tail + 1
End If
End For

activeCount[0] = 1  // 设定初始信号流的访问次数为1
activeCount[1 .. n] = 0
i = 0
While (i < tail) Then
nowModule = sequence[i]
For each j in (nowModule, j)
activeCount[j] = activeCount[j] + activeCount[ nowModule ]
inDegree[j] = inDegree[j] - 1
If (inDegree[j] == 0) Then
sequence[tail] = j
tail = tail + 1
End If
End For
i = i + 1
End While
最后再将
activeCount
数组依次输出即可。由于本题有多组数据,在实现时一定要注意初始化。

AC:


#include<cstdio>
#include<cstring>
using namespace std;

const int maxn = 1e5+5;
struct SOFTWARE
{
int start;
int num;
int E[5];
}sft[maxn];

struct VERTEX
{
int startE;
int first;
}ver[maxn];

struct Node
{
int id;
int next;
}list[maxn];
int n,m,sign[maxn],cnt,vis[maxn];

void add(int e,int id)
{
ver[e].startE = e;
list[cnt].id = id;
if(ver[e].first == -1) ver[e].first = cnt++;
else
{
list[cnt].next = ver[e].first;
ver[e].first = cnt++;
}
}

void init()
{
memset(vis,0,sizeof(vis));
memset(list,-1,sizeof(list));
memset(ver,-1,sizeof(ver));
cnt = 0;
}

void dfs(int cur,int e)
{
if(ver[e].first == -1) return;
for(int i = ver[e].first; i != -1; i = list[i].next)
{
int k = list[i].id;
vis[k]++;
for(int j = 1; j <= sft[k].num; j++)
dfs(k,sft[k].E[j]);
}
}

int main()
{
int t;
scanf("%d",&t);
while(t--)
{
init();
scanf("%d%d",&n,&m);
for(int i = 1; i <= m; i++)
scanf("%d",&sign[i]);
for(int i = 1; i <= n; i++)
{
scanf("%d%d",&sft[i].start,&sft[i].num);
add(sft[i].start,i);
for(int j = 1; j <= sft[i].num; j++)
scanf("%d",&sft[i].E[j]);
}
for(int i = 1; i <= m; i++)
dfs(0,sign[i]);
for(int i = 1; i <= n; i++)
printf("%d ",vis[i]);
printf("\n");
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  hihocoder