数位 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;
}