秋令营(未完)

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\n");
18     else printf("No\n");
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}\times p_2^{q_2}\times 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\n",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}\times p_2^{q_2}\times p_3^{q_3}\times ...\times p_n^{q_n}$

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

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

设$s_i=min(q_i,r_i)$,则数量为$(s_1+1)\times (s_2+1)\times ... \times(s_i+1)...\times(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\n",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\n");continue;}
19         if(sum&1) printf("Alice\n");
20         else printf("Bob\n");
21     }
22     return 0;
23 }
AC代码

 

Day3 T4:

 

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

 

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

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

$A_{i+1}=-2\times A_i+A_{i-1}+2\times 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\n",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)\times \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\n",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\n",(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}\times F_i$

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

F数列通项公式:$F_n = \frac {1}{\sqrt{5}} \times ((\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}\times F_n$

   =$\Sigma^n_{i=0}{C_n^i}*\frac {1}{\sqrt{5}} \times [(\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:

 

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

 

Day6 T2:

 

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

 

Day6 T3:

 

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

 

Day6 T4:

 

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

 

 

 

 

posted @ 2020-11-02 19:29  dfydn  阅读(115)  评论(0编辑  收藏  举报