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 } (编辑:ASP站长网) |