題目描述
小H是個善於思考的學生,她正在思考一個有關序列的問題。她的面前浮現出了一個長度爲n的序列{ai},她想找出兩個非空的集合S、T。
這兩個集合要滿足以下的條件:
1. 兩個集合中的元素都爲整數,且都在 [1, n] 裏,即Si,Ti ∈ [1, n]。
2. 對於集合S中任意一個元素x,集合T中任意一個元素y,滿足x < y。
3. 對於大小分別爲p, q的集合S與T,滿足
a[s1] xor a[s2] xor a[s3] ... xor a[sp] = a[t1] and a[t2] and a[t3] ... and a[tq].
小H想知道一共有多少對這樣的集合(S,T),你能幫助她嗎?
輸入格式
第一行,一個整數n第二行,n個整數,代表ai。
輸出格式
僅一行,表示最後的答案。樣例輸入
41 2 3 3
樣例輸出
4樣例解釋
S = {1,2}, T = {3}, 1 ^ 2 = 3 = 3 (^爲異或)S = {1,2}, T = {4}, 1 ^ 2 = 3 = 3
S = {1,2}, T = {3,4} 1 ^ 2 = 3 & 3 = 3 (&爲與運算)
S = {3}, T = {4} 3 = 3 = 3
數據範圍
30%: 1 <= n <= 1060%: 1 <= n <= 100
100%: 1 <= n <= 1000, 0 <= ai < 1024
題解
我覺的這是一道很考綜合能力的題。不過事實證明,我很弱……
首先我們可以想出一種兩邊分開的dp:f[i][j]和g[i][j]表示前i個xor值爲j,後i個and值爲j的方案數, 隨後枚舉分界點k來求總方案數。複雜度O(n * 1024 * 3)。最終分數:60分。
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<cmath>
#include<algorithm>
#define ll long long
using namespace std;
int n,a[1002];
ll f[1002][3000],g[1002][3000],add[1002][3000];
ll ans;
void init()
{
scanf("%d",&n);
int i;
for(i=1;i<=n;i++) scanf("%d",&a[i]);
}
void dp()
{
int i,j;
f[1][a[1]]=1; add[1][a[1]]=1;
for(i=2;i<=n;i++)
{f[i][a[i]]++; add[i][a[i]]++;
for(j=0;j<3000;j++)
{if(f[i-1][j]>0)
{f[i][j^a[i]]+=f[i-1][j]; add[i][j^a[i]]+=f[i-1][j];
f[i][j]+=f[i-1][j];
}
}
}
g[n][a[n]]=1;
for(i=n-1;i>0;i--)
{g[i][a[i]]++;
for(j=0;j<3000;j++)
{if(g[i+1][j]>0)
{g[i][j]+=g[i+1][j];
g[i][j&a[i]]+=g[i+1][j];
}
}
}
for(i=1;i<n;i++)
for(j=0;j<3000;j++)
ans+=add[i][j]*g[i+1][j];
printf("%I64d\n",ans);
}
int main()
{
freopen("sequence.in","r",stdin);
freopen("sequence.out","w",stdout);
init(); dp();
return 0;
}
那天在考試的時候,我覺得這樣就能A了。然而,這一題沒有“取mod”操作,剩下的40分需要高精度,這一點我沒有想到,所以這道題的一個收穫就是:沒有取mod的題要考慮它結果的範圍。
那麼既然需要高精度,我們肯定不能再在上面那種做法上“數組+一維”,這樣空間不允許。所以,我們需要將空間也優化,並且像上面那種dp,又乘又加的不得寫死……這裏提出一種新的dp方程:因爲“兩個數相等就相當於兩個數的xor爲0”。設 f[i][j][k=0..2]代表從後往前處理到第 I 個數,如果 k = 1代表and值爲j,如果k = 2代表xor值爲 j,如果k = 0則代表之前一個元素都沒取。所以很容易得到方程:
f[i][j][0] = f[i + 1][j][0]——————————其實只有j=1023且k=0時f[i][j][0]纔有值且恆爲1.
f[i][j & ai][1] = f[i + 1][j][1] + f[i + 1][j][0] + f[i + 1][j & ai][1]
f[i][j ^ ai][2] = f[i + 1][j][1] + f[i + 1][j][2] + f[i + 1][j ^ ai][2];
最後f[1][0][2]就是答案, 複雜度爲O(n * 1024 * 3)這樣統計答案就只需要高精加了。不過爲什麼說我弱呢?是因爲我自己寫的高精加只有70分。原因大概是我重載+號的算法裏有許多賦值的操作。而且其他的一些細節常數很大。這裏蠻貼一下。
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<cmath>
#include<algorithm>
#define M 1024
#define N 1000000000
using namespace std;
int n,a[1002];
struct shu{int s[50],l;} f[2][1030][3];
void init()
{
scanf("%d",&n);
int i;
for(i=1;i<=n;i++) scanf("%d",&a[i]);
}
shu operator + (const shu &x,const shu &y)
{
shu ans;
memset(ans.s,0,sizeof(ans.s));
int i;
ans.l=max(x.l,y.l);
for(i=1;i<=ans.l;i++)
{ans.s[i]+=x.s[i]+y.s[i];
if(ans.s[i]>=N)
{ans.s[i+1]++;
ans.s[i]%=N;
}
}
if(ans.s[ans.l+1]>0) ans.l++;
return ans;
}
void PRINT(const shu &x)
{
int i;
printf("%d",x.s[x.l]);
for(i=x.l-1;i>0;i--) printf("%09d",x.s[i]);
printf("\n");
}
void dp()
{
int i,j,k,now=0,next,vx,vy;
f[0][1023][0].s[1]=1; f[0][1023][0].l=1;
for(i=n;i>0;i--)
{next=now^1;
for(j=0;j<M;j++)
for(k=0;k<3;k++)
f[next][j][k]=f[now][j][k];
for(j=0;j<M;j++)
{vy=j&a[i]; vx=j^a[i];
f[next][vy][1]=f[next][vy][1]+f[now][j][0];
f[next][vy][1]=f[next][vy][1]+f[now][j][1];
f[next][vx][2]=f[next][vx][2]+f[now][j][1];
f[next][vx][2]=f[next][vx][2]+f[now][j][2];
}
now=now^1;
}
PRINT(f[now][0][2]);
}
int main()
{
freopen("sequence.in","r",stdin);
freopen("sequence.out","w",stdout);
init(); dp();
return 0;
}
所以比較好的方法是,重載+=,並且將其寫入高精度專門的結構體內。當然常數問題自己解決,這樣就能過了。
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<cmath>
#include<algorithm>
#define M 1024
#define N 100000000
using namespace std;
int n,a[1002];
struct shu
{
int s[50],l;
void operator += (const shu &x)
{
int i;
l=max(x.l,l);
for(i=1;i<=l;i++)
{s[i]+=x.s[i];
if(s[i]>=N)
{s[i+1]++; s[i]%=N;}
}
if(s[l+1]>0) l++;
}
}
f[2][1030][3];
void init()
{
scanf("%d",&n);
int i;
for(i=1;i<=n;i++) scanf("%d",&a[i]);
}
void PRINT(const shu &x)
{
int i;
printf("%d",x.s[x.l]);
for(i=x.l-1;i>0;i--)
printf("%08d",x.s[i]);//這種寫法很好,大概是壓x位寫“%0xd”
printf("\n");
}
void dp()
{
int i,j,k,now=0,next,vx,vy;
f[0][1023][0].s[1]=1; f[0][1023][0].l=1;
for(i=n;i>0;i--)
{next=now^1;
for(j=0;j<M;j++)
for(k=0;k<3;k++)
f[next][j][k]=f[now][j][k];
for(j=0;j<M;j++)
{vy=j&a[i]; vx=j^a[i];
f[next][vy][1]+=f[now][j][0];
f[next][vy][1]+=f[now][j][1];
f[next][vx][2]+=f[now][j][1];
f[next][vx][2]+=f[now][j][2];
}
now=now^1;
}
PRINT(f[now][0][2]);
}
int main()
{
freopen("sequence.in","r",stdin);
freopen("sequence.out","w",stdout);
init(); dp();
return 0;
}