B - Mysterious LCM
$FWT$。我们设$X=prod_{i=1}^{cnt}p_i^{c_i}$,显然,$cnt$不会很大,然后,我们对$a$数组进行一定的处理,只保留$X$的因子,如果都不是$X$的因子或者他们的$lcm$不是$X$,那么就无解,否者的话,这个问题就变成了,我们要挑出最少的数,使得对于$X$的每个质因子$p_i$,都存在$p_i^{c_i}$的倍数,如果我们能求出所有的$p_i^{c_i}$,那么我们就可以对每个数运用$bitmask$,来表示它是哪些数的倍数,然后问题就变成了,我们要选一些数,使得他们位或的结果是$2^{cnt}-1$,显然,答案不会超过$cnt$,所以这个可以 $FWT$暴力做。以上均为理论分析,可是实际情况略为特殊,注意到$X$的范围很大,我们没法快速进行质因数分解,不妨我们只用$10^6$因为的质数进行质因数分解,那么$X$剩下的结果可能是$1$,或者一个大质数,或者一个大质数的平方或者两个不用的质数的乘积,$1$的话不用特殊考虑,其余情况其实我们也可以处理,我们可以对$a$中$X$的因子也筛去$10^6$以内的质数,剩余的和部分和$X$情况相同,但是肯定都是$X$的因子,所以,我们可以枚举每个数的剩余部分,和$X$做比较,如果剩余部分和$X$相同或者剩余部分是$1$,我们就忽略不管,否则,我们就找到了这两个质因子,成功的进行了分解,如果所有数的剩余部分都是$1$或者和$X$剩余部分相同,那么我们就可以把$X$的剩余部分当成一个整体来看,这样并不会影响答案,处理完之后套用$FWT$即可。
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<vector> 5 typedef long long ll; 6 typedef std::pair<ll,int> P; 7 int T,n; 8 const int N=50005,M=1e6+1; 9 ll x; 10 std::vector<int> prime;bool vis[M]; 11 ll gcd(ll x,ll y) { 12 if(!y) return x; 13 return gcd(y,x%y); 14 } 15 void init() { 16 for(int i=2;i<M;i++) if(!vis[i]) { 17 prime.push_back(i); 18 for(int j=i+i;j<M;j+=i) vis[j]=1; 19 } 20 } 21 void FWT(std::vector<ll> &a,int n,bool inv=0) { 22 for(int i=1;i<n;i<<=1) 23 for(int p=i<<1,j=0;j<n;j+=p) 24 for(int k=0;k<i;k++) 25 inv?a[i+j+k]-=a[j+k]:a[i+j+k]+=a[j+k]; 26 } 27 int main() { 28 init();int kase=0; 29 scanf("%d",&T); 30 while(T--) { 31 scanf("%d%lld",&n,&x); 32 std::vector<ll> a; 33 for(ll v;n;n--) { 34 scanf("%lld",&v); 35 if(x%v) continue; 36 a.push_back(v); 37 } 38 printf("Case %d: ",++kase); 39 if(!a.size()) puts("-1"); 40 else if(x==1) puts("1"); 41 else { 42 std::vector<P> p; 43 for(int v:prime) { 44 if(x<v) break; 45 int num=0; 46 while(x%v==0) x/=v,++num; 47 if(num) p.push_back(P(v,num)); 48 } 49 int cnt=p.size(); 50 std::sort(a.begin(),a.end()); 51 a.erase(std::unique(a.begin(),a.end()),a.end()); 52 std::vector<int> mask(a.size()); 53 for(int i=0;i<a.size();i++) { 54 mask[i]=0; 55 for(int j=0;j<cnt;j++) { 56 int num=0; 57 while(a[i]%p[j].first==0) ++num,a[i]/=p[j].first; 58 if(num==p[j].second) mask[i]|=1<<j; 59 } 60 } 61 if(x>1) { 62 std::vector<ll>w; 63 for(ll v:a) { 64 if(v!=x&&v!=1) w.push_back(v),w.push_back(x/v); 65 } 66 std::sort(w.begin(),w.end()); 67 w.erase(std::unique(w.begin(),w.end()),w.end()); 68 if(!w.size()) w.push_back(x); 69 else if(w.size()==1) w[0]=x; 70 for(int i=0;i<a.size();i++) { 71 for(int j=0;j<w.size();j++) if(a[i]%w[j]==0) mask[i]|=1<<(cnt+j); 72 } 73 cnt+=w.size(); 74 } 75 int cur=0; 76 for(int v:mask) cur|=v; 77 if(cur+1!=(1<<cnt)) puts("-1"); 78 else { 79 std::vector<ll> f(1<<cnt),g; 80 for(int i=0;i<1<<cnt;i++) f[i]=0; 81 for(int v:mask) f[v]=1;g=f;FWT(g,1<<cnt); 82 int ans=1; 83 while(!f[(1<<cnt)-1]) { 84 FWT(f,1<<cnt); 85 for(int i=0;i<1<<cnt;i++) f[i]*=g[i]; 86 FWT(f,1<<cnt,1); 87 for(int i=0;i<1<<cnt;i++) f[i]=(f[i]>0?1:0); 88 ++ans; 89 } 90 printf("%d ",ans); 91 } 92 } 93 } 94 return 0; 95 }
C - Swipe Your Time Away
签到题。对每个位置预处理上下左右四个方向的最长可延伸的长度,更新答案即可。
1 #include<iostream> 2 #include<cstdio> 3 const int N=1005; 4 int T; 5 int a[N][N],up[N][N],down[N][N],lef[N][N],rig[N][N]; 6 int main() { 7 int kase=0,n,m; 8 scanf("%d",&T); 9 while(T--) { 10 scanf("%d%d",&n,&m); 11 for(int i=1;i<=n;i++) { 12 for(int j=1;j<=m;j++) { 13 scanf("%d",a[i]+j); 14 up[i][j]=(i==1||a[i][j]!=a[i-1][j]?0:up[i-1][j]+1); 15 } 16 } 17 for(int i=n;i;i--) { 18 for(int j=1;j<=m;j++) { 19 down[i][j]=(i==n||a[i][j]!=a[i+1][j]?0:down[i+1][j]+1); 20 } 21 } 22 for(int j=1;j<=m;j++) { 23 for(int i=1;i<=n;i++) { 24 lef[i][j]=(j==1||a[i][j]!=a[i][j-1]?0:lef[i][j-1]+1); 25 } 26 } 27 for(int j=m;j;j--) { 28 for(int i=1;i<=n;i++) { 29 rig[i][j]=(j==m||a[i][j]!=a[i][j+1]?0:rig[i][j+1]+1); 30 } 31 } 32 int ans=0; 33 for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { 34 if(up[i][j]&&down[i][j]&&lef[i][j]&&rig[i][j]) { 35 ans=std::max(ans,up[i][j]+down[i][j]+rig[i][j]+lef[i][j]+1); 36 } 37 } 38 printf("Case %d: %d ",++kase,ans); 39 } 40 return 0; 41 }
D - DarkCity, CrimsonCity of FlightLand
最短路。如果我们能知道两个点之间直接飞所需要的最小油耗,那么我们就可以建图,然后跑最短路。由于飞行距离都是整数,我们不妨设$f[i]$表示飞行$i$个单位距离所需要的最小油耗(为了方便计算,这里加上机身重量$A$),那么,飞行第一个单位距离所需要的油耗是$frac{f[i]}{D}$,所以有$f[i]-frac{f[i]}{D}=f[i-1]$,从而得到$f[i]=frac{D}{D-1}f[i-1]$,其中$f[0]=A$,因此可以知道$f[i]=(frac{D}{D-1})^iA$。
1 #include<iostream> 2 #include<cstdio> 3 #include<queue> 4 #include<cmath> 5 int T,n,A,B,C,D; 6 const int N=205; 7 struct Point { 8 int x,y; 9 void input() { 10 scanf("%d%d",&x,&y); 11 } 12 Point operator - (const Point &b) const { 13 return {x-b.x,y-b.y}; 14 } 15 long long dis() { 16 long long t=1LL*x*x+1LL*y*y; 17 long long e=floor(sqrt(t)); 18 if(e*e==t) return e; 19 else return e+1; 20 } 21 }a[N]; 22 double qpow(double x,long long y) { 23 double res=1; 24 for(;y;y>>=1,x=x*x) if(y&1) res=res*x; 25 return res; 26 } 27 double d[N][N],dis[N]; 28 int main() { 29 scanf("%d",&T); 30 while(T--) { 31 scanf("%d%d%d%d%d",&n,&A,&B,&C,&D); 32 for(int i=1;i<=n;i++) { 33 a[i].input(); 34 } 35 std::queue<int> q; 36 static bool in[N]; 37 for(int i=1;i<=n;i++) in[i]=0,dis[i]=1e18; 38 for(int i=1;i<=n;i++) for(int j=1;j<=n;j++){ 39 long long p=(a[i]-a[j]).dis(); 40 double tmp=qpow(1.0*(D-1)/D,p); 41 tmp=1.0/tmp-1; 42 d[i][j]=A*tmp*C+B; 43 } 44 q.push(1);in[1]=1;dis[1]=0; 45 while(!q.empty()) { 46 int u=q.front();q.pop();in[u]=0; 47 for(int j=1;j<=n;j++) if(dis[u]+d[u][j]<dis[j]) { 48 dis[j]=dis[u]+d[u][j]; 49 if(!in[j]) q.push(j); 50 } 51 } 52 printf("%.10f ",dis[n]); 53 } 54 return 0; 55 }
E - Consecutive Letters
乱搞题。用$set$维护相同字母的连续的段即可。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<set> 5 int T,kase=0; 6 const int N=2e5+5; 7 char s[N]; 8 struct Node { 9 int l,r; 10 bool operator < (const Node &b) const { 11 if(r!=b.r) return r<b.r; 12 return l<b.l; 13 } 14 }; 15 struct Tree { 16 std::set<Node> s; 17 void init() { 18 s.clear(); 19 } 20 void insert(int l,int r) { 21 s.insert({l,r}); 22 } 23 void split(int x) { 24 auto res=*s.lower_bound({-1,x}); 25 s.erase(res); 26 if(x!=res.l) s.insert({res.l,x-1}); 27 if(x!=res.r) s.insert({x+1,res.r}); 28 } 29 int query(int x) { 30 auto it=s.lower_bound({-1,x}); 31 return (it->r)-(it->l)+1; 32 } 33 }a[26]; 34 int main() { 35 scanf("%d",&T); 36 while(T--) { 37 scanf("%s",s); 38 int n=std::strlen(s),q,op,x; 39 int l=0;for(int i=0;i<26;i++) a[i].init(); 40 for(int i=1;i<n;i++) { 41 if(s[i]!=s[i-1]) { 42 a[s[i-1]-'A'].insert(l,i-1); 43 l=i; 44 } 45 } 46 a[s[n-1]-'A'].insert(l,n-1); 47 scanf("%d",&q); 48 printf("Case %d: ",++kase); 49 while(q--) { 50 scanf("%d%d",&op,&x); 51 if(op==1) printf("%d ",a[s[x]-'A'].query(x)); 52 else a[s[x]-'A'].split(x),s[x]='#'; 53 } 54 } 55 return 0; 56 }
H - Triangle Inside Rectangle Inside Pentagon
几何。很容易发现,$n$边形和$n-1$边形有一个重合的顶点,且$n$边形和$n-1$边形关于$n$边形的中心和该重合顶点的连线对称,然后该顶点旁还有一个角度均可计算的三角形,其中它的两条边分别对应了这两个多边形的边,如果我们设$f[n]$表示$n$边形的边长相对于三角形的边长放缩的比例,$g[n]$表示正$n$边形的内角大小,那么根据正弦定理可以得到$f[n]=frac{f[n-1]}{sin(g[n])}sin(pi-g[n]-frac{g[n]-g[n-1]}{2})$,预处理$f[n]$,我们就可以$O(1)$的计算$n$边形的边长并计算面积。
1 #include<iostream> 2 #include<cstdio> 3 #include<cmath> 4 const double PI=acos(-1); 5 const int N=1e5+5; 6 double f[N],g[N]; 7 void init() { 8 f[3]=PI/3;g[3]=1; 9 for(int i=4;i<N;i++) { 10 f[i]=PI*(i-2)/i; 11 g[i]=g[i-1]/sin(f[i])*sin(PI-f[i]-(f[i]-f[i-1])/2); 12 } 13 } 14 int main() { 15 int T,n,s; 16 init(); 17 scanf("%d",&T); 18 while(T--) { 19 scanf("%d%d",&n,&s); 20 double l=g[n]/sin(PI-f[n])*sin(f[n]/2); 21 printf("%.10f ",s*s*l*l*sin(PI-f[n])/2*n); 22 } 23 return 0; 24 }
I - IFibonacci Power Sum
矩阵快速幂。对于$fib(n)$,它的求法我们应该不陌生,矩阵快速幂即可。如果要求$sum fib(i)$,其实和前一种求法几乎一样,只要将矩阵扩展一维用于累和即可。所以此题也一样,如果我们知道如何求$fib(n)^k$,剩下的问题就好解决了,我们可以稍微的变换下,$fib(n)^k=(fib(n-1)+fib(n-2))^k=sum_{i=0}^{k}C_k^ifib(n-1)^ifib(n-2)^{k-i}$。右边都是相邻两项的幂次的乘积,似乎和左边格格不入,但是我们知道,$fib(n)^k=fib(n)^kfib(n-1)^0$,这样,左右两边形式都一样了,这就启发我们,对形如$fib(n)^ifib(n-1)^{k-i}$的式子进行递推维护,我们将其化简一下,有$fib(n)^ifib(n-1)^{k-i}=fib(n-1)^{k-i}(fib(n-1)+fib(n-2))^i=sum_{j=0}^iC_i^jfib(n-1)^{k-i+j}fib(n-2)^{i-j}$,这样,我们可以写出递推矩阵了,而求和的话,同样是加一维进行累和即可。
1 #include<iostream> 2 #include<cstdio> 3 const int N=15; 4 typedef long long ll; 5 const ll mod=1e9+7; 6 int T,kase=0,C,K,d; 7 ll comb[N][N]; 8 struct Matrix { 9 ll a[N][N]; 10 void init(int x=0) { 11 for(int i=0;i<d;i++) for(int j=0;j<d;j++) { 12 a[i][j]=(i==j?x:0); 13 } 14 } 15 Matrix operator * (const Matrix &b) const { 16 Matrix res;res.init(); 17 for(int i=0;i<d;i++) for(int k=0;k<d;k++) if(a[i][k]) { 18 for(int j=0;j<d;j++) (res.a[i][j]+=a[i][k]*b.a[k][j])%=mod; 19 } 20 return res; 21 } 22 Matrix operator ^ (ll y) const { 23 Matrix res,x; 24 res.init(1);x=*this; 25 for(;y;y>>=1,x=x*x) if(y&1) res=res*x; 26 return res; 27 } 28 }a,b; 29 void init() { 30 comb[0][0]=1; 31 for(int i=1;i<N;i++) { 32 comb[i][0]=comb[i][i]=1; 33 for(int j=1;j<i;j++) comb[i][j]=(comb[i-1][j]+comb[i-1][j-1])%mod; 34 } 35 } 36 int main() { 37 init(); 38 scanf("%d",&T); 39 while(T--) { 40 ll n;scanf("%lld%d%d",&n,&C,&K);d=K+2; 41 a.init();a.a[K+1][K+1]=1; 42 for(int i=0;i<=K;i++) { 43 for(int j=0;j<=i;j++) a.a[i][K-i+j]=comb[i][j]; 44 } 45 b=a; 46 for(int i=0;i<d-1;i++) (b.a[K+1][i]+=b.a[K][i])%=mod; 47 a=a^(C-1); 48 a=b*a; 49 a=a^(n); 50 printf("Case %d: %lld ",++kase,a.a[K+1][0]); 51 } 52 return 0; 53 }