目录
目录
牛客寒假算法基础集训营4
E、Applese 涂颜色
牛客寒假算法基础集训营1
C、小a与星际探索
D、小a与黄金街道
牛客寒假算法基础集训营5
J、迷宫
G、酷炫数字
牛客寒假算法基础集训营6
B、煤气灶
D、美食
G、区间或和
E、海啸
牛客寒假算法基础集训营3
D、处女座的训练
I、处女座的约会
牛客寒假算法基础集训营2
G、处女座与复读机
h、处女座的测验1
theme:求2^n%(10^9+7),其中n可到10^100000;
solution:如果用python3做的话,直接用pow()函数。一般做法:肯定要用快速幂,但n太大还是会超时,且不好存。利用费马小定理转化为求2^(n%(10^9+7-1))%(10^9+7)。即将幂次由n转换为n%(10^9+7-1).
print(pow(2, int(input().split()[0]), 10**9 + 7))
//OK???????
#include
#include
#include
#include
#include
#include
using namespace std;
#define far(i,n) for(int i=0;i=0;--i)
typedef long long ll;
#define j2h(x) (3.1415926*(x)/180.0)
const int mod=1e9+7;
ll quickPow(int n)
{
ll ans=1;
ll a=2;
while(n)
{
if(n&1)ans=(ans*a)%mod;
a=(a*a)%mod;
n/=2;
}
return ans;
}
int main()
{
string n,m;
cin>>n>>m;
ll k=n[0]-'0';
int l=n.length();
//cout<
theme:n个星球。小a驾着飞船要从星球1到n,每个星球都有一个能量数pi,能从星球i到j,当且仅当pi>pj,小a 的飞船有个耐久值t,没到一个星球耐久值变为t=t^pi,求小a到达n号星球的最大耐久值,若不能到达或达到后耐久值为0,则输出-1.1<=n,pi<=3000
solution:背包问题。首先达到下一个星球有限制,所以先从大到小排序(1号与n号必须到达所以不排),先判断能否到达n号。
用dp[j]表示是否可以达到j的耐久值,初始时dp[a[0]*a[n-1]]=1;则从大到小遍历时若dp[j]=1,则dp[j^a[i]]=1(到达该星球)。
最后的结果找到最大的j使得dp[j]=1即可。(考虑时可想用dp[i][j]表示前i个星球是否可以得到j的耐久值,则转移时考虑是否达到第i个星球)
#include
#include
#include
#include
#include
#include
using namespace std;
#define far(i,n) for(int i=0;i=0;--i)
typedef long long ll;
int a[3005];
int dp[4100]={0};
int main()
{
int n;
cin>>n;
far(i,n)
scanf("%d",&a[i]);
sort(a+1,a+n-1);
if(a[0]<=a[n-1])
{
cout<<-1;
return 0;
}
dp[a[0]^a[n-1]]=1;
for(int i=n-2;i>=1;--i){
if(a[i]<=a[n-1])break;
if(a[i]>=a[0])continue;
for(int j=0;j<=4095;++j)
if(dp[j])
dp[j^a[i]]=1;
}
for(int j=4095;j>=1;--j)
{
if(dp[j])
{
cout<
theme: 一条道路n米,左端点为0,右端点为n,a,b两个人速度都为1m/s,a从1出发走到n-1,b从n-1出发走到1,开始时他们各自拥有A与B的黄金数,若某一时刻他们分别走到点x,y,满足gcd(n,x)=1,gcd(n,y)=1,则这是他们的黄金数分别变为A=A*k^x,B=B*k^y,k已知,a走到n-1时结束,求最终A+B。结果mod 10^9+7
solution:首先根据"若gcd(n,x)=1,则gcd(n,n-x)=1"这个定理(反证法证),与a,b速度相同,x+y=n可知a可获得黄金的点是与n互质的点,b也一样,所以先通过欧拉函数求出小于n且与n互质的数的个数h(n),可知这n个数的和为h(n)*n/2,a,b贡献相同可把指数提出,最终答案为(A+B)*k^(h(n)*n/2),再用快速幂算。还可再用优化。
#include
#include
#include
#include
#include
#include
using namespace std;
#define far(i,n) for(int i=0;i=0;--i)
typedef long long ll;
int mod=1e9+7;
ll eular(ll n)
{
ll ans=n;
for(int i=2;i*i<=n;++i)
{
if(n%i==0)
{
ans-=ans/i;
while(n%i==0)
n/=i;
}
}
if(n>1)
ans-=ans/n;
return ans;
}
ll qpow(ll a,ll b)
{
ll ans=1;
while(b)
{
if(b%2)ans=ans*a%mod;
a=a*a%mod;
b>>=1LL;
}
return ans;
}
int main()
{
int n;
ll k,a,b;
cin>>n>>k>>a>>b;
a%=mod,b%=mod,k%=mod;
ll e=eular(n)*n/2;
e%=mod-1; //也可不加
ll ans=(a+b)%mod*qpow(k,e)%mod;
cout<
theme:给定n*m的迷宫,其中*表示障碍物,.表示可走的,给定初始位置r,c(保证不是障碍物),问迷宫中共有多少位置是可达的,且最多只能往左走x步,往右走y步。
solution:bfs可以求得那些位置是可达的,再加两个变量fl,fr记录到达这里时已经往左往右走了多少步,作为限制条件
#include
#include
#include
#include
#include
#include
using namespace std;
#define far(i,n) for(int i=0;i=0;--i)
typedef long long ll;
int n,m,r,c;
ll x,y;
char a[1005][1005];
int X[4]={0,0,-1,1};
int Y[4]={-1,1,0,0};//l,r,u,d
struct _a
{
int i,j,fl,fr;
_a(int a,int b,int c,int d)
{
i=a;
j=b;
fl=c;
fr=d;
}
};
int bfs()
{
queue<_a>q;
q.push(_a(r,c,0,0));
int ans=0;
a[r][c]='*';
while(!q.empty())
{
int i=q.front().i,j=q.front().j,fl=q.front().fl,fr=q.front().fr;
//cout<=n||dy>=m||a[dx][dy]=='*')
continue;
if(k==0)
++nl;
else if(k==1)
++nr;
a[dx][dy]='*';
if(nl<=x&&nr<=y)
q.push(_a(dx,dy,nl,nr));
}
//cout<>n>>m>>r>>c>>x>>y;
--r,--c;
far(i,n)
{
getchar();
scanf("%s",a[i]);
}
int ans=bfs();
cout<
theme:T组询问,每次询问给定n,求因子个数为n的最小数。1<=T,n<=1000000
solution:t很大,为不超时,最好先算出n为1~1000000的答案,这样T次询问直接输出即可。
先计算1~1000000之间数的因子数:类似线性筛法,每个数一定是它的倍数的因子,所以用a[i]表示i的因子个数,从1~1000000循环j,则a[j的倍数]++,而题目是已知因子个数求i,所以我们用另一个数组b[i]表示将a[i]的值与索引颠倒即为结果,而b[i]必然<=i,所以a[i]中没有的值即在i>1000000里,输出-1.
#include
#include
#include
#include
#include
#include
using namespace std;
#define far(i,n) for(int i=0;i=0;--i)
typedef long long ll;
int a[1000010],b[1000010];
int main()
{
int t;
cin>>t;
memset(a,0,sizeof(a));
memset(b,-1,sizeof(b));
for(int i=1;i<=1000000;++i)
for(int j=i;j<=1000000;j+=i)
a[j]++;
// for(int j=1;j<=20;j++)
// cout<i)
b[a[i]]=i;
while(t--)
{
int n;
scanf("%d",&n);
printf("%d\n",b[n]);
}
}
theme:第一天钱数为n,之后每一天都会获得比前一天多d的钱。已知煤气灶要m元,问最少哪天可以买了?(给定x,结果<=x<=10^9)
solution:某一天的钱总数即是等差数列求和:s=a0*x+x*(x-1)*d/2,解法即循环求s直到>=m,但由于数据大的话可能到x到10^9超时,所以用二分法找到第一个>=m的(即lower_bound()但直接用函数得先定义数组计算,耗时耗空间,所以手写该函数)
#include
#include
#include
#include
#include
#include
using namespace std;
#define far(i,n) for(int i=0;i=0;--i)
typedef long long ll;
int main()
{
double n,m,d,midm;
ll x,i=1,mid;
cin>>n>>m>>d>>x;
while(i=m)
x=mid;
else
i=mid+1;
}
cout<
theme:n中美食按顺序摆成一排,第i中美食可吃a[i]口,一次要吃两口,这两口要么是同一种美食,要么是相邻的,问最多能吃几次?
solution:贪心。从左往右遍历,如果前一种美食还有剩的(该策略下为1),则优先与前一种搭配,然后自我搭配为一次直至剩1或0
#include
#include
#include
#include
#include
#include
using namespace std;
#define far(i,n) for(int i=0;i=0;--i)
typedef long long ll;
ll a[100010]={0};
int main()
{
int n;
cin>>n;
far(i,n)
scanf("%lld",&a[i]);
ll ans=a[0]/2;
a[0]%=2;
for(int i=1;i
theme:给定a、b求 a|(a+1)|(a+2)|...|(b-1)|b。0≤a,b≤10^18,a≤b
solution:位运算当然转化为二进制考虑每一位。从低位开始,每一位上都是每过2^i(从0开始)变为0或1
#include
#include
#include
#include
#include
#include
using namespace std;
#define far(i,n) for(int i=0;i=0;--i)
typedef long long ll;
ll len[100];
int ans[100]={0};
int main()
{
ll x=2;
far(i,60)
{
len[i]=x;
x*=2;
}
ll a,b;
while(~scanf("%lld%lld",&a,&b))
{
ll l=b-a+1;
far(i,60)
{
if(l>(len[i]/2))
{
ans[i]=1;
continue;
}
if(a%len[i]>=0&&b%len[i]<=len[i]/2-1&&a%len[i]<=b%len[i])
ans[i]=0;
else
ans[i]=1;
}
ll res=0;
for(int i=59;i>=0;--i)
res=res*2+ans[i];
printf("%lld\n",res);
}
}
theme:给定n*m矩阵表示n*m座楼的高度,给定水深d,高度低于d的楼会被淹没。有q次询问,每次询问给定a,b,x,y询问以第a行第b座楼和第x行第y座楼为对角的矩形区域内,不会被淹没的楼数。
solution:即是计算出矩形区域内高度>=d的楼个数。由于询问有多组,所以采用动态规划一次性算出即可在询问时避免重复计算了。递归式:ans[i][j]=ans[i-1][j]+ans[i][j-1]-ans[i-1][j-1]。ans[i][j]表示以[0][0]和[i][j]为对角的矩形区域。
#include
#include
#include
#include
#include
#include
using namespace std;
#define far(i,n) for(int i=0;i=0;--i)
typedef long long ll;
int main()
{
int n,m;
ll d;
cin>>n>>m>>d;
ll h[n+5][m+5];
int live[n+5][m+5];
ll ans[n+5][m+5];
far(i,n)
far(j,m)
scanf("%lld",&h[i][j]);
far(i,n)
far(j,m)
if(h[i][j]>=d)
live[i][j]=1;
else
live[i][j]=0;
//dp求以[0][0]和[i][j]为对角的矩形区域内不被淹没的楼个数
far(i,n)
far(j,m)
{
ans[i][j]=0;
if(j-1>=0)
ans[i][j]+=ans[i][j-1];
if(i-1>=0)
ans[i][j]+=ans[i-1][j];
if(j-1>=0&&i-1>=0)
ans[i][j]-=ans[i-1][j-1];
ans[i][j]+=live[i][j];
}
// far(i,n){
// far(j,m)
// cout<>q;
far(i,q)
{
int a,b,x,y;
scanf("%d%d%d%d",&a,&b,&x,&y);
int res=ans[x-1][y-1];
if(b-2>=0)
res-=ans[x-1][b-2];
if(a-2>=0)
res-=ans[a-2][y-1];
if(a-2>=0&&b-2>=0)
res+=ans[a-2][b-2];
printf("%d\n",res);
}
}
theme:要做n个题目,每个题目有一个刷掉本题要花费的时间ai和本题每分钟会带来的疲倦值bi。在做题时,未做的题目每分钟会产生疲惫值,正在做的这题不会产生疲惫值,问昨晚这些题目最少会产生多少疲惫值
solution:选定刷题顺序后,总疲惫值计算方法为遍历除最后一题的所有题,总疲惫+=当前做题的解题时间*剩下所有未解题带来的疲惫值之和。由这个式子可知ai小的、bi大的题应先解,这样总乘积和就会小,由贪心知bi/ai小的题先解
注:由浮点数除法不准确性,比较时转化为乘: return b*c.a>a*c.b;
//ok
#include
#include
#include
#include
#include
#include
using namespace std;
#define far(i,n) for(int i=0;i=0;--i)
typedef long long ll;
struct _a
{
int a,b;
bool operator<(_a c)const
{
// float x=1.0*b/a,y=1.0*c.b/c.a;
return b*c.a>a*c.b;
}
}a[100010];
int main()
{
int n;
cin>>n;
ll sum=0;
far(i,n){
scanf("%d%d",&a[i].a,&a[i].b);
sum+=a[i].b;
}
sort(a,a+n);
// far(i,n)
// cout<
theme:一条龙有n个头,用01序列表示,1表示邪恶的头,0表示善良的头,你每次可以砍掉最右边的头而在最左边补一个头。如果你砍的是1,则你可以自主选择补0还是1;而如果你砍掉的是0,则由该龙选择补0还是1,龙总会跟你作对。
solution:先注意题目有一点误导性,101时也是可最终变为000的:砍掉最右边1,左边补1,变为110,再砍掉0后无论是011还是111都必能变为000。明白了这个就可以看出,处女座可通过每次砍了1后补0来把左边逐渐都变成1,使得达到无0、1相间的局面,即到达111000或00111这样的。所以必赢。
#include
#include
#include
#include
#include
#include
using namespace std;
#define far(i,n) for(int i=0;i=0;--i)
typedef long long ll;
int main()
{
int t;
cin>>t;
while(t--)
{
printf("cnznb\n");
}
}
G、处女座和小姐姐3
theme:给定l,r表示整数范围[l,r],0<=l<=r<=10^18,问[l,r]间的整数中,数字中含有6的整数有几个
solution:考虑计算[0,r]与[0,l)之间的再相减。思路是按个位是6、十位是6等依次分开考虑。对于个位是6,考虑r/10,因为每10个数出现一次个位为6,所以为r/10个,再判断r%10与6的大小判断还有没有一个;而十位是6则除以100,每100有9个(6~:~为1到9除6以外);依次类推,直到除后为0。
A、处女座的签到题
theme:给定n个点的坐标,求由其中点组成的三角形中面积第k大的面积。
solution:遍历得到三个点,由二维叉积(向量积)算三角形的面积。由于求第k大,而不满足构成三角形条件的两个向量的叉积为0,不影响结构,所以可以不用单独判断能否构成三角形。最后求第k大时,可排序,也可用nth_element(a,a+k-1,a+n),会将第k小的数放在它从小到大排应该放的位置(其它数不一定)。注意这题是求“大”,所以转换一下为n-k.
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define far(i,n) for(int i=0;i=0;--i)
typedef long long ll;
ll X[105];
ll Y[105];
ll ans[1000005];
int main()
{
int t;
cin>>t;
while(t--)
{
int n,k;
scanf("%d%d",&n,&k);
far(i,n)
scanf("%lld%lld",&X[i],&Y[i]);
int cnt=0;
far(i,n-2)
for(int j=i+1;j
theme:给定一个字符串s,问经过以下操作两次后能否得到字符串t。(全为小写字母)每次可以:
(1)将其中一个字母改为另一个字母。(2)插入一个字母。(3)删除一个字母
solution:DP:用dp[i][j]表示操作从s[1..i]到t[1...j]至少需要的操作数,则若s[i]==t[j],则dp[i][j]=dp[i-1][j-1],否则为min(dp[i-1][j]+1,dp[i][j-1]+1,dp[i-1][j-1]+1)
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define far(i,n) for(int i=0;i=0;--i)
typedef long long ll;
int dp[110][110];
int main()
{
char s[200],t[200];
scanf("%s%s",s,t);
int l1=strlen(s),l2=strlen(t);
far(i,l1)dp[i][0]=i;
far(i,l2)dp[0][i]=i;
for(int i=1;i<=l1;++i)
for(int j=1;j<=l2;++j)
{
if(s[i]==t[j])
dp[i][j]=dp[i-1][j-1];
else
dp[i][j]=min(dp[i-1][j-1]+1,min(dp[i-1][j]+1,dp[i][j-1]+1));
}
if(dp[l1][l2]<=2)
cout<<"YES\n";
else
cout<<"NO\n";
}
theme:输出2000个数(1-4*10^8之间),要求:1、这2000个数两两互质;2、任意两数的乘积的因数>10
solution:既然两两互质,则它们乘积的因子即为这两个数的因子互乘,所以要求任意一个数的因数>=4,乘积的因数就>=16了,谈到互质应该联想到质数,质数只有2个因子,所以两个质数的乘积的因数为4正好满足,所以找出前4000个质数两个想乘凑2000个,考虑到范围,最好首尾相乘
//ok
#include
#include
#include
#include
#include
#include
using namespace std;
#define far(i,n) for(int i=0;i=0;--i)
typedef long long ll;
int p[5000];
int pcnt=0;
bool Iscomp[40000]={false};
//线性筛法
void sieve2()
{
for(int i=2;i<40000;i++)
{
if(!Iscomp[i])p[pcnt++]=i;
for(int j=0;j