标签:卷积 row names 乘法 private char time 插入 +=
大致题意: 给定\(n\)个实数(小数长度不超过\(9\)),问有多少对数乘积为整数。
直接做肯定会爆精度。
考虑到小数长度不超过\(9\),因此我们把所有数乘上\(10^9\),然后只要判断两数乘积末尾是否至少含有\(18\)个\(0\)即可。
但直接乘又会爆\(long\ long\)。
因此我们记有\(i\)个因子\(2\)以及\(j\)个因子\(5\)的数的个数为\(s_{i,j}\)。
每次枚举\(i,j,u,v\),若\(min(i+u,j+v)\ge18\),则将答案加上\(s_{i,j}\times s_{u,v}\)(注意特判\(i=u,j=v\)的情况)。
最终将答案除以\(2\)即可。
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define LL long long
using namespace std;
int n,s[55][55];
int main()
{
RI i,j,t,u,v;long long x;char c;for(scanf("%d",&n),i=1;i<=n;++i)//读入每个数
{
x=t=0;W(!isdigit(c=getchar()));W(x=(x<<3)+(x<<1)+(c&15),isdigit(c=getchar()));//整数部分
if(c==‘.‘) W(isdigit(c=getchar())) x=(x<<3)+(x<<1)+(c&15),++t;//小数部分
u=v=0;W(t<9) ++t,++u,++v;W(!(x%2)) x/=2,++u;W(!(x%5)) x/=5,++v;++s[u][v];//乘上1e9,统计因子2和因子5的个数
}
long long ans=0;for(i=0;i<=50;++i) for(j=0;j<=50;++j) for(u=0;u<=50;++u)
for(v=0;v<=50;++v) min(i+u,j+v)>=18&&(ans+=1LL*s[i][j]*(s[u][v]-(i==u&&j==v)));//暴力统计答案,注意一个数无法与自身计算贡献
return printf("%lld\n",ans>>1),0;//输出答案
}
大致题意: 给定\(n\)个字符串,问有多少对字符串,满足其中一个字符串可以通过另一个字符串转化得到。转化的方式是,任意次操作,每次可以删去字符串开头两个字符中的一个。
考虑一对字符串\(s1,s2\)的转化(设\(len(s_1)\ge len(s_2)\)),肯定是\(s1\)转化为\(s2\)。
由于转化过程中会对\(s1\)操作\(len(s1)-len(s2)\)次,每次又只操作前两个字符,因此不可能修改到\(s1\)的最后\(len(s2)-1\)个字符。
也就是说,\(s1\)与\(s2\)的最后\(len(s2)-1\)个字符必须一致,且在\(s1\)前\(len(s1)-len(s2)+1\)个字符中必须要存在\(s2\)的第一个字符。
考虑我们对于每个串的倒串建一棵\(Trie\)树,每个节点维护出子树内包含某个字符的串数各自有多少个(可以在插入同时一并维护)。
最终只要枚举每个串作为较短串,找到它后\(len(s2)-1\)个字符对应的节点进行询问即可。
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 200000
#define SZ 1000000
using namespace std;
int n,id[N+5],p[N+5];string s[N+5];
class Trie//Trie树
{
private:
int Nt,g[30];struct node {int S[30],C[30];}O[SZ+5];
public:
I Trie() {Nt=1;}I int Ins(string s)//插入
{
RI i,j,x=1,y=1,t,l=s.length();for(i=0;i^l;++i) ++g[s[i]&31];for(i=l-1;~i;--i)
{
for(j=1;j<=26;++j) g[j]&&++O[x].C[j];--g[t=s[i]&31];//更新当前点子树内包含某种字符的串数
!O[x].S[t]&&(O[x].S[t]=++Nt),y=x,x=O[x].S[t];//跳到子节点
}return y;//返回上一个字符所在的位置用于询问
}
I int Qry(CI x,CI v) {return O[x].C[v]-1;}//询问,除去自身贡献
}T;
int main()
{
RI i,x;ios::sync_with_stdio(false),cin>>n;for(i=1;i<=n;++i) cin>>s[i],p[i]=T.Ins(s[i]);//插入
long long t=0;for(i=1;i<=n;++i) t+=T.Qry(p[i],s[i][0]&31);return printf("%lld\n",t),0;//枚举较短串统计答案
}
大致题意: 给定\(n\)个数,求\(\sum_{i=1}^n\sum_{j=i+1}^n((a_i\times a_j)\ mod\ P)\)的值(\(P=200003\),求和时不取模)。
由于模数很小,考虑枚举每一个余数\(x\),去求\((a_i\times a_j)\ mod\ P=x\)的\(i,j\)对数。
一个套路的转化,乘法变加法。
求出模\(P\)意义下的原根\(g\),令\(Lg(x)=log_gx\),根据原根的性质,\(x=1\sim P-1\)时\(Lg(x)\)各不相同。
显然有一个变形:
容易发现这就是一个循环卷积的形式,不取模的话直接\(FFT\)就可以了。
还要注意减去每个数自身的贡献。
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 200000
#define X 200003
#define PR 2
#define LL long long
using namespace std;
int n,a[N+5],s[X+5],Lg[X+5];LL p[X+5];
class FastIO
{
private:
#define FS 100000
#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
#define D isdigit(c=tc())
char c,*A,*B,FI[FS];
public:
I FastIO() {A=B=FI;}
Tp I void read(Ty& x) {x=0;W(!D);W(x=(x<<3)+(x<<1)+(c&15),D);}
}F;
namespace Poly
{
#define DB double
#define Pi acos(-1)
struct node//复数
{
DB x,y;I node(Con DB& a=0,Con DB& b=0):x(a),y(b){}
I node operator + (Con node& o) Con {return node(x+o.x,y+o.y);}
I node operator - (Con node& o) Con {return node(x-o.x,y-o.y);}
I node operator * (Con node& o) Con {return node(x*o.x-y*o.y,x*o.y+y*o.x);}
}A[4*X+5];int P,L,R[4*X+5];
I void FFT(node *s,CI op)//FFT
{
RI i,j,k;node x,y,U,S;for(i=0;i^P;++i) i<R[i]&&(x=s[i],s[i]=s[R[i]],s[R[i]]=x,0);
for(i=1;i^P;i<<=1) for(U=node(cos(Pi/i),op*sin(Pi/i)),j=0;j^P;j+=i<<1)
for(S=1,k=0;k^i;++k,S=S*U) s[j+k]=(x=s[j+k])+(y=S*s[i+j+k]),s[i+j+k]=x-y;
}
I void Sqr(int *a,long long *res)//序列自我卷积
{
RI i;for(i=0;i<=X-2;++i) A[i]=a[i];
P=1,L=0;W(P<=2*(X-2)) P<<=1,++L;for(i=0;i^P;++i) R[i]=(R[i>>1]>>1)|((i&1)<<L-1);//预处理
for(FFT(A,1),i=0;i^P;++i) A[i]=A[i]*A[i];//平方
for(FFT(A,-1),i=0;i<=2*(X-2);++i) res[i%(X-1)]+=A[i].x/P+0.1;//循环卷积下标取模
}
}
I void Init() {RI i,x=1;for(i=0;i<X-1;++i) Lg[x]=i,x=1LL*x*PR%X;}//初始化Lg
int main()
{
RI i;for(Init(),F.read(n),i=1;i<=n;++i) F.read(a[i]),a[i]&&++s[Lg[a[i]]];//读入,注意忽略a[i]=0
for(Poly::Sqr(s,p),i=0;i<X-1;++i) p[(i<<1)%(X-1)]-=s[i];//除去自身贡献
LL ans=0;for(i=1;i^X;++i) ans+=1LL*i*p[Lg[i]];return printf("%lld\n",ans>>1),0;//枚举余数统计答案
}
【AtCoder】AtCoder Grand Contest 047 解题报告($A,B,C$)
标签:卷积 row names 乘法 private char time 插入 +=
原文地址:https://www.cnblogs.com/chenxiaoran666/p/AtCoderAGC047.html