数位 dp trick

upt:本质上是有加法,所以会产生进位,所以你要从低位到高位做,然后因为每个位的进位数是 \(O(n)\) 的,所以保证你记忆化复杂度正确。

CF1290F Making Shapes

考虑凸包,向量集合确定了就能确定唯一凸包。

考虑向量加法转化到最后就是 \(\sum_{x_i>0}c_ix_i=-\sum_{x_i<0}c_ix_i,\sum_{y_i>0}c_iy_i=-\sum_{y_i<0}c_iy_i,\sum_{x_i>0}c_ix_i\le m,\sum_{y_i>0}c_iy_i \le m\)

这里 trick 就是用数位 dp 的方式去解决 c 的方案。

先考虑在 10 进制下怎么做?

因为有进位,意味着我们要从低位到高位确定 \(c_i\),假如当前到了第 \(pos\) 位,那么从 \([0,9]\) 枚举,然后使得其符合条件(每一位都要满足当前位的 \(\sum_{x_i>0}c_ix_i=-\sum_{x_i<0}c_ix_i,\sum_{y_i>0}c_iy_i=-\sum_{y_i<0}c_iy_i\) ,做类似十进制下的加法运算(维护每一个 \(sum\) 的进位)即可。

更优地,即在二进制下做加法。

#include <bits/stdc++.h>
#define int long long
#define pb push_back
using namespace std;
const int mod=998244353;
int n,m,a[6],b[6],xgf[100002][6],tot,NW[6];
int f[10][23][23][23][23][2][2];

int get(int x,int y) {
	while(y) x/=10,--y; return x%10;
}

bool check(int x,int y,bool fl) {
	if(x!=y) return x<y?0:1;
	return fl;
}

int dfs(int pos,int sum1,int sum2,int sum3,int sum4,bool lim1,bool lim2) { //sum1 sum2 x 正 x 负 
//	if(sum1) cout<<pos<<" "<<sum1<<" "<<sum2<<" "<<sum3<<" "<<sum4<<endl;
	if(pos==10) return (!sum1&&!sum2&&!sum3&&!sum4&&!lim1&&!lim2);
	if(~f[pos][sum1][sum2][sum3][sum4][lim1][lim2]) return f[pos][sum1][sum2][sum3][sum4][lim1][lim2];
	int qwq=get(m,pos); 
//	cout<<qwq<<endl;
	f[pos][sum1][sum2][sum3][sum4][lim1][lim2]=0;
	for(int i=1;i<=tot;i++) {
		int res1=sum1,res2=sum2,res3=sum3,res4=sum4;
		for(int j=1;j<=n;j++) {
			if(a[j]>0) res1+=xgf[i][j]*a[j];
			if(a[j]<0) res2-=xgf[i][j]*a[j];
			if(b[j]>0) res3+=xgf[i][j]*b[j];
			if(b[j]<0) res4-=xgf[i][j]*b[j];
		}
//		cout<<res1<<" "<<res2<<" : "<<res3<<" "<<res4<<endl;
		if((res1%10)==(res2%10)&&(res3%10)==(res4%10)) {
			f[pos][sum1][sum2][sum3][sum4][lim1][lim2]=(f[pos][sum1][sum2][sum3][sum4][lim1][lim2]+dfs(pos+1,res1/10,res2/10,res3/10,res4/10,check(res1%10,qwq,lim1),check(res3%10,qwq,lim2)))%mod;
		}
	}
	return f[pos][sum1][sum2][sum3][sum4][lim1][lim2];
}

void init(int x) {
	if(x==n+1) {
		++tot; for(int i=1;i<=n;i++) xgf[tot][i]=NW[i];
//		for(int i=1;i<=n;i++) cout<<NW[i]<<' ';
//		cout<<'\n';
		return ;
	}
	for(int i=0;i<=9;i++) NW[x]=i,init(x+1);
}

signed main() {
//	cin.tie(0); ios::sync_with_stdio(false);
	
//	cout<<tot<<endl;
	cin>>n>>m; init(1); memset(f,-1,sizeof(f));
	for(int i=1;i<=n;i++) cin>>a[i]>>b[i];
	cout<<(dfs(0,0,0,0,0,0,0)+mod-1)%mod;
}
#include <bits/stdc++.h>
#define int long long
#define pb push_back
using namespace std;
const int mod=998244353;
int n,m,a[6],b[6];
int f[31][21][21][21][21][2][2];

bool check(int x,int y,bool fl) {
	if(x!=y) return x<y?0:1;
	return fl;
}

int dfs(int pos,int sum1,int sum2,int sum3,int sum4,bool lim1,bool lim2) { //sum1 sum2 x 正 x 负 
//	if(sum1) cout<<pos<<" "<<sum1<<" "<<sum2<<" "<<sum3<<" "<<sum4<<endl;
	if(pos==30) return (!sum1&&!sum2&&!sum3&&!sum4&&!lim1&&!lim2);
	if(~f[pos][sum1][sum2][sum3][sum4][lim1][lim2]) return f[pos][sum1][sum2][sum3][sum4][lim1][lim2];
	int qwq=(m>>pos)&1; 
//	cout<<qwq<<endl;
	f[pos][sum1][sum2][sum3][sum4][lim1][lim2]=0;
	for(int S=0;S<(1<<n);S++) {
		int res1=sum1,res2=sum2,res3=sum3,res4=sum4;
		for(int j=1;j<=n;j++) {
			int c=(S>>(j-1))&1;
			if(a[j]>0) res1+=c*a[j];
			if(a[j]<0) res2-=c*a[j];
			if(b[j]>0) res3+=c*b[j];
			if(b[j]<0) res4-=c*b[j];
		}
//		cout<<res1<<" "<<res2<<" : "<<res3<<" "<<res4<<endl;
		if((res1&1)==(res2&1)&&(res3&1)==(res4&1)) {
			f[pos][sum1][sum2][sum3][sum4][lim1][lim2]=(f[pos][sum1][sum2][sum3][sum4][lim1][lim2]+dfs(pos+1,res1>>1,res2>>1,res3>>1,res4>>1,check(res1&1,qwq,lim1),check(res3&1,qwq,lim2)))%mod;
		}
	}
	return f[pos][sum1][sum2][sum3][sum4][lim1][lim2];
}//er

signed main() {
	cin.tie(0); ios::sync_with_stdio(false);
	cin>>n>>m; memset(f,-1,sizeof(f));
	for(int i=1;i<=n;i++) cin>>a[i]>>b[i];
	cout<<(dfs(0,0,0,0,0,0,0)+mod-1)%mod;
}

AT2272 [ARC066B] Xor Sum

考虑 \((v,u),(a+b,a \bigotimes b),(2(a\&b)+a\bigotimes b,a\bigotimes b)\),显然我们有 \(a\&b=(v-u)/2\),且 \((a\&b)\& (a\bigotimes b)=0\),即 \(\dfrac{v-u}{2}\& u=0\)

因为 \(v\ge u\),那么限制 \(v\) 做数位 dp 即可,\(v=2*\dfrac{v-u}{2}+u\),转移的时候让当前这一位的与为 0。

#include <bits/stdc++.h>
#define int long long
#define pb push_back
using namespace std;
const int mod=(int)(1e9+7);
int n,ans,f[62][100][2];

bool check(int x,int y,bool fl) {
	if(x^y) return x<y?0:1; return fl;
}

int dfs(int pos,int sum,bool lim) {
	if(pos==62) return (!lim&&!sum); //没有达到上界且没有进位,因为这位必为 0 
	if(~f[pos][sum][lim]) return f[pos][sum][lim];
	int qwq=(n>>pos)&1;
	f[pos][sum][lim]=(dfs(pos+1,sum>>1,check(sum&1,qwq,lim))+dfs(pos+1,(sum+1)>>1,check((sum+1)&1,qwq,lim))+dfs(pos+1,(sum+2)>>1,check((sum+2)&1,qwq,lim)))%mod;
	return f[pos][sum][lim];
}

signed main() {
	cin.tie(0); ios::sync_with_stdio(false);
	cin>>n; memset(f,-1,sizeof(f));
	cout<<dfs(0,0,0);
}

https://codeforces.com/contest/1670/problem/F

#include <bits/stdc++.h>
#define int long long
#define pb push_back
#define il inline
using namespace std;
const int N=1002,mod=(int)(1e9+7);
int f[60][2][5000];
int n,m,L,R,z,C[N];

int fpow(int x,int y) {
	int res=1; x%=mod;
	while(y) {
		if(y&1) res=res*x%mod;
		y>>=1; x=x*x%mod;
	} return res;
}

int dfs(int pos,int Lim,int sum) {
	if(pos==60) {
		if(!sum&&!Lim) return 1;
		return 0;
	}
	if(sum>(m>>pos)) return 0;
	if(~f[pos][Lim][sum]) return f[pos][Lim][sum];
	int qwq=(m>>pos)&1,res=0;
	if((z>>pos)&1) {
		for(int i=1;i<=n;i+=2) {
			int qaq=sum+i;
			int d=(qaq&1);
			if(d>qwq) res=(res+dfs(pos+1,1,qaq>>1)*C[i]%mod)%mod;
			if(d==qwq) res=(res+dfs(pos+1,Lim,qaq>>1)*C[i]%mod)%mod;
			if(d<qwq) res=(res+dfs(pos+1,0,qaq>>1)*C[i]%mod)%mod;
		}
	} else {
		for(int i=0;i<=n;i+=2) {
			int qaq=sum+i;
			int d=(qaq&1);
			if(d>qwq) res=(res+dfs(pos+1,1,qaq>>1)*C[i]%mod)%mod;
			if(d==qwq) res=(res+dfs(pos+1,Lim,qaq>>1)*C[i]%mod)%mod;
			if(d<qwq) res=(res+dfs(pos+1,0,qaq>>1)*C[i]%mod)%mod;
		}
	}
	return f[pos][Lim][sum]=res;
}

signed main() {
	cin.tie(0); ios::sync_with_stdio(false);
	cin>>n>>L>>R>>z;
	C[0]=1; for(int i=0;i<n;i++) C[i+1]=C[i]*(n-i)%mod*fpow(i+1,mod-2)%mod;
	memset(f,-1,sizeof(f));
	m=R; int ans=dfs(0,0,0);
	memset(f,-1,sizeof(f));
	m=L-1; ans=(ans-dfs(0,0,0))%mod;
	ans=(ans%mod+mod)%mod;
	cout<<ans;
	return 0;
}
posted @ 2022-05-03 13:44  FxorG  阅读(32)  评论(0编辑  收藏  举报