1. 程式人生 > >Codeforces 999D Equalize the Remainders 【數據結構】【貪心】

Codeforces 999D Equalize the Remainders 【數據結構】【貪心】

會有 iostream 轉移 都是 CI The 問題 多少 數據

考場的時候想到的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 }

Codeforces 999D Equalize the Remainders 【數據結構】【貪心】