CF1567C——两种解法:隔离的思想状压dp;并拓展到一般情况
Posted hans774882968
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CF1567C——两种解法:隔离的思想状压dp;并拓展到一般情况相关的知识,希望对你有一定的参考价值。
题意:定义了另一种加法,把加法进位的位置从i+1变为i+2。求满足x+y=n
的(x,y)
个数,x和y是正整数,1000组数据,n<=1e9
一道让人思维开阔的好题。
法一:隔离的思想。既然进位位置是i+2,那么i、i+2、i+4……和i+1、i+3、……是互不干扰的。于是我们惊奇地发现,按位置下标i
模2的剩余类内部,进行的是正常的加法。因此我们按位置下标的奇偶,分出两个数,按乘法原理组合出方案即可。比如n=114514,分出141和154,其中一个方案是071+070=141,004+150=154,则007014+017500=114514。高位补0是为了一致性,奇数下标的数字用粗体标出。
但这样算出的方案是含有(n,0)和(0,n)这两种非法方案的,因此还要减2。
拓展:若进位位置改成i+x,则按位置下标模x的剩余类来分出x个整数,进行乘法原理即可~
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define rep(i,a,b) for(int i = (a);i <= (b);++i)
#define re_(i,a,b) for(int i = (a);i < (b);++i)
#define dwn(i,a,b) for(int i = (a);i >= (b);--i)
const int N = 3e5 + 5;
int n;
void dbg(){puts("");}
template<typename T, typename... R>void dbg(const T &f, const R &... r) {
cout << f << " ";
dbg(r...);
}
template<typename Type>inline void read(Type &xx){
Type f = 1;char ch;xx = 0;
for(ch = getchar();ch < '0' || ch > '9';ch = getchar()) if(ch == '-') f = -1;
for(;ch >= '0' && ch <= '9';ch = getchar()) xx = xx * 10 + ch - '0';
xx *= f;
}
int main(int argc, char** argv) {
int T;read(T);
while(T--){
read(n);
vector<int> v1,v2;
for(int i = 0;n;++i,n /= 10){
if(i&1) v2.push_back(n%10);
else v1.push_back(n%10);
}
reverse(v1.begin(),v1.end());
reverse(v2.begin(),v2.end());
int n1 = 0,n2 = 0;
for(int v: v1) n1 = 10*n1+v;
for(int v: v2) n2 = 10*n2+v;
printf("%d\\n",(n1+1)*(n2+1)-2);
}
return 0;
}
法二:状压dp。官方题解讲得很晦涩,但它的思想很简单。还是拿n=114514举例。首先,我们假设已经求出了11451的答案,然后看能不能转移,因此状态需要记录i
,表示当前数字是从位置i
到最高位(下标为len-1
)的。但这不够,因为114514可能向11451的十位进位,所以状态需要记录数字的十位(下标为1)是否有来自低位的进位。因为114514的十位就是11451的个位,即11451的个位的进位情况,必须和114514的十位的进位情况保持同步,所以状态还需要记录数字的个位(下标为0)是否有进位。
总结一下,我们引入了dp[i][j]
,而进位情况同步的要求引入了状态k
,j
、k
分别表示当前数字的十位和个位是否有来自低位的进位。
为了方便,我们先允许非负整数,再减去(n,0)和(0,n)这两种非法方案。所以答案是dp[0][0][0]-2
边界:dp[len-1][0][k]=d[dl-1]+k
,既然只有1位,那么十位必须是不进位才有方案。
转移:分11451的十位有和没有进位来讨论。(d[i]+1-k)*dp[i+1][0][j] + (9+k-d[i])*dp[i+1][1][j]
拓展:若进位位置改成i+x,则就是一个纯正的状压dp。个位以外的所有进位情况都是保持同步。具体参照”拓展“的代码(对拍已通过,可以放心食用~)。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define rep(i,a,b) for(int i = (a);i <= (b);++i)
#define re_(i,a,b) for(int i = (a);i < (b);++i)
#define dwn(i,a,b) for(int i = (a);i >= (b);--i)
const int N = 3e5 + 5;
int n,dp[13][2][2];
void dbg(){puts("");}
template<typename T, typename... R>void dbg(const T &f, const R &... r) {
cout << f << " ";
dbg(r...);
}
template<typename Type>inline void read(Type &xx){
Type f = 1;char ch;xx = 0;
for(ch = getchar();ch < '0' || ch > '9';ch = getchar()) if(ch == '-') f = -1;
for(;ch >= '0' && ch <= '9';ch = getchar()) xx = xx * 10 + ch - '0';
xx *= f;
}
int main(int argc, char** argv) {
int T;read(T);
while(T--){
read(n);
memset(dp,0,sizeof dp);
vector<int> d;
for(;n;n /= 10) d.push_back(n%10);
int dl = d.size();
dp[dl-1][0][0] = d[dl-1]+1;
dp[dl-1][0][1] = d[dl-1];
dwn(i,dl-2,0){
rep(j,0,1){
rep(k,0,1){
dp[i][j][k] = (d[i]+1-k)*dp[i+1][0][j] + (9+k-d[i])*dp[i+1][1][j];
}
}
}
// rep(i,0,dl-1) rep(j,0,1) rep(k,0,1) cout<<dp[i][j][k]<<" \\n"[j&&k];//
printf("%d\\n",dp[0][0][0]-2);
}
return 0;
}
拓展
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define rep(i,a,b) for(int i = (a);i <= (b);++i)
#define re_(i,a,b) for(int i = (a);i < (b);++i)
#define dwn(i,a,b) for(int i = (a);i >= (b);--i)
const int N = 3e5 + 5;
int n,dp[13][32];
void dbg(){puts("");}
template<typename T, typename... R>void dbg(const T &f, const R &... r) {
cout << f << " ";
dbg(r...);
}
template<typename Type>inline void read(Type &xx){
Type f = 1;char ch;xx = 0;
for(ch = getchar();ch < '0' || ch > '9';ch = getchar()) if(ch == '-') f = -1;
for(;ch >= '0' && ch <= '9';ch = getchar()) xx = xx * 10 + ch - '0';
xx *= f;
}
int solve1(int n,int pos){
vector<int> v[pos];
for(int i = 0;n;++i,n /= 10){
v[i%pos].push_back(n%10);
}
re_(i,0,pos) reverse(v[i].begin(),v[i].end());
int ans = 1;
re_(i,0,pos){
int x = 0;
for(int va: v[i]) x = 10*x+va;
ans *= x+1;
}
return ans-2;
}
int solve2(int n,int pos){
memset(dp,0,sizeof dp);
vector<int> d;
for(;n;n /= 10) d.push_back(n%10);
int dl = d.size();
dp[dl-1][0] = d[dl-1]+1;
dp[dl-1][1] = d[dl-1];
dwn(i,dl-2,0){
re_(j,0,1<<pos){
int k = j&1;
dp[i][j] = (d[i]+1-k)*dp[i+1][j>>1] + (9+k-d[i])*dp[i+1][(1<<(pos-1))|(j>>1)];
}
}
// rep(i,0,dl-1) re_(j,0,1<<pos) cout<<dp[i][j]<<" \\n"[j+1==(1<<pos)];//
return dp[0][0]-2;
}
int main(int argc, char** argv) {
int T;read(T);
while(T--){
read(n);
rep(i,2,5){
int a1 = solve1(n,i),a2 = solve2(n,i);
printf("%d %d\\n",a1,a2);
assert(a1 == a2);
}
}
return 0;
}
以上是关于CF1567C——两种解法:隔离的思想状压dp;并拓展到一般情况的主要内容,如果未能解决你的问题,请参考以下文章
CodeForces - 1567C Carrying Conundrum(思维/状压)