原题链接:https://codeforces.com/problemset/problem/1931/F
题意翻译
给出 n 个人进行的聊天室中,k 个人做的截图,每个截图中截图者都在第一位置,其后各位按照发帖时间前后确定。判断这些聊天室截图是否自洽(即能确定至少一种可能的发帖顺序)。
输入
第一行包含一个整数 t ( 1≤t≤10^4 ) - 输入测试用例的数量。下面是测试用例的说明。
每个测试用例描述的第一行包含两个整数 n 和 k ( 1≤k≤n≤2⋅10^5,n⋅k≤2⋅10^5 )--聊天参与者人数和发布屏幕截图的参与者人数。
接下来的 k 行包含参与者发布的截图描述。
第 i 行包含 n 个整数 aij ( 1≤aij≤n ,所有 aij 都是不同的)--参与者显示给参与者 ai0 的顺序,其中 ai0 --截图的作者。您可以在截图描述中将其显示在列表顶部。
保证所有测试用例的 n⋅k 总和不超过 2⋅10^5 。还可以保证所有截图的作者都是不同的。
输出
输出 t 行,每一行都是相应测试用例的答案。如果至少存在一种参与者顺序,可以获得所有 k 截图,则输出 "是"。否则,输出 "否"。
您可以用任何大小写(大写或小写)输出答案。例如,字符串 "yEs"、"yes"、"Yes "和 "YES "将被视为肯定回答。
输入输出样例
输入
10
5 1
1 2 3 4 5
4 4
1 2 3 4
2 3 1 4
3 2 1 4
4 2 3 1
6 2
1 3 5 2 4 6
6 3 5 2 1 4
3 3
1 2 3
2 3 1
3 2 1
10 2
1 2 3 4 5 6 7 8 9 10
10 9 8 7 6 5 4 3 2 1
1 1
1
5 2
1 2 3 5 4
2 1 3 5 4
3 3
3 1 2
2 3 1
1 3 2
5 4
3 5 1 4 2
2 5 1 4 3
1 5 4 3 2
5 1 4 3 2
3 3
1 3 2
2 1 3
3 2 1
输出
YES
YES
YES
YES
NO
YES
YES
YES
YES
NO
解题思路:
对于每张截图,第一个位置是作者自己不需要考虑,除了第一个元素之外后面的元素的先后顺序可以根据截图确定,对于每一张截图,我们从第二个位置开始分析,那么第二个位置的应该在第三个位置前面,我们可以从第二个位置向第三个位置连一条边,同理第三个位置可以向第四个位置连一条边,后面依此类推,注意第二个位置向第四个位置连边是没有必要的,因为2->3并且3>4那么根据传递性2和4先后顺序也就确定了,所以对于这样的中间边是没有意义的,连这些边反而会导致时间和空间复杂度都变的非常高,所以我们对于每张截图,只需要从第二个位置开始,每一个位置向当前位置的后一个位置连一条有向边即可,那么题目要求的就是是否存在一种顺序使得所有截图除去第一个位置的先后顺序都被满足,那么就是将所有边连上之后会形成一个有向图,问有向图是否存在一个拓扑序,也就是判断是否有环,如果存在一个拓扑序没有环输出YES,否则说明不是拓扑序有环输出NO,对于判断是否有环直接使用拓扑排序处理即可。
时间复杂度:拓扑排序O(n),输入k张截图,每张n个数O(n*k),最终时间复杂度O(n*k)。
空间复杂度:O(n)。
cpp代码如下:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N=2e5+10;
typedef long long LL;
int T,n,k;
int a[N];
int h[N],e[N],ne[N],idx;
int q[N],d[N];
void add(int a,int b)
{
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
bool top_sort()
{
int hh=0,tt=-1;
for(int i=1;i<=n;i++)
if(!d[i])q[++tt]=i;
while(hh<=tt)
{
auto t=q[hh++];
for(int i=h[t];i!=-1;i=ne[i])
{
int j=e[i];
if(--d[j]==0){
q[++tt]=j;
}
}
}
return tt==n-1;
}
void solve()
{
cin>>n>>k;
for(int i=0;i<=n;i++)h[i]=-1,d[i]=0;
idx=0;
for(int i=0;i<k;i++)
{
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=2;i<n;i++)add(a[i],a[i+1]),d[a[i+1]]++; //每一张截图从第二个位置开始连边
}
if(top_sort())cout<<"YES"<<'\n'; //拓扑排序判断是否有环
else cout<<"NO"<<'\n';
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>T;
while(T--)
{
solve();
}
return 0;
}