题目大意:
总共有两种操作:
1.给一个长度为m的序列
2.给两个数字x,y,代表本次操作是将第x个序列和第y个序列拼接成一个序列。(x,y均为之前某次操作得到的串)
操作总数1e6,给出的序列长度1e6,由2操作得到的串的长度保证不超过1e18,求最后一个操作得到的串的最大快乐值是多少
题解:
比赛时三个人一起开了这个银牌题,不难想出一个序列的最大快乐值,all表示所有数出现的总次数,sum表示一个数出现的最大次数。当all/2<sum时,答案为2*(all-sum),否则为all。之后我们开始混乱了,最后写了一个暴力的,发现t了,又发现可以记录一下每个序列的次数,然后又t了,最后队友转换成树来做,还是T,真是自闭。
这个题有两种解法O(nlogn)和O(n)的解法,而且会卡掉常数比较大的O(nlogn)的写法,O(n)的解法肯定可以过。
先讲时间复杂度比较大的解法,不难发现这种合并操作类似于拓扑排序,因此我们就可以根据组成序列的顺序建图,即如果x和y组成了序列z,那么z向x和y建一条边,然后按照拓扑序列进行bfs,最后记录一下答案就行了。
具体的时间复杂度我也不会分析,大概是O(e+n),肯定会比O(n)大,比较容易被卡常数,注意找个好的快读板子。
trick:注意不能建好图之后就直接拓扑排序,因为那些对答案没有贡献的序列是不需要处理的,所以我们要先bfs搜出一个局部的和答案相关联的序列的图。
最后是O(n)的解法,我们可以直接从n到1枚举那些组合的序列,维护最后对答案贡献的序列的出现次数,然后利用一个题里面的O(n)求众数的解法(BZOJ2456: mode(众数))求出出现次数最大的数,维护答案即可。
解法一代码实现:
#include <bits/stdc++.h> using namespace std; #define ll long long #define pb emplace_back #define mp make_pair #define se second #define endl '\n' #define IO ios::sync_with_stdio(false);cin.tie(0) const int N = 1e6 + 5; ll cnt[N];//每个序列的使用次数 int ru[N]; bool vis[N]; vector<int>G[N], co[N]; unordered_map<int, ll>ans; queue<int>q; int t, n, m, flag[N]; inline char nc() {static char buf[1000000],*p1=buf,*p2=buf;return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;} inline void read(int &sum) {char ch=nc();sum=0;while(!(ch>='0'&&ch<='9')) ch=nc();while(ch>='0'&&ch<='9') sum=(sum<<3)+(sum<<1)+(ch-48),ch=nc();} void init(){ while(!q.empty()) q.pop(); for(int i = 1; i <= n; i++){ co[i].clear(); G[i].clear(); cnt[i] = ru[i] = vis[i] = 0; } ans.clear(); } void tuopu(){ q.push(n); while(!q.empty()){ ll now = q.front(); q.pop(); for(auto nxt : G[now]){ ru[nxt]--; cnt[nxt] += cnt[now]; if(ru[nxt] == 0) q.push(nxt); } } } void bfs(){ q.push(n); vis[n] = 1; while(!q.empty()){ ll now = q.front(); q.pop(); for(auto nxt : G[now]){ ru[nxt]++; if(!vis[nxt]) q.push(nxt), vis[nxt] = 1; } } } void solve(){ for(int i = 1; i <= n; i++){ if(cnt[i] != 0 && flag[i] == 1){//当它是给定的初始序列时才去计算它 for(auto j : co[i]) ans[j] += cnt[i]; } } ll sum = 0, maxx = 0; for(auto &i : ans){ sum += i.se; if(i.se > maxx) maxx = i.se; } if(maxx * 2 >= sum) cout << (sum - maxx) * 2 << endl; else cout << sum << endl; } int main(){ IO; read(t); while(t--){ read(n); init(); int a, b; for(int i = 1; i <= n; i++){ read(flag[i]); if(flag[i] == 1){ read(m); for(int j = 1; j <= m; j++){ read(a); co[i].pb(a); } } else{ read(a);read(b); G[i].pb(a); G[i].pb(b); } } cnt[n] = 1; bfs();//找出有用的序列 tuopu(); solve(); } }
解法二代码实现:
#pragma GCC optimize(2) #include<iostream> #include<algorithm> #include<cmath> #include<cstring> #include<cstdio> #include<cstdlib> #include<vector> #include<map> #include<set> #include<stack> #include<queue> #define PI atan(1.0)*4 #define E 2.718281828 #define rp(i,s,t) for (register int i = (s); i <= (t); i++) #define RP(i,t,s) for (register int i = (t); i >= (s); i--) #define ll long long #define ull unsigned long long #define mst(a,b) memset(a,b,sizeof(a)) #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define pii pair<int,int> #define mp make_pair #define pb push_back #define debug printf("ac\n"); using namespace std; inline int read() { int a=0,b=1; char c=getchar(); while(c<'0'||c>'9') { if(c=='-') b=-1; c=getchar(); } while(c>='0'&&c<='9') { a=(a<<3)+(a<<1)+c-'0'; c=getchar(); } return a*b; } const int INF = 0x3f3f3f3f; const int N = 1e6+7; vector<int> G[N]; ll num[N],all,MAX,sum; int op[N][3]; int T,n,m; int main(){ T=read(); while(T--){ n=read(); rp(i,1,n){ op[i][0]=read(); num[i]=0,G[i].clear(); if(op[i][0]==1){ m=read(); rp(j,1,m){ int x=read(); G[i].pb(x); } } else{ op[i][1]=read(); op[i][2]=read(); } } num[n]=1; RP(i,n,1){ if(op[i][0]==2){ num[op[i][1]]+=num[i]; num[op[i][2]]+=num[i]; } } sum=MAX=all=0; rp(i,1,n){ if(num[i]!=0&&op[i][0]==1){ for(auto val:G[i]){ if(MAX==0) MAX=val,sum+=num[i]; else if(val==MAX) sum+=num[i]; else{ sum-=num[i]; if(sum<0){ sum=-sum; MAX=val; } } all+=num[i]; } } } sum=0; rp(i,1,n){ if(op[i][0]==1&&num[i]!=0){ for(auto val:G[i]){ if(val==MAX) sum+=num[i]; } } } if(sum<=all/2) printf("%lld\n",all); else printf("%lld\n",(all-sum)*2); } return 0; } /* 2 1 1 5 3 3 2 1 3 3 1 3 3 3 2 1 4 2 2 3 3 2 1 2 */
CCPC2019哈尔滨E题——拓扑排序|数学
最新推荐文章于 2021-11-25 11:50:23 发布