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

【AtCoder】AtCoder Grand Contest 047 解题报告($A,B,C$)

时间:2020-08-18 14:03:59      阅读:276      评论:0      收藏:0      [点我收藏+]

标签:卷积   row   names   乘法   private   char   time   插入   +=   

点此进入比赛

\(A\):Integer Product(点此看题面

大致题意: 给定\(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;//输出答案
}

\(B\):First Second(点此看题面

大致题意: 给定\(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;//枚举较短串统计答案
}

\(C\):Product Modulo(点此看题面

大致题意: 给定\(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)\)各不相同。

显然有一个变形:

\[(a_i\times a_j)\ mod\ P=x\Leftrightarrow (Lg(a_i)+Lg(a_j))\ mod\ (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

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