• 秋令营(未完)


    Day1 T1:

    链接:https://www.luogu.com.cn/problem/T149958

    用字符串读入的水题。

     1 #include<cstdio>
     2 #include<iostream>
     3 #include<cstring>
     4 using namespace std;
     5 char s[1000005];
     6 int cnt;
     7 int main(){
     8     freopen("magic.in","r",stdin);
     9     freopen("magic.out","w",stdout);
    10     scanf("%s",s);
    11     int len=strlen(s);
    12     for(int i=0;i<len;i++){
    13         if(s[i]=='0'||s[i]=='1'||s[i]=='2'||s[i]=='9'){
    14             cnt++;
    15         }
    16     }
    17     if(cnt==len) printf("Yes
    ");
    18     else printf("No
    ");
    19     return 0;
    20 }
    AC代码

     Day1 T2:

     链接:https://www.luogu.com.cn/problem/U132839

    Day1 T3:

     链接:https://www.luogu.com.cn/problem/U132840

    Day1 T4:

     链接:https://www.luogu.com.cn/problem/T149959

    Day2 T1:

     链接:https://www.luogu.com.cn/problem/T149963

    明显的贪心,但这道题自己在考场上时考虑过于简单,只是a从小到大排序,b从小到大排序,然后for i=1...n,sum+=a[i]+b[i+1]。

    但是忽略了一些特殊情况:如a=0.1 b=0.1,100。

    此时应该让 a[1]和b[1]相乘,然后再加上b[2]才为最大值。

    那么可以直接在考虑的时候在a[]数组中直接添加一个元素a[]=1,这样不会对答案产生任何影响,然后对a[]和b[]分别排序,sum+=a[i]*b[i]即可。

    这是一种比较重要且常见的思想。

    #include<cstdio>
    #include<algorithm>
    #include<iostream>
    using namespace std;
    const int N=100005;
    int n;
    double a[N],b[N],sum;
    int main(){
        scanf("%d",&n);
        for(int i=1;i<=n;i++) scanf("%lf",&a[i]);
        a[n+1]=1;
        for(int i=1;i<=n+1;i++) scanf("%lf",&b[i]);
        sort(a+1,a+n+2);
        sort(b+1,b+n+2);
        for(int i=1;i<=n+1;i++) sum+=a[i]*b[i];
        printf("%lf",sum);
        return 0;
    }
    AC代码

    Day2 T2:

     链接:https://www.luogu.com.cn/problem/T149734

    根据唯一分解定理,可以将每个正整数n分解成$p_1^{q_1} imes p_2^{q_2} imes p_3^{q_3}...$,然后对于每个数统计质因数2和5的个数。(这里自己想到了)

    下一步便要找出0数量最多的那m个数。如果用暴力枚举的话只能的30pts。

    考虑dp:

    设计:

    设dp[i][j][k]表示从前i个数中选j个,有k个5时2的个数最多是多少。(注意最后一维要是5的个数。2的个数太多,枚举时会T)

    不难发现,dp[i][j][k]总是从dp[i-1][][]中转移过来的,所以可以压掉第一维。直接dp[j][k]。

    转移:

    初始f[0][0]=0,其他赋为0xc0。

    f[j][k]=max(f[j][k],f[j-1][k-s[i]]+t[i])。其中s[i]表示i中5的个数,t[i]表示i中2的个数。

    从不选i(f[j][k])和选i(f[j-1][k-s[i]]+t[i])转移。

    枚举5的个数k,最后答案为max(min(k,f[n][m][k])。

    AC代码:

     1 #include<cstdio>
     2 #include<iostream>
     3 #include<cstring>
     4 #include<algorithm> 
     5 using namespace std;
     6 typedef long long ll;
     7 const int N=205;
     8 int n,m,ans;
     9 int cnt[N][2];
    10 ll a;
    11 int f[N][N*30];
    12 //1<<60 
    13 //f[i][j][k] 从前i个数中选j个,其中2的个数为k  5的个数 
    14 int main(){
    15     scanf("%d%d",&n,&m);
    16     for(int i=1;i<=n;i++){
    17         scanf("%lld",&a);
    18         while(a%2==0){
    19             cnt[i][0]++;
    20             a/=2;
    21         }
    22         while(a%5==0){
    23             cnt[i][1]++;
    24             a/=5;
    25         }
    26     }
    27     memset(f,0xc0,sizeof(f));
    28     f[0][0]=0;
    29     for(int i=1;i<=n;i++){
    30         for(int j=min(m,i);j>=1;j--){
    31             for(int k=cnt[i][1];k<=30*i;k++)
    32                 f[j][k]=max(f[j-1][k-cnt[i][1]]+cnt[i][0],f[j][k]);
    33         }
    34     }
    35     for(int i=1;i<=30*n;i++){
    36         int t=min(i,f[m][i]);
    37         ans=max(ans,t);
    38     }
    39     printf("%d",ans);
    40     return 0;
    41 }
    AC代码
      

    Day2 T3:

    链接:https://www.luogu.com.cn/problem/T149741

     Day2 T4:

     链接:https://www.luogu.com.cn/problem/T149969

     

    Day3 T1:

     
    这道题注意分析数据范围:因为c[i]<=1000,但询问次数m<=1000000,所以一定会有重复的询问,所以需要离线预处理,最后直接O(1)输出即可。
    并且这道题不需要用快速幂,相反快速幂会多一个logn的常数。
     
    AC代码:
     1 #include<cstdio>
     2 #include<iostream>
     3 using namespace std;
     4 const int mod=10007;
     5 const int N=1005;
     6 int n,m,sum[N],a[N];
     7 void init(){
     8     for(int i=1;i<=1000;i++){
     9         int t=1;
    10         for(int j=0;j<=n;j++){
    11             sum[i]+=a[j]*t;
    12             sum[i]%=mod;
    13             t*=i;
    14             t%=mod;
    15         }
    16     }
    17 }
    18 int main(){
    19     scanf("%d%d",&n,&m);
    20     for(int i=n;i>=0;i--) scanf("%d",&a[i]);
    21     init();
    22     for(int i=1;i<=m;i++){
    23         int c;
    24         scanf("%d",&c);
    25         printf("%d
    ",sum[c]);
    26     }
    27     return 0;
    28 }
    AC代码

    Day3 T2:

    链接:https://www.luogu.com.cn/problem/T149876

     根据唯一分解定理,可以得到结论:a和b的公约数数量=gcd(a,b)的约数数量。

    ∵c|a,c|b,$s_i<=min(q_i,r_i)$

    $a=p_1^{q_1} imes p_2^{q_2} imes p_3^{q_3} imes ... imes p_n^{q_n}$

    $b=p_1^{r_1} imes p_2^{r_2} imes p_3^{r_3} imes ... imes p_n^{r_n}$

    ∴$c=p_1^{min(r_1,q_1)} imes p_2^{min(r_2,q_2)} imes p_3^{min(r_3,q_3)} imes ... imes p_n^{min(r_n,q_n)}$

    设$s_i=min(q_i,r_i)$,则数量为$(s_1+1) imes (s_2+1) imes ... imes(s_i+1)... imes(s_n+1)$

    所以这道题可以先预处理出[1,100000]中每个数的约数的个数:

    for(int i=1;i<=100000;i++){
        for(int j=i;j<=100000;j+=i){
            f[j]++;
        }
    }

    注意这段代码的时间复杂度为logn级别的。

    然后每输入a,b,求出它们的gcd,O(1)输出答案。

    AC代码:

     1 #include<cstdio>
     2 #include<iostream>
     3 using namespace std;
     4 const int N=100005;
     5 int T;
     6 int f[N];
     7 int gcd(int a,int b){
     8     if(b==0) return a;
     9     return gcd(b,a%b);
    10 }
    11 void init(){
    12     for(int i=1;i<=100000;i++){
    13         for(int j=i;j<=100000;j+=i){
    14             f[j]++;
    15         }
    16     }
    17 }//1+1/2+1/3+..+1/n是logn级别的 
    18 int main(){
    19     scanf("%d",&T);
    20     init();
    21     while(T--){
    22         int a,b;
    23         scanf("%d%d",&a,&b);
    24         int d=gcd(a,b);
    25         printf("%d
    ",f[d]);
    26     }
    27     return 0;
    28 }
    AC代码

    Day3 T3:

    链接:https://www.luogu.com.cn/problem/T149880

    找规律:

    通过手推数据(严格证明)可以得到规律:

    如果最大值大于其他所有数的和的话,那么先手一定会赢(让先手一直选最大的那个);

    如果不是的话,所有数和为偶数,则后手赢;否则先手赢。

    AC代码:

     1 #include<cstdio>
     2 #include<iostream>
     3 #include<algorithm>
     4 using namespace std;
     5 int n,maxx,T,sum;
     6 int main(){
     7     scanf("%d",&T);
     8     while(T--){
     9         sum=0;
    10         maxx=0;
    11         scanf("%d",&n);
    12         for(int i=1;i<=n;i++){
    13             int a;
    14             scanf("%d",&a);
    15             sum+=a;
    16             maxx=max(a,maxx);
    17         }
    18         if(maxx>(sum-maxx)){printf("Alice
    ");continue;}
    19         if(sum&1) printf("Alice
    ");
    20         else printf("Bob
    ");
    21     }
    22     return 0;
    23 }
    AC代码

    Day3 T4:

    链接:https://www.luogu.com.cn/problem/U132994

    这道题自己的思路被限制到了,在考场上一直在求解n元一次方程,并没有写成递推的形式。然而求解n元一次方程代码量大,容易把自己绕进去,所以以后尽量避开n元一次方程组的求解。

    这道题可以写成递推形式,从小到大递推。根据题中的信息,可以列出递推式:

    $A_{i+1}=-2 imes A_i+A_{i-1}+2 imes d$

    然后设$A_2=x$,那么通过递推,那么任意$A_i$可以写成ax+b的形式,则用s[i]存a,t[i]存b。

    每次递推,s[i+1]=-2*s[i]+s[i-1],t[i+1]=-2*t[i]+t[i-1]+2*d。

    那么A[n]也可以用ax+b表示出来,而A[n]也是已知的。从而可以解出x。A[m]即为s[m]*x+t[m]。

    AC代码:

     1 #include<cstdio>
     2 #include<iostream>
     3 using namespace std;
     4 const int N=105;
     5 int n,m;
     6 double d,a[N];
     7 double s[N],t[N];
     8 int main(){
     9     scanf("%d%d",&n,&m);
    10     scanf("%lf%lf%lf",&d,&a[1],&a[n]);
    11     t[1]=a[1]; s[2]=1;
    12     for(int i=3;i<=n;i++){
    13         s[i]=-2*s[i-1]+s[i-2];
    14         t[i]=-2*t[i-1]+t[i-2]+2*d;
    15     }
    16     double x=(a[n]-t[n])/s[n];
    17     printf("%.3lf",s[m]*x+t[m]);
    18     return 0;
    19 }
    AC代码

    Day4 T1:

    链接:https://www.luogu.com.cn/problem/T150020

    法一:一个二次函数求最值的题目,直接分类讨论,比较建议。

     1 #include<cstdio>
     2 #include<iostream>
     3 #include<cmath>
     4 using namespace std;
     5 int a,b,c;
     6 int main(){
     7     freopen("game.in","r",stdin);
     8     freopen("game.out","w",stdout);
     9     scanf("%d%d%d",&a,&b,&c);
    10     if(a==0){
    11         if(b>0) printf("12");
    12         else printf("0");
    13         return 0;
    14     }
    15     double t=b*1.0/(-2*a);
    16     if(a>0){
    17         if(t<=0) printf("12");
    18         else if(t>0&&t<12){
    19             double l=t;
    20             double r=12-t;
    21             if(l>=r) printf("0");
    22             else printf("12");
    23         }
    24         else printf("0");
    25     }
    26     else{
    27         if(t>0&&t<12){
    28             if(t-floor(t)<=ceil(t)-t) printf("%d",(int)floor(t));
    29             else printf("%d",(int)ceil(t));
    30         }
    31         else if(t<=0) printf("0");
    32         else printf("12");
    33     }
    34     return 0;
    35 }
    AC代码

    法二:暴力枚举,从0到12,注意都要开ll,尤其注意在赋值之前的计算时也要转成,否则会炸。

     1 #include<cstdio>
     2 #include<iostream>
     3 using namespace std;
     4 typedef long long ll;
     5 ll a,b,c,id;
     6 ll maxx=-1e16;
     7 int main(){
     8     scanf("%lld%lld%lld",&a,&b,&c);
     9     for(ll i=0;i<=12;i++){
    10         ll t=a*i*i+b*i+c;
    11         if(maxx<t) maxx=t,id=i;
    12     }
    13     printf("%lld
    ",id);
    14     return 0;
    15 }
    AC代码

    Day4 T2:

     链接:https://www.luogu.com.cn/problem/T150022

     法一:

    直接暴力从L~R枚举,因为L很大,R很大,但R-L很小,而判断一个数是不是质数只需要$sqrt(n)$的复杂度,这样从L到R暴力判断每个是不是素数只需要$O((R-L) imes sqrt(R))$的时间复杂度。这个是可以接受的。

    AC代码:

     1 #include<cstdio>
     2 #include<iostream>
     3 #include<cmath>
     4 using namespace std;
     5 typedef long long ll;
     6 int l,r;
     7 int flag;
     8 ll sum;
     9 int main(){
    10     scanf("%d%d",&l,&r);
    11     for(int i=l;i<=r;i++){
    12         int t=sqrt(i);
    13         flag=0;
    14         for(int j=2;j<=t;j++){
    15             if(!(i%j)) {flag=1;break;}
    16         }
    17         if(!flag) sum+=i;
    18     }
    19     printf("%lld
    ",sum);
    20     return 0;
    21 }
    AC代码

    法二:

     根据欧拉筛的思想,每个数只会被它的最小质因数给筛掉。而R范围中的数,它的最小质因数只会出现2~$sqrt(n)$的范围内。所以可以先预处理出2~$sqrt(n)$中的素数表,然后用这些素数来筛[L,R]区间内的素数。

    AC代码:

     1 #include<cstdio>
     2 #include<iostream>
     3 using namespace std;
     4 typedef long long ll;
     5 const int N=1000005;
     6 int cnt;
     7 int prime[N];
     8 bool vis[N],vis1[N];
     9 int L,R;
    10 ll sum;
    11 void is_prime(){
    12     for(int i=2;i*i<=R;i++){
    13         if(!vis[i]){
    14             for(int j=i*2;j*j<=R;j+=i){
    15                 vis[j]=1;//筛[2,sqrt(R)] 
    16             }
    17             for(int j=max(2,(L+i-1)/i)*i;j<=R;j+=i){
    18                 //(a+i-1)/i为[a,b)区间内第一个数至少为i的多少倍 
    19                 vis1[j-L]=1;//筛[a,b] 
    20             } 
    21         }
    22     }
    23 }
    24 int main(){
    25     freopen("prime.in","r",stdin);
    26     freopen("prime.out","w",stdout);
    27     scanf("%d%d",&L,&R);
    28     is_prime();
    29     for(int i=L;i<=R;i++){
    30         if(!vis1[i-L]) sum+=i;
    31     }
    32     printf("%lld",sum);
    33     return 0;
    34 }
    AC代码

    Day4 T3:

     链接:https://www.luogu.com.cn/problem/T150024

    Day4 T4:

     链接:https://www.luogu.com.cn/problem/T150031

    首先不考虑边权,那么Alice和Bob都应该选当前可选中的最大的。对于每一个边权,如果一个人把两边的点都选了,他才能得到这个边权,但两个人分别选两端的两个点,那么这条边的边权不属于任何人。而恰好这道题最后只考虑Alice的得分减去Bob的得分。

    所以这道题完全可以将每条边的边权分成两份,分给这条边两端的点。然后让两个人贪心地从大到小选。

    但是这样可能会有小数等的情况。所以可以直接将点权*=2,然后两端的点权+=边权。最后答案直接/=2即可,是等效的。

     AC代码:

     1 #include<cstdio>
     2 #include<iostream>
     3 #include<algorithm>
     4 using namespace std;
     5 typedef long long ll;
     6 const int N=10005;
     7 int n,m;
     8 ll sum1,sum2;
     9 ll w[N];
    10 bool cmp(ll a,ll b){
    11     return a>b;
    12 }
    13 int main(){
    14     freopen("play.in","r",stdin);
    15     freopen("play.out","w",stdout);
    16     scanf("%d%d",&n,&m);
    17     for(int i=1;i<=n;i++) {scanf("%lld",&w[i]); w[i]*=2;}
    18     for(int i=1;i<=m;i++){
    19         int a,b,c;
    20         scanf("%d%d%d",&a,&b,&c);
    21         w[a]+=c; w[b]+=c;
    22     }
    23     sort(w+1,w+n+1,cmp);
    24     for(int i=1;i<=n;i++){
    25         if(i&1) sum1+=w[i];
    26         else sum2+=w[i];
    27     }
    28     printf("%lld
    ",(sum1-sum2)/2);
    29     return 0;
    30 }
    AC代码

    Day5 T1:

    链接:https://www.luogu.com.cn/problem/T150188

    很明显这是一个贪心问题。

    可以有两种(或更多的贪心写法):

    自己在考场上写的是一个类似调整法贪心:

    按它们的左端点排序。假设上一个区间是选了的,用pre记录它的右端点。我们在枚举下一个区间的时候,如果它的pre>a[i].l,也就是当前区间与它重合,那么就在pre这个区间和当前a[i]这个区间两者之间做一个选择:选择一个右端点更小的,它一定更优。并用pre记录下来。然而这些操作都不会对cnt产生影响。如果pre<=a[i].l,那么就先把a[i]这个区间选上,之后再做调整。

     1 #include<cstdio>
     2 #include<iostream>
     3 #include<algorithm> 
     4 using namespace std;
     5 const int N=1e6+5;
     6 int n,vis[N],cnt;
     7 int pre;
     8 int T;
     9 struct node{
    10     int l,r;
    11 }a[N];
    12 bool cmp(node a,node b){
    13     return a.l<b.l;
    14 }
    15 int main(){
    16     freopen("count.in","r",stdin);
    17     freopen("count.out","w",stdout);
    18     scanf("%d",&n);
    19     for(int i=1;i<=n;i++) scanf("%d%d",&a[i].l,&a[i].r);
    20     sort(a+1,a+n+1,cmp);
    21     for(int i=1;i<=n;i++){
    22         if(pre>a[i].l) pre=min(a[i].r,pre);
    23         else {cnt++; pre=a[i].r;}
    24     }
    25     printf("%d",cnt);
    26     return 0;
    27 }
    AC代码

    也可以直接用普通贪心做:

    把它们直接按右端点排序,从左往右扫,如果当前a[i].l>=pre,即能选,则就选。选当前这个一定会比选它的下一个更优:

    因为不选这一个,所省下来的空间最多留给下一个区间,而下一个区间的右端点更靠右。

     1 #include<cstdio>
     2 #include<iostream>
     3 #include<algorithm>
     4 using namespace std;
     5 const int N=1000005;
     6 int n,pre,cnt;
     7 struct node{
     8     int l,r;
     9 }q[N];
    10 bool cmp(node a,node b){
    11     return a.r<b.r;
    12 }
    13 int main(){
    14     scanf("%d",&n);
    15     for(int i=1;i<=n;i++){
    16         scanf("%d%d",&q[i].l,&q[i].r);
    17     }
    18     sort(q+1,q+n+1,cmp);
    19     for(int i=1;i<=n;i++){
    20         if(pre<=q[i].l) {cnt++; pre=q[i].r;}
    21     }
    22     printf("%d",cnt);
    23     return 0;
    24 }
    AC代码

    Day5 T2:

     链接:https://www.luogu.com.cn/problem/T150189

    首先根据贪心的思想,按照p从大到小排序。然后进行DP:

    设dp[i][j]表示从排序后前i头猪中选j头的最大获利。每次转移考虑第i头猪选不选。

    如果选,则dp[i][j]=dp[i-1][j-1]+当前时刻第i头的价值。如果不选,则dp[i][j]=dp[i-1][j],取max。

    注意在DP过程中取max。

     1 #include<cstdio>
     2 #include<iostream>
     3 #include<algorithm>
     4 using namespace std;
     5 const int N=1005;
     6 int n,k,ans;
     7 int f[N][N];
     8 struct node{
     9     int p,a;
    10 }q[N];
    11 bool cmp(node a,node b){
    12     return a.p>b.p;
    13 }
    14 int main(){
    15     scanf("%d%d",&n,&k);
    16     for(int i=1;i<=n;i++) scanf("%d",&q[i].a);
    17     for(int i=1;i<=n;i++) scanf("%d",&q[i].p);
    18     sort(q+1,q+n+1,cmp);
    19     for(int i=1;i<=n;i++){
    20         for(int j=k;j>=1;j--){
    21             f[i][j]=max(f[i][j],max(f[i-1][j],f[i-1][j-1]+q[i].a-q[i].p*(j-1)));
    22             ans=max(f[i][j],ans);
    23         }
    24     }
    25     printf("%d",ans);
    26     return 0;
    27 }
    AC代码

    不难发现dp[i][]都是由dp[i-1][]转移而来的,所以可以压掉第一维。

     1 #include<cstdio>
     2 #include<iostream>
     3 #include<algorithm>
     4 using namespace std;
     5 const int N=1005;
     6 int n,k,ans;
     7 int f[N];
     8 struct node{
     9     int p,a;
    10 }q[N];
    11 bool cmp(node a,node b){
    12     return a.p>b.p;
    13 }
    14 int main(){
    15     scanf("%d%d",&n,&k);
    16     for(int i=1;i<=n;i++) scanf("%d",&q[i].a);
    17     for(int i=1;i<=n;i++) scanf("%d",&q[i].p);
    18     sort(q+1,q+n+1,cmp);
    19     for(int i=1;i<=n;i++){
    20         for(int j=k;j>=1;j--){
    21             f[j]=max(f[j],f[j-1]+q[i].a-q[i].p*(j-1));
    22             ans=max(f[j],ans);
    23         }
    24     }
    25     printf("%d",ans);
    26     return 0;
    27 }
    AC代码AC

    Day5 T3:

     链接:https://www.luogu.com.cn/problem/T150191

    题中有一个比较好的提示,当n=5时,答案为89。而在斐波那契数列中F[10]恰好为89。那么可以大胆猜想答案即为F[2*n]。

    严格:

    首先题目要求的可以写成:$Sigma^n_{i=0}{C_n^i} imes F_i$

    证明可以通过F数列的通项公式和二项式定理可以进行证明:

    F数列通项公式:$F_n = frac {1}{sqrt{5}} imes ((frac{1+sqrt{5}}{2})^{n+1}-(frac{1-sqrt{5}}{2})^{n+1})$

    二项式定理:$(x+y)^n = Sigma_{i=0}^n C^i_nx^iy^{n-i}$

    证明:

    $ Sigma^n_{i=0}{C_n^i} imes F_n$

       =$Sigma^n_{i=0}{C_n^i}*frac {1}{sqrt{5}} imes [(frac{1+sqrt{5}}{2})^{n+1}-(frac{1-sqrt{5}}{2})^{n+1}]$

       =$frac{1}{sqrt{5}}(Sigma^n_{i=0}{C_n^i}[(frac{1+sqrt{5}}{2})^{i+1}-(frac{1-sqrt{5}}{2})^{i+1})]$
       
       =$frac{1}{sqrt{5}}[(frac{1+sqrt{5}}{2})(frac{3+sqrt{5}}{2})^n)-(frac{1-sqrt{5}}{2})(frac{3-sqrt{5}}{2})^n]$
     
       =$frac{1}{sqrt{5}}[(frac{1+sqrt{5}}{2})^{2n+1}-(frac{1-sqrt{5}}{2})^{2n+1}]$

       =$F_{2n}$

    Day5 T4:

    Day6 T1:

  • 相关阅读:
    leetcode5
    leetcode4
    maven笔记
    枚举使用笔记
    List遍历删除解决方案:遍历删除,迭代删除,removeIf
    java笔记(web部分)
    webview使用
    json数据格式+gson解析json问题总结
    android:layout_weight的简单使用
    欢迎界面效果
  • 原文地址:https://www.cnblogs.com/New-ljx/p/13764728.html
Copyright © 2020-2023  润新知