SDU暑期集训排位(3)题解

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 }
View Code

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 }
View Code

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 }
View Code

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 }
View Code

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 }
View Code

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 }
View Code
原文地址:https://www.cnblogs.com/Onlymyheart/p/11271758.html