BC div2补题以及 复习模除 逆元__BestCoder Round #78 (div.2)
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BC div2补题以及 复习模除 逆元__BestCoder Round #78 (div.2)相关的知识,希望对你有一定的参考价值。
第一题没话说 智商欠费 加老柴辅导终于过了
需要在意的是数据范围为2的63次方-1 三个数相加肯定爆了
四边形的定义 任意边小于其余三边之和
换句话说就是 最长边小于其余三边之和
这样的话问题转化为 最长边依次减其余三边的结果是否小于等于0
还有一点是题目出现0边 即最小边不为0 想得太多反而把0也算为合法。。。。
问题只需要 sort一下 判断a[0]==0||a[3]-a[2]-a[1]-a[0]<=0 输出NO
第二题 好多种姿势 题目链接http://bestcoder.hdu.edu.cn/contests/contest_chineseproblem.php?cid=683&pid=1002
官方题解
我们令dp[i][j]表示在前i个数中,选出若干个数使得它们的gcd为j的方案数,于是只需要枚举第i+1个数是否被选中来转移就可以了
令第i+1个数为v,当考虑dp[i][j]的时候,我们令$dp[i+1][j] += dp[i]j,dp[i+1][gcd(j,v)] += dp[i]j
复杂度O(N*MaxV) MaxV 为出现过的数的最大值
其实有O(MaxV *log(MaxV))的做法,我们考虑记f[i]表示从这些数中选择若干个数,使得他们的gcd是i的倍数的方案数。假如有K个数是i的倍数,则 f[i]=2^K-1,再用g[i]表示从这些数中选择若干个数,使得他们的gcd是i的方案数,则g[i]=f[i] - g[j] (对于所有j是i的倍数)。
由调和级数可以得到复杂度为O(MaxV *log(MaxV))
DP之二维数组转移
我们把dp[i][j]作为考虑了第i个数GCD为j的方案数
直接gcd会超时 所以我们打个表GCD
那么dp[i][j]+=dp[i-1][j]; dp[i][GCD[j][v[i]]]+=dp[i-1][j]; 然后就可以转移辣;
#include<cstdio> #include<map> //#include<bits/stdc++.h> #include<vector> #include<stack> #include<iostream> #include<algorithm> #include<cstring> #include<cmath> #include<set> #include<queue> #include<cstdlib> #include<climits> #define PI acos(-1.0) #define INF 0x3fffffff using namespace std; typedef long long ll; typedef __int64 int64; const ll mood=1e9+7; const int64 Mod=100000007; const double eps=1e-9; const int N=1005; const int MAXN=250050; typedef int rl; inline void r(rl&num){ num=0;rl f=1;char ch=getchar(); while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1;ch=getchar();} while(ch>=‘0‘&&ch<=‘9‘)num=num*10+ch-‘0‘,ch=getchar(); num*=f; } int gcd(int a,int b) { return b==0?a:gcd(b,a%b); } int v[N]; int GCD[N][N]; int64 dp[N][N]; int main() { for(int i=1;i<1001;i++) { for(int j=1;j<=i;j++) { GCD[i][j]=GCD[j][i]=gcd(i,j); } } int ci; r(ci); while(ci--) { int n; r(n); int mx=-1; for(int i=1;i<=n;i++) { r(v[i]); mx=max(mx,v[i]); dp[i][v[i]]=1; } for(int i=2;i<=n;i++) { for(int j=1;j<=mx;j++) { dp[i][j]+=dp[i-1][j]; dp[i][j]%=Mod; dp[i][GCD[j][v[i]]]+=dp[i-1][j]; dp[i][GCD[j][v[i]]]%=Mod; } } int64 ans=0; for(int i=1;i<=mx;i++) { ans+=(dp[n][i]*i)%Mod; ans%=Mod; } memset(dp,0,sizeof(dp)); memset(v,0,sizeof(v)); printf("%I64d\n",ans); } return 0; }
仔细想了一下 觉得可以优化为滚动数组 试了好久不对 最后瞎蒙
每个数都多考虑了一次 所以/2需要乘逆元 正好1e8+7是素数
Mod为素数,那么还可以根据费马小定理得到逆元为 2的(Mod-2)次方%Mod
即除2等于乘2的(Mod-2)次方%Mod
所以加了一个快速幂 但是优化为滚动数组后 时间增加了一丢丢 但空间大幅度减少
16757862 | 2016-04-03 12:45:34 | Accepted | 5656 | 2511MS | 5504K | 1925 B | G++ | zxMrlc |
16755798 | 2016-04-03 00:36:10 | Accepted | 5656 | 2449MS | 13404K | 1722 B | G++ | zxMrlc |
但是姿势老感觉有问题 等wtw学长指点后我再改改 还有官方的第二个姿势还没有学会。。。衰
#include<cstdio> #include<map> //#include<bits/stdc++.h> #include<vector> #include<stack> #include<iostream> #include<algorithm> #include<cstring> #include<cmath> #include<set> #include<queue> #include<cstdlib> #include<climits> #define PI acos(-1.0) #define INF 0x3fffffff using namespace std; typedef long long ll; typedef __int64 int64; const ll mood=1e9+7; const int64 Mod=100000007; const double eps=1e-9; const int N=1005; const int MAXN=250050; typedef int rl; inline void r(rl&num){ num=0;rl f=1;char ch=getchar(); while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1;ch=getchar();} while(ch>=‘0‘&&ch<=‘9‘)num=num*10+ch-‘0‘,ch=getchar(); num*=f; } int gcd(int a,int b) { return b==0?a:gcd(b,a%b); } int v[N]; int GCD[N][N]; int64 dp[N]; int main() { int64 xx=Mod-2; int64 an=1,t=2; while(xx>0) { if(xx&1) an*=t; xx/=2; an%=Mod; t*=t; t%=Mod; } for(int i=1;i<1001;i++) { for(int j=1;j<=i;j++) { GCD[i][j]=GCD[j][i]=gcd(i,j); } } int ci; r(ci); while(ci--) { int n; r(n); int mx=-1; for(int i=1;i<=n;i++) { r(v[i]); mx=max(mx,v[i]); } for(int i=1;i<=n;i++) { dp[v[i]]++; for(int j=1;j<=mx;j++) { // dp[i][j]+=dp[i-1][j]; dp[j]%=Mod; dp[GCD[j][v[i]]]+=dp[j]; dp[GCD[j][v[i]]]%=Mod; } } int64 ans=0; //for(int i=1;i<=mx;i++) cout<<dp[i]<<endl; for(int i=1;i<=mx;i++) { ans+=(dp[i]*i)%Mod; ans%=Mod; } memset(dp,0,sizeof(dp)); memset(v,0,sizeof(v)); printf("%I64d\n",ans*an%Mod); } return 0; }
我们每次加入的数据会导致翻倍 所以刚才改为加完/2;
因为添加的v[i]导致的影响就是 当前位置dp[v[i]]多1 即方案数多了选自己的 所以在循环结尾-1就ok了 。。。根本不需要模除 但时间特么变大了
还是有点模糊的 不太清楚到底怎么回事。
16758163 | 2016-04-03 13:17:49 | Accepted | 5656 | 2636MS | 5504K | 1730 B | G++ | zxMrlc |
#include<cstdio> #include<map> //#include<bits/stdc++.h> #include<vector> #include<stack> #include<iostream> #include<algorithm> #include<cstring> #include<cmath> #include<set> #include<queue> #include<cstdlib> #include<climits> #define PI acos(-1.0) #define INF 0x3fffffff using namespace std; typedef long long ll; typedef __int64 int64; const ll mood=1e9+7; const int64 Mod=100000007; const double eps=1e-9; const int N=1005; const int MAXN=250050; typedef int rl; inline void r(rl&num){ num=0;rl f=1;char ch=getchar(); while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1;ch=getchar();} while(ch>=‘0‘&&ch<=‘9‘)num=num*10+ch-‘0‘,ch=getchar(); num*=f; } int gcd(int a,int b) { return b==0?a:gcd(b,a%b); } int v[N]; int GCD[N][N]; int64 dp[N]; int main() { for(int i=1;i<1001;i++) { for(int j=1;j<=i;j++) { GCD[i][j]=GCD[j][i]=gcd(i,j); } } int ci; r(ci); while(ci--) { int n; r(n); int mx=-1; for(int i=1;i<=n;i++) { r(v[i]); mx=max(mx,v[i]); } for(int i=1;i<=n;i++) { dp[v[i]]++; for(int j=1;j<=mx;j++) { // dp[i][j]+=dp[i-1][j]; dp[j]%=Mod; dp[GCD[j][v[i]]]+=dp[j]; dp[GCD[j][v[i]]]%=Mod; } dp[v[i]]--; } int64 ans=0; for(int i=1;i<=mx;i++) { ans+=(dp[i]*i)%Mod; ans%=Mod; } memset(dp,0,sizeof(dp)); memset(v,0,sizeof(v)); printf("%I64d\n",ans%Mod); } return 0; }
以上是关于BC div2补题以及 复习模除 逆元__BestCoder Round #78 (div.2)的主要内容,如果未能解决你的问题,请参考以下文章
codeforces round 418 div2 补题 CF 814 A-E
codeforces round 422 div2 补题 CF 822 A-F