模擬賽 circle 題解

題意:有N個數,問有多少個x,\((x\leq T)\),滿足這N個數分別+x後,異或和爲S。每個數小於\(2^M\)
數位DP。
由於是加法,需要記錄進位,因此從低位到高位DP。
只要記錄下有幾個進位,就可以根據這N的數的大小知道究竟是哪幾個進位了。
\(dp(i,j,0/1)\)表示考慮到第i位,有j個進位,與T的大小關係爲0/1的方案數。
可以提前預處理出轉移,即\(g(i,j,0/1)\)表示考慮到第i位,有j個進位,當前位使用\(0/1\)造成的進位數。
時間複雜度\(O(NMlogN)\)(有排序),可以優化至\(O(NM)\)
代碼(未經優化):

#include <stdio.h>
#include <stdlib.h>
#define ll long long
struct SPx
{
	ll z;int i;
};
SPx px[52][100010];
ll sz[100010],dp[52][100010][2];
int sl[52][100010][2],tm[100010],ss[52];
int sgn(ll x)
{
	if(x>0)
		return 1;
	else if(x<0)
		return -1;
	return 0;
}
int cmp(const void*a,const void*b)
{
	return sgn(((SPx*)b)->z-((SPx*)a)->z);
}
ll solve(int m,int n,ll S,ll T)
{
	if(T==0)return 0;
	T-=1;
	for(int i=m;i>=0;i--)
	{
		int b=bool(S&(1ll<<i)),bt=bool(T&(1ll<<i));
		for(int j=0;j<=n;j++)
		{
			if(i==m)
			{
				dp[i][j][0]=1;
				continue;
			}
			if((ss[i]+j)%2!=b)
				dp[i][j][0]=dp[i][j][1]=0;
			else
			{
				if(bt==0)
				{
					dp[i][j][0]=dp[i+1][sl[i][j][0]][0]+dp[i+1][sl[i][j][1]][1];
					dp[i][j][1]=dp[i+1][sl[i][j][0]][1]+dp[i+1][sl[i][j][1]][1];
				}
				else
				{
					dp[i][j][0]=dp[i+1][sl[i][j][0]][0]+dp[i+1][sl[i][j][1]][0];
					dp[i][j][1]=dp[i+1][sl[i][j][0]][0]+dp[i+1][sl[i][j][1]][1];
				}
			}
		}
	}
	return dp[0][0][0];
}
ll work(int m,int n,ll S,ll T)
{
	ll d=(1ll<<m);
	return (T/d)*solve(m,n,S,d)+solve(m,n,S,T%d);
}
int main()
{
	freopen("circle.in","r",stdin);
	freopen("circle.out","w",stdout);
	int n,m;ll S,T;
	scanf("%d%d%lld%lld",&n,&m,&S,&T);
	for(int i=0;i<n;i++)
	{
		ll a;
		scanf("%lld",&a);
		sz[i]=a=(a+1)%(1ll<<m);
		for(int j=0;j<m;j++)
		{
			if(a&(1ll<<j))
				ss[j]=(ss[j]+1)%2;
			px[j][i].i=i;
			px[j][i].z=a%(1ll<<(j+1));
		}
	}
	for(int i=0;i<m;i++)
		qsort(px[i],n,sizeof(SPx),cmp);
	for(int i=1;i<m;i++)
	{
		int s0=0;
		for(int j=n-1;j>=0;j--)
			tm[j]=tm[j+1]+bool(sz[px[i-1][j].i]&(1ll<<i));
		for(int j=0;j<=n;j++)
		{
			if(j>0)
				s0+=bool(sz[px[i-1][j-1].i]&(1ll<<i));
			sl[i][j][0]=s0;sl[i][j][1]=tm[j]+j;
		}
	}
	sl[0][0][0]=0;
	for(int i=0;i<n;i++)
		sl[0][0][1]+=(sz[i]%2);
	printf("%lld",work(m,n,S,T));
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章