zoukankan      html  css  js  c++  java
  • 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
  • 相关阅读:
    Ynoi2016 这是我自己的发明
    Luogu P5268 [SNOI2017]一个简单的询问
    Ynoi2017 由乃的玉米田
    BZOJ4320 [Shanghai2006]Homework
    JOISC2014C 歴史の研究
    莫队
    LOJ6119 「2017 山东二轮集训 Day7」国王
    Luogu P3295 [SCOI2016]萌萌哒
    10.1 进程间通信--消息队列
    9.2 网络协议理论
  • 原文地址:https://www.cnblogs.com/Onlymyheart/p/11271758.html
Copyright © 2011-2022 走看看