博客食用更佳bossbaby's blog
思路
并查集
每次使两位相同,相当于在计数时把两个位当成一位来算
所以我们可以用并查集,在每次操作时把两个位用并查集并在一起,查找时用,设最后集合个数为M
那么答案是\(9*(M-1)^{10}\)
因为和1并在一起的数只有9种选择(首位不为0)
但是这样的话复杂度最大是\(O(10^{10})(n,m<=10^5)\)
所以不行
线段树
于是我们考虑线段树的区间操作
可惜不行...
倍增(st表)
另外一种区间操作就是st表
\(st[i][j]\)储存从\(i\)开始往后\(2^j\)的区间
那么就用并查集来判断每个区间的连通性,
每次合并时就用2进制从大到小分成区间合并
比如从\(l_1\)开始的23个数
就分成\(st[l_1][4],st[l_1][2],st[l_1][1],st[l_1][0]\)
分别与\(st[l_2][4],st[l_2][2],st[l_2][1],st[l_2][0]\)合并
最后再把每个区间下放到它的一半这两个区间,最后下放到\(st[i][0]\)就可以统计了
代码
#include
#define N 100010
#define P 1000000007
using namespace std;
int st[N][20],fa[N*18],cnt=0,s[N*18];
int n,m;
int find(int x){
if(x!=fa[x])fa[x]=find(fa[x]);
return fa[x];
}
void unionn(int x,int y,int k){
int r1=find(st[x][k]),r2=find(st[y][k]);
if(r1>r2)swap(r1,r2),swap(x,y);
fa[r2]=r1;
find(y);
}//并查集
templatevoid read(T&x){
x=0;
char c=getchar();
while(!isdigit(c))c=getchar();
while(isdigit(c))x=x*10+c-'0',c=getchar();
}//快读
templatevoid write (const T x){
if(x<0){
putchar('-');
return write(-x);
}
if(x>9)write(x/10);
putchar(x%10+'0');
}//快写
int main(){
read(n);read(m);
if(n==1){
write(10);return 0;
}
for(int j=0;j<=18;j++)
for(int i=1;i+(1<=0;j--){
if(l1+(1<=1;j--){
for(int i=1;i+(1<