CodeForces - 466C Number of Ways(推公式/dp)
阿新 • • 發佈:2020-12-10
題目連結:點選檢視
題目大意:給出一個長度為 n 的數列,現在要求出滿足條件的 ( i , j ) 的匹配數量,滿足:
題目分析:訓練時推的公式,簡單說一下吧,維護字首和 sum,則確定兩個斷點 ( i , j ) 後可以確定下來三個區間:
- sum[ i - 1 ]
- sum[ j] - sum[ i - 1 ]
- sum[ n ] - sum[ j ]
因為需要三段區間的權值和連等,根據等式的傳遞性,我們可以讓前兩項相等,再讓後兩項相等,最後不難推出三項連等,建立等式:
- 前兩項相等:sum[ i - 1 ] = sum[ j ] - sum[ i - 1 ],推得 2 * sum[ j ] = sum[ i - 1 ] * 4(兩側同時乘以 2,下面要用)
- 後兩項相等:sum[ j ] - sum[ i - 1 ] = sum[ n ] - sum[ j ],推得 2 * sum[ j ] = sum[ n ] - sum[ i - 1 ]
利用 2 * sum[ j ] 當做中間量,不難看出當確定下來 i 後,只有滿足sum[ n ] - sum[ i - 1 ] =sum[ i - 1 ] * 4 時才存在 j 與其匹配,此時只需要查詢一下區間 [ i , n ] 中有多少個 j 與其匹配即可
另一種方法,是將其轉換成一個很經典的 dp 模型,這裡拿一下 PTA 乙級題目的題面:
字串
APPAPT
中包含了兩個單詞PAT
,其中第一個PAT
是第 2 位(P
),第 4 位(A
),第 6 位(T
);第二個PAT
是第 3 位(P
),第 4 位(A
),第 6 位(T
)。現給定字串,問一共可以形成多少個
PAT
?
比較顯然的是,假設 sum 為 n 個數之和,( i , j ) 將整個序列分成的三段大小分別都是 sum / 3(設 sum % 3 == 0),所以第一個斷點的位置也就是字首和等於 sum / 3 的位置,同理第二個斷點的位置也就是字首和等於 sum / 3 * 2 的位置,如此一來就轉換成了上面那個非常經典的動態規劃模型:
給定 n 個位置,問一共可以形成多少個 ( sum/3 的位置,sum/3*2 的位置)
如此一來這個題目就可以繼續擴充套件了,諸如匹配 ( i , j , k ) 三元對的斷點將數列四等分劃分這樣的
妙啊
程式碼:
推公式
//#pragma GCC optimize(2)
//#pragma GCC optimize("Ofast","inline","-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
#include<bitset>
using namespace std;
typedef long long LL;
typedef unsigned long long ull;
const int inf=0x3f3f3f3f;
const int N=1e6+100;
LL sum[N];
map<LL,int>mp;
int main()
{
#ifndef ONLINE_JUDGE
// freopen("data.ans.txt","r",stdin);
// freopen("data.out.txt","w",stdout);
#endif
// ios::sync_with_stdio(false);
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
LL num;
scanf("%lld",&num);
sum[i]=sum[i-1]+num;
}
LL ans=0;
for(int i=n-1;i>1;i--)//列舉i
{
mp[sum[i]*2]++;
if(4*sum[i-1]==sum[n]+sum[i-1])
ans+=mp[sum[n]+sum[i-1]];
}
printf("%lld\n",ans);
return 0;
}
dp
//#pragma GCC optimize(2)
//#pragma GCC optimize("Ofast","inline","-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
#include<bitset>
using namespace std;
typedef long long LL;
typedef unsigned long long ull;
const int inf=0x3f3f3f3f;
const int N=1e6+100;
LL sum[N],dp[N][2];
int main()
{
#ifndef ONLINE_JUDGE
// freopen("data.ans.txt","r",stdin);
// freopen("data.out.txt","w",stdout);
#endif
// ios::sync_with_stdio(false);
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
LL num;
scanf("%lld",&num);
sum[i]=sum[i-1]+num;
}
if(sum[n]%3!=0)
return 0*puts("0");
LL mark=sum[n]/3;
for(int i=1;i<=n;i++)
{
for(int j=0;j<2;j++)
dp[i][j]=dp[i-1][j];
if(i!=n)
{
if(sum[i]==mark)
dp[i][0]++;
if(sum[i]==mark*2)
dp[i][1]+=dp[i-1][0];
}
}
printf("%lld\n",dp[n][1]);
return 0;
}