Codeforces 999D Equalize the Remainders 【数据结构】【贪心】

考场的时候想到的n*m做法tle了,正解是O(n+m)

首先想到一个性质是不管a[i],a[j]相差多少,只要a[i],a[j]同余,那想让他们increase后%m得到另一个余数,那他们需要increse的次数是相等的。

所以我们想到把n个数按%m从0到m-1分成m类。这样就能贪心了,因为如果%m为0这样的数大于n/m个,那这样说明这里面的v.size()-n/m个数至少要加1,我们把这些多出来的%m为0的数转化成%m为1的数;如果小于n/m先不处理。一遍扫完我们发现m-1这一项会有很多数(因为前面的都压过来了),m-1之前size最大为n/m。

然后我们再扫一遍把多余的余m-1的数转移到之前不满n/m的就可以了。但这样复杂度是O(n*m),因为可能一开始余0里面有n个数,这样的话每个数都要不断的+1放到下一个余数里,再+1放到下一个余数里以此类推。

正解是把多出来的那些数放到一个叫fre的vector里(free是关键字所以不能用)【与此相对的是我把它放到下一个余数的vector中】,这样就巧妙地避免了我之前遇到的问题(不会重复加和删除)。这里说一下vector里方法的复杂度:push_back()和pop_back()是O(1);insert和erase是O(n)【我以前以为都是常数。。】

 

 1 #include<iostream>
 2 #include<vector>
 3 #include<map>
 4 using namespace std;
 5 
 6 vector<int> remain[200005];
 7 vector< pair<int,int> > fre; //first是index,second是余数 
 8 long long a[200005],pl[200005],ans;
 9 
10 int main(){    
11     int n,m; cin>>n>>m;
12     for(int i=1;i<=n;i++){
13         cin>>a[i];
14         remain[ a[i]%m ].push_back(i);
15     }
16      
17     for(int i=0;i<m;i++){//遍历每一个remainder
18         if( remain[i].size()>n/m ){
19             int tran = remain[i].size()-n/m;
20             while(tran--){
21                 fre.push_back( make_pair(remain[i].back(),i) );
22                 remain[i].pop_back();
23             }
24         }
25         else if( remain[i].size()<n/m ){
26             int need = n/m - remain[i].size();
27             //free里面的余数肯定比现在的小
28             while( (!fre.empty() ) && need-- ){
29                 pl[ fre.back().first ]+= i - fre.back().second;
30                 ans+=i - fre.back().second;
31                 remain[i].push_back( fre.back().first  );
32                 fre.pop_back();
33             } 
34         }
35     }
36     
37     for(int i=0;i<m;i++){
38         if( remain[i].size()<n/m){
39             int need=n/m-remain[i].size();
40             //free里面的余数肯定比现在的大 
41             while( need-- ){
42                 pl[ fre.back().first ]+= (m-fre.back().second) + i;
43                 ans+=(m-fre.back().second) + i;
44                 remain[i].push_back( fre.back().first  );
45                 fre.pop_back();
46             }     
47         }
48     }
49     
50     cout<<ans<<endl;
51     for(int i=1;i<=n;i++) cout<<a[i]+pl[i]<<" ";
52     
53     return 0;
54 }

相关文章

【啊哈!算法】算法3:最常用的排序——快速排序       ...
匿名组 这里可能用到几个不同的分组构造。通过括号内围绕的正...
选择排序:从数组的起始位置处开始,把第一个元素与数组中其...
public struct Pqitem { public int priority; ...
在编写正则表达式的时候,经常会向要向正则表达式添加数量型...
来自:http://blog.csdn.net/morewindows/article/details/6...