题目描述:
题目链接: Codefoces E.Exchanging Gifts
题面:
After the dress rehearsal of CCPC Harbin Site 2019, m contestants are still in the contest arena. They are taking photos, discussing the problems, and exchanging gifts.
Initially, everyone has exactly one gift in their hand. Note that some contestants may have the same type of gifts. Specifically, the type of the gift in the i-th contestant’s hand can be represented as a positive integer gi. Two contestants i and j (1≤i,j≤m) share the same type of gifts if and only if gi=gj holds.
There can be many rounds of gift exchanging between these m contestants. In each round, two contestants may exchange their gifts with each other. Note that a pair of contestants can exchange gifts multiple times if they like. In the end, there will still be exactly one gift in each contestant’s hand.
Let’s denote hi as the type of gift in the i-th contestant’s hand in the end. If gi≠hi holds, the i-th contestant will be happy, because they have a different type of gift, otherwise they will be unhappy. Your task is to write a program to help them exchange gifts such that the number of happy contestants is maximized. For example, if g=[3,3,2,1,3] and h=[1,2,3,3,3], there will be 4 happy contestants.
Since m can be extremely large, you will be given n sequences s1,s2,…,sn, and the sequence g is equal to sn. The i-th (1≤i≤n) sequence will be given in one of the following two formats:
“1 k q[1…k]” (1≤k≤
1
0
6
10^6
106, 1≤
q
i
q_i
qi≤
1
0
9
10^9
109): It means
s
i
s_i
si=[
q
1
,
q
2
,
…
,
q
k
q_1,q_2,…,q_k
q1,q2,…,qk].
“2 x y” (1≤x,y≤i−1): It means
s
i
=
s
x
+
s
y
s_i=s_x+s_y
si=sx+sy. Here “+” denotes concatenation of sequences, for example [3,3,2]+[2,2,3,3]=[3,3,2,2,2,3,3].
Input
The input contains multiple cases. The first line of the input contains a single integer T (1≤T≤10000), the number of cases.
For each case, the first line of the input contains a single integer n (1≤n≤ 1 0 6 10^6 106), denoting the number of sequences. Each of the next n lines describes a sequence in one of the two formats defined in the problem statement, where the i-th (1≤i≤n) line describes the sequence s i s_i si.
It is guaranteed that the sum of all n over all cases does not exceed 1 0 6 10^6 106, and the sum of k over all cases does not exceed 1 0 6 10^6 106. It is also guaranteed that no sequence has a length that exceeds 1 0 18 10^{18} 1018.
Output
For each case, print a single line containing a single integer denoting the maximum number of happy contestants.
题目大意:
给你一个序列, 如果能够通过交换元素,使得序列原位置的元素不一样,那么快乐值+1。例如[3,3,2,1,3] 通过交换位置变成[1,2,3,3,3],则快乐值为4。现在给你n个序列,一个序列有两种情况:一是直接给你,二是由其前面的两个序列合并而成。现在求第n个序列可能的最大快乐值。
输入输出格式:
多组数据(1≤T≤10000)。每一组第一行为n(1≤n≤
1
0
6
10^6
106),接下来n行,每行第一数字为1或2,代表情形。为1时,第二个数位k,表示该序列个数,接下来k个数,表示各个元素;为2时,两个数x,y表示该序列有x,y序列合并而来 (1≤x,y≤i−1)。保证所有n之和不超过
1
0
6
10^6
106,保证所有k之和不超过
1
0
6
10^6
106,保证一个序列的长度不超过
1
0
18
10^{18}
1018。
输出仅一行,所求答案。
样例输入输出:
input
2
1
1 5 3 3 2 1 3
3
1 3 3 3 2
1 4 2 2 3 3
2 1 2
output
4
6
题目分析:
首先考虑对于一个给定的序列怎么求最大快乐值:假设序列个数为n,出现次数最多的数的个数为m,那么当m<=n/2时,肯定能够实现全部互换使得原位置的数不同,即ans=n;如果m>n/2时,多出来的那部分不能匹配,能够匹配的是n-m对,即ans=2*(n-m)。
再考虑第n个序列,如果直接给你那就是上面那种情形就很简单,但如果是由序列合并而来呢?由上面的结论知,我们其实只关心这个序列的个数,以及出现次数最多的数的个数,那么我们就可以实现区间合并,就是个数相加,统计每个数出现的次数。所以只要找到是哪些序列合成的,每个序列参与的次数就行了。
所以最开始我就是建图,直接从n开始DFS,找到它是由哪些初始序列构成的。但是TLE,因为这里对于一个序列,它可以多次参与合成后面的序列,那么在DFS的时候,它就会被搜对应的次数,而这个次数都会超int。
考虑用求拓扑序的方式,即入度为0时,才加入这个点(求拓扑序的讲解,不会的可以看看:拓扑排序)。而这保证了每个点只会被搜一次,次数由n的1往下推就好了。但是有些合成序列它不参与构成n的序列,它是无关的,但是它也会提供入度。比如,n由n-2,n-3构成,n-1由n-2,n-4构成。如果我们在读入那里直接计算入度,很明显搜索的时候n-2的入度会无法减到0。所以我们可以先BFS一次,统计入度,让其只能由搜到的点提供,即排除那些无关的点。注意:这里BFS的时候,要让每个点只被加入一次,否则时间复杂度就会和前面DFS是一样的了。因为我们这里只关心入度,所以每个点只搜一次是对的。
注意:这里清0操作不要直接memset,会超时。
附代码:
#include<iostream>
#include<map>
#include<queue>
#include<cstring>
#include<vector>
using namespace std;
const int N=1e6+10;
int T,n,m,po,bj,tot,first[N],du[N],nxt[2*N],to[2*N],sze;
long long num,ans,sum,maxnum,w[N];
bool check[N];
queue<int> q1,q2;
map<int,long long> map2;
vector<vector<int> > q(N);
int readint()
{
char ch;int i=0,f=1;
for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
if(ch=='-') {ch=getchar();f=-1;}
for(;ch>='0'&&ch<='9';ch=getchar()) i=(i<<3)+(i<<1)+ch-'0';
return i*f;
}
void create(int x,int y)
{
tot++;
nxt[tot]=first[x];
first[x]=tot;
to[tot]=y;
}
void bfs(int u)//统计入度
{
int v;
q1.push(u);
check[u]=true;
while(!q1.empty())
{
u=q1.front();
q1.pop();
for(int e=first[u];e;e=nxt[e])
{
v=to[e];
du[v]++;
if(check[v]==false)
{
check[v]=true;
q1.push(v);
}
}
}
}
void tuopu(int u)//拓扑图,统计序列以及其次数
{
int v;
q1.push(u);
w[u]=1;
while(!q1.empty())
{
u=q1.front();q1.pop();
if(first[u]==0)
{
q2.push(u);
continue;
}
for(int e=first[u];e;e=nxt[e])
{
v=to[e];
du[v]--;
w[v]+=w[u];//次数
if(du[v]==0) q1.push(v);
}
}
}
int main()
{
//freopen("lx.in","r",stdin);
int x,y;
T=readint();
while(T--)
{
map2.clear();q.clear();sum=0;tot=0;
n=readint();
for(int i=1;i<=n;i++)
{
bj=readint();
if(bj==1)
{
m=readint();
for(int j=1;j<=m;j++)
{
x=readint();
q[i].push_back(x);
}
}
else
{
x=readint();y=readint();
create(i,x);
create(i,y);
}
}
bfs(n);
tuopu(n);
while(!q2.empty())
{
x=q2.front();
q2.pop();
sze=q[x].size();
sum+=sze*w[x];//求总个数
for(int i=0;i<sze;i++)
map2[q[x][i]]+=w[x];//统计每个数字的个数,用map直接hash
}
map<int,long long>::iterator it;
maxnum=0;
for(it=map2.begin();it!=map2.end();it++)
{
num=it->second;
maxnum=max(maxnum,num);
}
if(sum>=2*maxnum) ans=sum;
else ans=2*(sum-maxnum);
printf("%lld\n",ans);
for(int i=1;i<=n;i++)
{
q[i].clear();
first[i]=0;
check[i]=0;
w[i]=0;
}
}
return 0;
}