标签:return nta 初始化 ane contest long msu BMI 双指针
任意门:http://acm.hdu.edu.cn/showproblem.php?pid=6301
Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 5312 Accepted Submission(s): 1823
需要求一个长度为 N 的序列,要求满足给出的 M 的子区间内不能出现重复的数,要求字典序最小。
一开始直接双指针模拟,但是果断WA,原因就是当前子区间不能的数不一定是连续的,需要记录前面不能使用的数。
后来用了一个标记数组记录所有不能用的数,TL,想想也知道,查询和维护需要时间复杂度太高。
那有什么可以快速维护这些当前可以使用的数,并且把已经使用过的数删除掉的呢?
C++ STL 里 的 set (可以说功能十分强大了,自动升序排序,可查询,删除)
那么如果知道了当前区间不能时候用什么数,能使用什么数,剩下的就是贪心策略了,尽可能地把需要满足条件子区间扩大(大的子区间满足了它所包含的小的子区间肯定也是满足的)。
用一个数组记录当前序列位置 x 所在的区间的最左值即可。
根据这个最左值 pre[x] 就可以判断当前以求的答案序列里 < pre[x] 的数都是可以用来填充当前子区间的。
AC code:
1 #include<cstdio> 2 #include<algorithm> 3 #include<iostream> 4 #include<cstring> 5 #include<vector> 6 #include<queue> 7 #include<cmath> 8 #include<set> 9 #define INF 0x3f3f3f3f 10 #define LL long long 11 using namespace std; 12 const int MAXN = 1e5+10; 13 int num[MAXN]; 14 int pre[MAXN]; 15 int ans[MAXN]; 16 set<int>ss; 17 int N, M; 18 int main() 19 { 20 int l, r; 21 int T_case; 22 scanf("%d", &T_case); 23 while(T_case--){ 24 ss.clear(); 25 scanf("%d %d", &N, &M); //初始化 26 for(int i = 1; i <= N; i++){pre[i] = i; ss.insert(i);} 27 for(int i = 1; i <= M; i++){scanf("%d %d", &l, &r); pre[r] = min(pre[r], l);} 28 for(int i = N-1; i >= 1; i--) pre[i] = min(pre[i], pre[i+1]); 29 int p = 1; 30 for(int i = 1; i <= N; i++){ 31 while(p < pre[i]){ss.insert(ans[p]);p++;} 32 ans[i] = *ss.begin(); 33 ss.erase(ans[i]); 34 } 35 36 for(int i = 1; i <= N; i++){printf("%d", ans[i]);if(i < N) printf(" ");} 37 puts(""); 38 } 39 return 0; 40 }
2018 Multi-University Training Contest 1 Distinct Values 【贪心 + set】
标签:return nta 初始化 ane contest long msu BMI 双指针
原文地址:https://www.cnblogs.com/ymzjj/p/10326676.html