*【2019牛客暑期多校训练营(第三场)- G】Removing Stones(分治)
題干:
鏈接:https://ac.nowcoder.com/acm/contest/883/G
來(lái)源:牛客網(wǎng)
?
Summer vacation is coming and Mark has returned home from his university having successfully survived the exam week. Today, he is very bored. So his friend Alice challenges him to play a game with stones which is invented by her. Alice gives Mark?N\ N?N piles of stones numbered from?1\ 1?1 to?N\ N?N, and there are aia_iai? stones in the?i\ i?i-th pile. The rules of the game are simple: Mark will try to remove all stones. In each move, Mark chooses two different non-empty piles and removes one stone from each of those two piles. Mark can perform any number of moves. If all the piles are empty after some number of moves, Mark wins the game. If he can't make a valid move but not all piles are empty, he loses the game.?Obviously, if the total number of stones is odd, then Mark is not able to win the game. So there is an additional rule: if initially, the total number of stones is odd, then Mark removes a single stone from the pile with the fewest stones before starting the game. If there are multiple piles with the smallest number of stones, Mark chooses one among them to remove a stone.
?
Mark found the optimal strategy for Alice's game very quickly and gets bored again. Also, he noticed that for some configuration of stones there is no way to win. So he challenges you to solve this problem: count the number of integer pairs (l,r)?(1≤l<r≤N)(l,r) \ (1 \le l < r \le N)(l,r)?(1≤l<r≤N) such that it is possible for Mark to win the game if the game is played using only the piles numbered from?l\ l?l to?r\ r?r.
輸入描述:
The input contains multiple cases. The first line of the input contains a single positive integer?T\ T?T, the number of cases. The first line of each case contains a single integer N?(2≤N≤300000)N \ (2 \le N \le 300000)N?(2≤N≤300000), the number of piles. The following line contains?N\ N?N space-separated integers, where the?i\ i?i-th integer denotes ai?(1≤ai≤109)a_i \ (1 \le a_i \le 10^9)ai??(1≤ai?≤109), the number of stones in the?i\ i?i-th pile. It is guaranteed that the sum of?N\ N?N over all cases does not exceed?1000000\ 1000000?1000000.輸出描述:
For each case, print a single integer on a single line, the number of pairs satisfying the required property.示例1
輸入
復(fù)制
2 3 1 1 1 4 1 2 3 4輸出
復(fù)制
3 3題目大意:
給你N堆石子,每堆石子中有a[i]個(gè)石子,問(wèn)多少個(gè)石子區(qū)間,可以滿足:
①石子總和若為偶數(shù),那么可以兩兩取 來(lái)自不同堆的石子各一個(gè),重復(fù)操作,直到取完。
②如果為奇數(shù),那么在最多石子的一堆中去掉其中一個(gè),使得石子總和變?yōu)榕紨?shù),然后進(jìn)入操作①。
解題報(bào)告:
對(duì)于一個(gè)區(qū)間來(lái)說(shuō),如果最大值大于除它之外其他數(shù)的和,就肯定不能清空了,反之,通過(guò)歸納證明,一定可以清空。(這一個(gè)證明好像是前幾天codeforce的一個(gè)div2B原題?)
因?yàn)檫@題轉(zhuǎn)化后的題意是這樣:求有多少個(gè)長(zhǎng)度不小于2的連續(xù)子序列,使得其中最大元素不大于序列和的1/2。
再轉(zhuǎn)化一下題意:求:區(qū)間最大值的兩倍 小于等于 區(qū)間和的區(qū)間個(gè)數(shù)。
子序列的統(tǒng)計(jì)的題,可以考慮分治解決。
首先找到最大值的位置pos,這樣的話[L,R]= [L,pos-1] + [pos+1,R] + 跨過(guò)pos這個(gè)位置的區(qū)間個(gè)數(shù)。不難發(fā)現(xiàn)這樣分治是正確的。(和第一場(chǎng)那個(gè)分治做法差不多啊)
但是注意下一步統(tǒng)計(jì)跨過(guò)pos這個(gè)位置的答案的時(shí)候,要枚舉那個(gè)小區(qū)間,好像可以證明均攤復(fù)雜度是nlogn的。(我也不會(huì)證)
然后比較正確的做法就是枚舉這個(gè)小區(qū)間的每一個(gè)位置,然后對(duì)前綴和二分(因?yàn)榍熬Y和是具有單調(diào)性的,而值沒(méi)有),二分右區(qū)間的位置。如果尺取的話復(fù)雜度是不一定能保證的,詳見(jiàn)下面的代碼。
AC代碼:
#include<cstdio> #include<iostream> #include<algorithm> #include<queue> #include<map> #include<vector> #include<set> #include<string> #include<cmath> #include<cstring> #define FF first #define SS second #define ll long long #define pb push_back #define pm make_pair using namespace std; typedef pair<int,int> PII; const int MAX = 3e5 + 5; int lg[MAX],f[MAX][33]; int a[MAX],n,Log[MAX]; ll sum[MAX],ans; inline void ST() {for(int i = 1; i<=n; i++) f[i][0] = i;for(int x,y,j = 1; (1<<j)<=n; j++) {for(int i = 1; i+(1<<j)-1<=n; i++) {x = f[i][j-1],y = f[i+(1<<j-1)][j-1];if(a[x] > a[y]) f[i][j] = x;else f[i][j] = y;}} } inline int gMax(int l,int r) {int k = Log[r-l+1];int x = f[l][k],y = f[r-(1<<k)+1][k];if(a[x] < a[y]) return y;else return x; } void solve(int L,int R) {if(L >= R) return ;int pos = gMax(L,R);solve(L,pos-1);solve(pos+1,R);if(pos - L + 1< R - pos + 1) {//如果左邊的數(shù)字少的話 int r = pos;for(int l = L; l<=pos; l++) {while(r <= R && sum[r] - sum[l-1] < 2*a[pos]) r++;ans += R-r+1;}// for(int l = pos; l>=L; l--) { // while(r >= pos && sum[r] - sum[l-1] >= 2*a[pos]) r--; // ans += R-r; // }}else {int l = pos;for(int r = R; r>=pos; r--) {while(l>=L && sum[r] - sum[l-1] < 2*a[pos]) l--;ans += l-L+1;}// for(int r = pos; r<=R; r++) { // while(l <= pos && sum[r] - sum[l-1] >= 2*a[pos]) l++; // ans += l-L; // }} } int main() {for(int i = 2; i<MAX; i++) Log[i] = Log[i>>1]+1;int t;cin>>t;while(t--) {scanf("%d",&n);for(int i = 1; i<=n; i++) scanf("%d",a+i),sum[i] = sum[i-1] + a[i];ST();ans = 0;solve(1,n);printf("%lld\n",ans);}return 0 ; }TLE的代碼:
分析一下原因,其實(shí)思路都是一樣的,只是實(shí)現(xiàn)的時(shí)候他相當(dāng)于是 先移動(dòng)左指針,然后看右指針最多移動(dòng)到那里。我是按照一般的尺取,先移動(dòng)右指針。這樣就有個(gè)問(wèn)題當(dāng)他是遞減區(qū)間的時(shí)候,我這樣相當(dāng)于還是n^2的復(fù)雜度,但是他那樣的話雖然看起來(lái)也是n^2的復(fù)雜度,但是至少對(duì)于單減的序列或者單增序列復(fù)雜度都沒(méi)問(wèn)題。(不過(guò)保險(xiǎn)起見(jiàn)這題還是寫二分比較好吧..最差復(fù)雜度保證了O(nlog^2 n))
#include<cstdio> #include<iostream> #include<algorithm> #include<queue> #include<map> #include<vector> #include<set> #include<string> #include<cmath> #include<cstring> #define FF first #define SS second #define ll long long #define pb push_back #define pm make_pair using namespace std; typedef pair<int,int> PII; const int MAX = 3e5 + 5; int lg[MAX],f[MAX][33]; int a[MAX],n,Log[MAX]; ll sum[MAX],ans; inline void ST() {for(int i = 1; i<=n; i++) f[i][0] = i;for(int x,y,j = 1; (1<<j)<=n; j++) {for(int i = 1; i+(1<<j)-1<=n; i++) {x = f[i][j-1],y = f[i+(1<<j-1)][j-1];if(a[x] > a[y]) f[i][j] = x;else f[i][j] = y;}} } inline int gMax(int l,int r) {int k = Log[r-l+1];int x = f[l][k],y = f[r-(1<<k)+1][k];if(a[x] < a[y]) return y;else return x; } void solve(int L,int R) {if(L >= R) return ;int pos = gMax(L,R);solve(L,pos-1);solve(pos+1,R);if(pos - L + 1< R - pos + 1) {//如果左邊的數(shù)字少的話 int r = R;for(int l = pos; l>=L; l--) {while(r >= pos && sum[r] - sum[l-1] >= 2*a[pos]) r--;ans += R-r;}}else {int l = L;for(int r = pos; r<=R; r++) {while(l <= pos && sum[r] - sum[l-1] >= 2*a[pos]) l++;ans += l-L;}} } int main() {for(int i = 2; i<MAX; i++) Log[i] = Log[i>>1]+1;int t;cin>>t;while(t--) {scanf("%d",&n);for(int i = 1; i<=n; i++) scanf("%d",a+i),sum[i] = sum[i-1] + a[i];ST();ans = 0;solve(1,n);printf("%lld\n",ans);}return 0 ; }咖啡雞的On代碼:(但是并不能看懂 先放著吧)
對(duì)于每個(gè)數(shù)都看把這個(gè)數(shù)當(dāng)成最大值得左邊界和右邊界的和,然后求是否這個(gè)區(qū)間成立。
但是這里不好統(tǒng)計(jì)有多少成立的區(qū)間,于是便反向取,即求有多少不滿足的區(qū)間。
?
總結(jié)
以上是生活随笔為你收集整理的*【2019牛客暑期多校训练营(第三场)- G】Removing Stones(分治)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: propelac.exe - prope
- 下一篇: pruttct.exe - pruttc