【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}\)
最終將答案除以\(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\)
考慮我們對於每個串的倒串建一棵\(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;//列舉餘數統計答案
}