1. 程式人生 > 實用技巧 >【AtCoder】AtCoder Grand Contest 047 解題報告($A,B,C$)

【AtCoder】AtCoder Grand Contest 047 解題報告($A,B,C$)

點此進入比賽

\(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;//列舉餘數統計答案
}