码迷,mamicode.com
首页 > 其他好文 > 详细

题解 CF1103E Radix sum

时间:2020-07-08 12:56:43      阅读:44      评论:0      收藏:0      [点我收藏+]

标签:ace   size   如何   个数   typename   set   溢出   整数   get   

题目传送门

题目大意

给出一个\(n\)个数的序列\(a_{1,2,..,n}\),可以选\(n\)次,每次可以选与上次选的相同的数,问对于\(\forall p\in[0,n-1]\)满足选出来的数进行十进制不进位加法结果为\(p\)的方案数。答案对\(2^{58}\)取模。

思路

乍一看,这是一道\(k=10\)\(k\)进制\(\text {FWT}\)的板题。但是,我们发现这个模数十分的神仙,于是我们就需要解决下面两个问题:

  • 如何求出\(10^5\)的逆元

  • 如何求出模\(2^{58}\)意义下的\(w_{10}\)

似乎第一个问题比较好解决一点。这里解释一下为什么是除以\(10^5\),本来逆运算的时候应该除以\(10\),但是我们为了方便可以先不除,最后一起除。

我们发现\(5\)在模\(2^{58}\)意义下是有逆元的,于是我们的问题就是如何求出\(2^5\)的逆元。我们可以先求出模\(2^{64}\)下的答案,然后我们发现直接除以\(2^5\)就是答案了。

于是,我们发现模\(2^{64}\)其实就是\(\text {unsigned long long}\)自然溢出。

我们现在需要解决第二个问题。我们发现这个无理数不可能在\(2^{58}\)有对应的数。而我们现在又发现\(w_{10}^5=w_2=-1\)\(2^{64}\),于是我们只需要求到\(w_{10}^2=w_{5}\)就可以求到正确的\(w_{10}\)。于是,我们可以设\(x=w_5\),那么,我们就可以在\(\text {FWT}\)过程中用多项式计算和表示了。

我们注意到一点,就是\(w_5^5=x^5=1\),于是,我们求到的多项式实际上就等价于在模\(x^5-1\)意义下的。而我们发现\(x=1\)\(x^5-1=0\),所以我们实际上也等价于在模\(1+x+x^2+x^3+x^4\)意义下的多项式。但是实际上两个都是可以的。

于是,最后的问题就是如何把最后的多项式转换成一个整数。我们可以证明,该多项式除了次数为\(0\)的项其他项系数一定为\(0\),可以使用反证法证明。

于是,我们就在\(\Theta(2500n\log n)\)的时间复杂度解决了这个问题。

\(\text {Code}\)

#include <bits/stdc++.h>
using namespace std;

#define ull unsigned long long
#define Int register int
#define MAXN 400005
#define lim 100000

template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < ‘0‘ || c > ‘9‘){if (c == ‘-‘) f = -f;c = getchar();}while (c >= ‘0‘ && c <= ‘9‘){t = (t << 3) + (t << 1) + c - ‘0‘;c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar (‘-‘);}if (x > 9) write (x / 10);putchar (x % 10 + ‘0‘);}

int n;
ull inv = 6723469279985657373ull;

struct Poly{
	ull a[5];
	Poly(){a[0] = a[1] = a[2] = a[3] = a[4] = 0;}
	Poly operator + (Poly p){
		Poly res;
		for (Int i = 0;i < 5;++ i) res.a[i] = a[i] + p.a[i];
		return res;
	}
	Poly operator * (Poly p){
		ull tmp[10];Poly res;
		memset (tmp,0,sizeof (tmp));
		for (Int i = 0;i < 5;++ i)
			for (Int j = 0;j < 5;++ j)
				tmp[i + j] += a[i] * p.a[j];
		for (Int i = 0;i < 5;++ i) res.a[i] = tmp[i] - tmp[i + 5];
		return res; 
	}
	ull Turn (){
		ull tmp = a[1];for (Int i = 0;i < 5;++ i) a[i] -= tmp;
		tmp = a[2];for (Int i = 0;i < 5;i += 2) a[i] -= tmp;
		return a[0];
	}
}ans[MAXN],bas[10],zero;

Poly quick_pow (Poly a,int b){
	Poly res;memset (res.a,0,sizeof (res.a)),res.a[0] = 1;
	for (;b;b >>= 1,a = a * a) if (b & 1) res = res * a;
	return res;
}

void FWT (Poly *a,int type){
	int id[10];Poly b[10];
	for (Int len = 1;len < lim;len *= 10)
		for (Int i = 0;i < lim;i += len * 10)
			for (Int j = 0;j < len;++ j){
				for (Int d = 0;d < 10;++ d){
					id[d] = i + j + d * len;
					b[d] = a[id[d]],a[id[d]] = zero;
				}
				for (Int d = 0;d < 10;++ d)
					for (Int e = 0;e < 10;++ e)
						a[id[d]] = a[id[d]] + bas[(10 + type) * d * e % 10] * b[e];
			}
}

signed main(){
	read (n);
	for (Int i = 1,a;i <= n;++ i) read (a),++ ans[a].a[0];
	for (Int i = 0;i < 10;++ i) bas[i].a[i % 5] = i >= 5 ? -1 : 1;
	FWT (ans,1);
	for (Int i = 0;i < lim;++ i) ans[i] = quick_pow (ans[i],n);
	FWT (ans,-1);
	for (Int i = 0;i < n;++ i) write (((ans[i].Turn() * inv) >> 5) % (1ull << 58)),putchar (‘\n‘);
	return 0;
}

参考博客

https://www.luogu.com.cn/blog/foreverlasting/solution-cf1103e
https://www.cnblogs.com/Mr-Spade/p/10390667.html
https://memset0.cn/cf1103e
https://www.luogu.com.cn/blog/command-block/wei-yun-suan-juan-ji-yu-ji-kuo-zhan

题解 CF1103E Radix sum

标签:ace   size   如何   个数   typename   set   溢出   整数   get   

原文地址:https://www.cnblogs.com/Dark-Romance/p/13266284.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!