Identical Day 題解(思維)
阿新 • • 發佈:2021-06-13
題目連線
題目大意
給你一個長度為\(n(n\leq1e5)\)的\(01\)串
求最少使得多少個\(1\)變為\(0\)後這個串的價值小於\(k(k\le1e10)\)
串的價值為所有連續為\(1\)的串的價值
一段長度為\(len\)連續為\(1\)的價值為\(len*(len+1)/2\)
例如\(0111101\)的價值為\(\frac{4\times5}{2}+\frac{1\times2}{2}=11\)
題目思路
這個題目我一開始想的過於簡單了,首先我認為很容易想到優先佇列貪心
我用的是每次拿出最長的那一段然後直接中間分隔,用優先佇列去維護
但是可以想一下如果一段要分為三段,那麼最終我分的比例等於\(1:1:2\)
但是顯然是要\(1:1:1\) 即三等分,但是我那樣第一步是分為兩份肯定不行
然後我就不不會了。。。
其實正解和這個差不了太多,就是差值最大
設\(cal(a,b)\)為一段長度為\(a\)的連續的\(1\),中間人為變了\(b\)個的最小价值
將\((a,b)\)這個放入優先佇列,那麼優先佇列只要比較\(cal(a,b)-cal(a,b+1)\)即可
\(cal(a,b)\)這個函式計算也很簡單\(b\)個\(0\),那麼分為\(b+1\)段
肯定是要等分每一份為\(x=(a-b)/(b+1)\),而多餘了\(y=(a-b)\%(b+1)\)個
那麼肯定是給\(y\)份每一個多\(1\)個
- \((b+1-y)\)
- \(y\)份長度為\(x+1\)
以前寫過類似的題目,感覺這種題目就是要往差值方面去考慮,不過這個稍微難一點點
程式碼
#include<bits/stdc++.h> #define debug printf("\n I am here\n"); #define fi first #define se second #define pii pair<int,int> typedef long long ll; const int maxn=1e5+5,inf=0x3f3f3f3f,mod=1e9+7; const ll INF=0x3f3f3f3f3f3f3f3f; using namespace std; ll n,k; char s[maxn]; ll cal(ll a,ll b){ // 長度為a個1 中間有b個0 ll x=(a-b)/(b+1); ll y=(a-b)%(b+1); ll ans=x*(x+1)/2*(b+1-y)+(x+1)*(x+2)/2*y; return ans; } ll dif(ll a,ll b){ return cal(a,b)-cal(a,b+1); } struct node{ ll x,y; friend bool operator<(node a,node b){ return dif(a.x,a.y)<dif(b.x,b.y); } }; priority_queue<node> pq; int main(){ scanf("%lld%lld",&n,&k); scanf("%s",s+1); ll len=0,sum=0; for(int i=1;i<=n;i++){ if(s[i]=='1'){ len++; } if(s[i]=='0'||i==n){ if(len!=0){ sum+=len*(len+1)/2; pq.push({len,0}); } len=0; } } ll ans=0; while(sum>k){ ans++; node temp=pq.top(); pq.pop(); sum-=dif(temp.x,temp.y); if(temp.x==temp.y+1) continue; pq.push({temp.x,temp.y+1}); } printf("%lld\n",ans); return 0; }