题意:
有三个序列,a、b、c,每次操作可以把一个序列中的一个数移动到另一个序列中,
问,最少操作几次后,可以使得 a 序列里的所有数 小于 b 里面的所有数,b 里面的小于 c 里面的。
数字不重复,总共2e5的数据量。
思路:
做法一:(LIS)
这个做法是网上看到的,确实挺巧妙的,用这个方法,即使以后来个100个序列的,也不用怕了。
分别对a b c 排序,然后合并,
求最大上升子序列,然后上升子序列里的数不动,只移动非序列里的,答案就是 n - len,len最大,答案就最小。
求LIS的时候,要用树状数组优化一下,这样复杂度才能是 O(n*logn )。
做法二:(乱搞)
这个是自己瞎想想出来的,复杂度O(n),不过只适用于三个序列的。
三个序列里的,看做三种颜色记作颜色c1、c2、c3,然后混在一起排序,
然后每次固定一个左端点 l,表示移动到最后 1~l 为序列a里的数,查找一个可以使操作最小的右端点 r ,表示到最后 l+1~r 为序列b里的数,那么剩下的 r+1~n 就是序列c里的数。
怎么找操作最小的右端点r呢,先假设l不存在,也就是我们现在只把序列分为两段,
当右端点为 i 时,显然,操作数就是 1~i 的c3的数量 加上 i+1~n的 c2的数量,(这个就是需要对序列b c操作的次数)(对a操作的次数很好求,就是不在1~l 里的c1的数量)
为什么可以假设 l 不存在,因为当 l 移动的时候,如果吞掉一个c3,那么,r 在 l + 1 ~ n 每个点时的操作数就是全部减一,所以最小值的位置不变,只是值减一而已。
只分两段的时候,我们就很容易求 r 在 j ~ n时的最小值,这个就从后往前更新一下就好了。
所以,简而言之,就是预处理 j~n 的 r 的最小的位置在哪里,然后枚举 l ,更新答案。
(每次思维题我思路都讲得乱七八糟的。。。)
代码:
做法一:(LIS)
#include <stdio.h> #include <string.h> #include <algorithm> #include <vector> #include <queue> #include <functional> using namespace std; const int maxn = 2e5 + 10; const int inf = 0x3f3f3f3f; int dp[maxn],mx[maxn]; int lowbit(int i){ return i & (-i); } void insert(int i,int x,int n){ while(i <= n){ mx[i] = max(mx[i],x); i += lowbit(i); } } int _find(int i){ int res = 0; while(i > 0){ res = max(mx[i],res); i -= lowbit(i); } return res; } int LIS(int a[],int n){ int res = 0; memset(mx,0,sizeof(mx)); memset(dp,0,sizeof(dp)); for(int i = 1;i <= n;i++){ dp[a[i]] = _find(a[i]) + 1; res = max(res,dp[a[i]]); insert(a[i],dp[a[i]],n); } return res; } int a[maxn],b[maxn]; int main(){ int _n[3],n; while(scanf("%d%d%d",&_n[0],&_n[1],&_n[2]) != EOF){ n = 0; int cnt = 0; for(int k = 0;k < 3;k++){ for(int i = 1;i <= _n[k];i++) scanf("%d",&b[i]); sort(b + 1,b + _n[k] + 1); for(int i = 1;i <= _n[k];i++) a[++cnt] = b[i]; n += _n[k]; } int ans = LIS(a,n); printf("%d ",n - ans); } return 0; }
做法二:(乱搞)
#include <stdio.h> #include <string.h> #include <iostream> #include <cmath> #include <vector> #include <queue> #include <algorithm> using namespace std; typedef long long ll; const int maxn = 2e5 + 10; int sum2[maxn],sum3[maxn],mi_p[maxn],mi[maxn]; int vis[maxn]; int main(){ int n1,n2,n3,x,n; while(scanf("%d%d%d",&n1,&n2,&n3) != EOF){ memset(vis,0,sizeof(vis)); n = n1 + n2 + n3; for(int i = 1;i <= n1;i++){ scanf("%d",&x); vis[x] = 1; } for(int i = 1;i <= n2;i++){ scanf("%d",&x); vis[x] = 2; } for(int i = 1;i <= n3;i++){ scanf("%d",&x); vis[x] = 3; } sum3[0] = 0; for(int i = 1;i <= n;i++){ sum3[i] = sum3[i - 1]; if(vis[i] == 3) sum3[i]++; } sum2[n + 1] = 0; for(int i = n;i >= 1;i--){ sum2[i] = sum2[i + 1]; if(vis[i] == 2) sum2[i]++; } int mival = sum3[n],ans = 1e9; mi[n] = sum3[n]; for(int i = n - 1;i >= 1;i--){ mi[i] = mi[i + 1]; if(mival > sum3[i] + sum2[i + 1]){ mival = sum3[i] + sum2[i + 1]; mi[i] = mival; } } mi[0] = min(sum2[1],mi[1]); int one = 0,sub = 0,res; ans = n2 + n3; for(int i = 0;i < n;i++){ if(vis[i] == 1) one++; if(vis[i] == 3) sub++; res = (i - one) + mi[i] - sub + (n1 - one); if(res < ans) ans = res; } printf("%d ",ans); } return 0; }