10.8离线赛
来源:互联网 发布:电力人工智能与机器人 编辑:程序博客网 时间:2024/06/05 08:08
一、弹钢琴
数据: 对于70%,N∈[1,1000]
对于100%,N∈[1,1e9],K∈[1,50]
排列组合的一道裸题。两种实现排列组合的方法:
1、变为递推式计算。因为对于一个组合(aCb)=((a-1) C (b))+((a-1) C (b-1))。然后按照双重循环过去就好了。
#include<bits/stdc++.h>#define Mod 1000000007using namespace std;int A[100005],dp[100005][55];int main(){ int n,k; scanf("%d%d",&n,&k); for(int i=1;i<=n;i++)scanf("%d",&A[i]); sort(A+1,A+1+n); for(int i=0;i<=n;i++)dp[i][0]=1; for(int i=1;i<=n;i++) for(int j=1;j<=k;j++) dp[i][j]=(dp[i-1][j]+dp[i-1][j-1])%Mod; //处理出索要用到的所有C。用dp实现 long long ans=0; for(int i=k;i<=n;i++) ans=(ans+(1LL)*dp[i-1][k-1]*A[i])%Mod; //直接加,但是要边加边模 printf("%lld\n",ans); return 0;}
2、用乘法逆元。乘法逆元大概就是a/b%p,其中p是质数,就可以把式子写成a*(b^(p-2)) mod p。然后就可以一边循环过去吧C也算出来。对于b^(p-2),用快速幂就行了
#include<bits/stdc++.h>#define Mod 1000000007#define ll long long#define M 100005using namespace std;ll A[M],C[M];ll f(ll x){//快速幂 ll res=1; int n=Mod-2; while(n){ if(n&1)res=res*x%Mod; x=x*x%Mod; n>>=1; } return res;}int main(){ int n,k; scanf("%d%d",&n,&k); for(int i=1;i<=n;i++)scanf("%lld",&A[i]); sort(A+1,A+1+n); C[k-1]=1; for(int i=k;i<=n;i++)C[i]=C[i-1]%Mod*i%Mod*f((1LL)*(i-k+1))%Mod;//乘法逆元 ll ans=0; for(int i=k;i<=n;i++)ans=(ans+C[i-1]*A[i])%Mod; printf("%lld\n",ans); return 0;}
二、删除数字
数据:对于30%,A∈[1,100],N∈[1,10]
对于70%,A∈[1,1e5]
对于100%,A∈[2,1e12],N∈[1,100]
这种题一看就是dp。但是考试时前面时间浪费太多了,只留了五分钟打了个记忆化搜索,还运行错误了。
删除当做1,不删当做0,那么一个长度为12的数的状态只有2^12=4096种,这样就够了。每次把上一个数在某种状态下数字比自己小的dp值转过来就行。
#include<bits/stdc++.h>#define ll long long#define Mod 1000000007using namespace std;ll A[105],cnt[4105];struct node{ ll a,sum; bool operator < (const node &x)const{ return a<x.a; }}dp[105][4105];ll f(int x,int y){//把A[x]中删除掉几个数 bool Q[15]={0}; int num[15]={0}; int a=1,i=1; while(i<=y){//哪几个数位要删标为1 if(y&i)Q[a]=1; a++;i<<=1; } ll c=A[x];int k=0; while(c)num[++k]=c%10,c/=10;//分解 ll b=0; for(int i=k;i>=1;i--)//合并 if(Q[i])b=b*10+num[i]; return b;}//这样写有点麻烦,可以再简单点int main(){ ll a;int n,len=0; scanf("%lld%d",&a,&n); for(int i=0;i<=n;i++)A[i]=a+i; while(a)len++,a/=10; for(int i=1;i<(1<<len);i++){//0位置要先处理出来 dp[0][i].sum=1; dp[0][i].a=f(0,i); } for(int i=1;i<=n;i++){ sort(dp[i-1]+1,dp[i-1]+(1<<len));//写法比较神奇 for(int j=1;j<(1<<len);j++) cnt[j]=(cnt[j-1]+dp[i-1][j].sum)%Mod; //前缀和方便计算 for(int j=1;j<(1<<len);j++){ ll x=f(i,j); int y=upper_bound(dp[i-1],dp[i-1]+(1<<len),(node){x,0})-dp[i-1]-1;//找一个符合的数 dp[i][j].a=x; dp[i][j].sum=cnt[y];//然后把数都加过来 } } ll ans=0; for(int i=0;i<(1<<len);i++) ans=(ans+dp[n][i].sum)%Mod; printf("%lld\n",ans); return 0;}
除了用二分查找,也可以用归并。下降方案数弄出来,然后直接排序,用归并找就行了,具体看admin
三、四点旅行
数据:对于60%,N∈[10,200]
对于100%,N∈[10,2000]
这道题可以分成两个问题:
1、快速求出任意两点之间的最短路径
2、快速求出四个点的路径值
对于1,不能看到是图就只用图的算法。因为是有向图并且边权为1,完全可以用BFS,这样就只有N*M。而要是用其他的,像Dijkstra就算加上优先级队列优化也要N^2 * log M
对于2,四个点中ad两个点是独立的,只与bc两个点有关系,那就之枚举bc,就只有N^2。注意要事先与处理出来和b相连的最长边和c的最长边。每个都要三条,因为要避免abcd四个点重复出现
#include<bits/stdc++.h>#define FOR(i,a,b) for(int i=(a);i<=(b);i++)using namespace std;vector<int>A[2][2005];int dis[2][2005][2005],n,m;bool mark[2005];struct node{int id,v;};queue<node>Q;void f(int p,int st){//BFS求最短路,p是因为有向图,b->a要反着弄,其实可以不用,但这样清晰一点 memset(mark,0,sizeof mark); mark[st]=1; Q.push((node){st,0}); while(!Q.empty()){ node x=Q.front();Q.pop(); dis[p][st][x.id]=x.v; FOR(i,0,A[p][x.id].size()-1) if(!mark[A[p][x.id][i]]){ Q.push((node){A[p][x.id][i],x.v+1}); mark[A[p][x.id][i]]=1; } }}struct node1{int id[4];}G[2][2005];int main(){ scanf("%d%d",&n,&m); FOR(i,1,m){ int x,y; scanf("%d%d",&x,&y); A[0][x].push_back(y); A[1][y].push_back(x); } FOR(i,1,n)f(0,i),f(1,i);//最短路 FOR(p,0,1)FOR(i,1,n)FOR(j,1,n)//预处理每个点下的最长的三条路径 if(dis[p][i][j]!=0){ FOR(k,1,3) if(dis[p][i][j]>dis[p][i][G[p][i].id[k]]){ for(int l=3;l>k;l--)//只用三条,较短的往后移 G[p][i].id[l]=G[p][i].id[l-1]; G[p][i].id[k]=j; break; } } int ans=0; FOR(b,1,n)FOR(c,1,n)//枚举bc两个点 if(dis[0][b][c]!=0){//这两个点存在路径 FOR(i,1,3)if(G[1][b].id[i]!=b&&G[1][b].id[i]!=c){//找一个a点,a不与c重合 FOR(j,1,3) if(G[0][c].id[j]!=b&&G[0][c].id[j]!=G[1][b].id[i]){找一个d点,d不与ab重合 ans=max(ans,dis[1][b][G[1][b].id[i]]+dis[0][c][G[0][c].id[j]]+dis[0][b][c]);//更新 } } } printf("%d\n",ans); return 0;}
这一次时间把握的不好,第一道题写了这么久不仅没对,还没留下时间写第二题;还有排列组合把握的不扎实,要再研究一下。
- 10.8离线赛
- 10.26离线赛题解
- 天池离线赛
- 天池离线赛
- 天池离线赛
- 天池离线赛
- 10.3离线赛
- 10.4离线赛
- 离线赛20171004总结
- 10.6离线赛
- 离线赛20171006总结
- 20171006离线赛总结
- 20171007离线赛总结
- 离线赛20171007总结
- 10.7离线赛
- 离线赛20171008总结
- 离线赛20171008总结
- 离线赛总结
- 定时函数
- ElasticSearch
- ThinkPHP3.2完整版解析
- stringstream的基本用法
- linux驱动学习记录(三)-PCI IO读写、中断、DMA传输
- 10.8离线赛
- mysql 面试题 查出每门课程成绩都不低于80份的同学的名字
- 1056. 组合数的和(15)
- vba excel学习系列(三)---事件过程
- 10.8 巧克力 2405
- ROC和AUC介绍以及如何计算AUC
- python线程的使用
- FtpUtil
- 模拟 blue