这次考的还不错哈
可惜第二题输入的数据忘开longlong了。。。只有80分
弹钢琴 1794
这题的情景描述还是比较坑的
其实讲的就是从n个数中取k个,将所有情况里的最大值加起来
只要排序一下(从小到大)
就可以发现每个数只要是和它前面的组合,这样它就是组合的最大值
用前缀和维护一下1~k的组合数量就可以了
不过这题用逆元结合下费马小定理也能直接算出来
删除数字 2988
又是一道状压DP
我们只需要计录一下当前行的数的状态
就可以转移了,当然,别忘了开longlong。。。
如果直接枚举上一层状态,再加上枚举当前状态,时间复杂度显然超了
其实可以发现在寻找上一层的状态的时候
只有那些小于或者等于当前状态的数是合法的
那么就可以将每一层可以生成的数都sort一遍
转移状态的时候二分查找一下就能在log的时间里找到
当然,这么做要将dp数组维护一下前缀和
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define FOR(i,a,b) for(int i=(a),i##_end_=(b);i<=i##_end_;++i)
#define DOR(i,a,b) for(int i=(a),i##_end_=(b);i>=i##_end_;--i)
#define INF 0x3f3f3f3f
#define LL long long
#define N 105
#define P 1000000007
void chkmx(int &a,int b){if(a<b)a=b;}
void chkmi(int &a,int b){if(a>b)a=b;}
int MAX(int a,int b){return a>b?a:b;}
int MIN(int a,int b){return a<b?a:b;}
int Num[N][1<<13],l[N];
LL ans,dp[N][1<<13],A[N],Sum[N][1<<13];
int n;
void Init(){
FOR(i,0,n){
LL t=A[i];
while(t) Num[i][l[i]++]=t%10,t/=10;
FOR(j,1,(1<<l[i])-1){
LL &sum=Sum[i][j],h=1;
FOR(k,0,l[i]-1) if(j&(1<<k)) sum+=Num[i][k]*h,h*=10;
}
}
FOR(i,0,n) sort(Sum[i]+1,Sum[i]+(1<<l[i]));
}
LL Find(int i,LL sum){
int L=0,R=(1<<l[i])-1,mid,tmp=0;
while(L<=R){
mid=L+R>>1;
if(Sum[i][mid]<=sum) tmp=mid,L=mid+1;
else R=mid-1;
}
return dp[i][tmp];
}
int main(){
// freopen("num.in","r",stdin);
// freopen("num.out","w",stdout);
cin>>A[0]>>n;
FOR(i,1,n) A[i]=A[i-1]+1;
Init();
FOR(i,1,(1<<l[0])-1) dp[0][i]=1+dp[0][i-1];
FOR(i,1,n) FOR(j,1,(1<<l[i])-1){
dp[i][j]=Find(i-1,Sum[i][j]);
dp[i][j]=(dp[i][j]+dp[i][j-1])%P;
}
cout<<dp[n][(1<<l[n])-1]<<endl;
return 0;
}
其实还有一种方法效率更高
排完序后,可以利用类似归并排序的方法直接转移
这就能实现在线性时间内完成一行的状态转移
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define FOR(i,a,b) for(int i=(a),i##_end_=(b);i<=i##_end_;++i)
#define DOR(i,a,b) for(int i=(a),i##_end_=(b);i>=i##_end_;--i)
#define INF 0x3f3f3f3f
#define LL long long
#define N 105
#define P 1000000007
void chkmx(int &a,int b){if(a<b)a=b;}
void chkmi(int &a,int b){if(a>b)a=b;}
int MAX(int a,int b){return a>b?a:b;}
int MIN(int a,int b){return a<b?a:b;}
int Num[N][1<<13],l[N];
LL ans,dp[N][1<<13],A[N],Sum[N][1<<13];
int n;
void Init(){
FOR(i,0,n){
LL t=A[i];
while(t) Num[i][l[i]++]=t%10,t/=10;
FOR(j,1,(1<<l[i])-1){
LL &sum=Sum[i][j],h=1;
FOR(k,0,l[i]-1) if(j&(1<<k)) sum+=Num[i][k]*h,h*=10;
}
}
FOR(i,0,n) sort(Sum[i]+1,Sum[i]+(1<<l[i]));
}
void Add(LL &a,LL b){
a=(a+b)%P;
}
void Merge(int i){
int s=1;
FOR(j,1,(1<<l[i])-1){
dp[i][j]=dp[i][j-1];
while(s<(1<<l[i-1])&&Sum[i][j]>=Sum[i-1][s]) Add(dp[i][j],dp[i-1][s++]);
}
}
int main(){
// freopen("num.in","r",stdin);
// freopen("num.out","w",stdout);
cin>>A[0]>>n;
FOR(i,1,n) A[i]=A[i-1]+1;
Init();
FOR(i,1,(1<<l[0])-1) dp[0][i]=1;
FOR(i,1,n) Merge(i);
FOR(i,1,(1<<l[n])-1) Add(ans,dp[n][i]);
cout<<ans<<endl;
return 0;
}
本题也可以滚动数组,但显然没必要
四点旅行 2989
这题其实不怎么难
按照题意模拟下来我们可以发现复杂度都堆在找最短路上
其实可以发现这道题的边权都是1
那么,直接bfs就好了
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
#define FOR(i,a,b) for(int i=(a),i##_end_=(b);i<=i##_end_;++i)
#define DOR(i,a,b) for(int i=(a),i##_end_=(b);i>=i##_end_;--i)
#define INF 0x3f3f3f3f
#define LL long long
#define M 2005
#define N 5005
#define P 1000000007
void chkmx(int &a,int b){if(a<b)a=b;}
void chkmi(int &a,int b){if(a>b)a=b;}
int MAX(int a,int b){return a>b?a:b;}
int MIN(int a,int b){return a<b?a:b;}
int d[M][M];
struct node{int maxl[2],s[2],cnt[2];}A[M];
vector<int>es[N];
struct W {int x,v;};
int n,m,ans;
queue<W> Q;
void bfs(int s){
Q.push((W){s,0});
while(!Q.empty()){
W q=Q.front();Q.pop();
int x=q.x;
if(d[s][x]!=INF) continue;
d[s][x]=q.v;
FOR(i,0,es[x].size()-1){
int y=es[x][i];
if(d[s][y]!=INF) continue;
Q.push((W){y,q.v+1});
}
}
}
int main(){
// freopen("four.in","r",stdin);
// freopen("four.out","w",stdout);
memset(d,INF,sizeof(d));
cin>>n>>m;
FOR(i,1,m){
int a,b;
scanf("%d%d",&a,&b);
es[a].push_back(b);
}
FOR(i,1,n) bfs(i);
FOR(i,1,n)FOR(j,1,n){
if(d[i][j]==INF||i==j)continue;
if(d[i][j]>A[i].maxl[0])
A[i].s[0]=j,A[i].cnt[0]=1,A[i].maxl[0]=d[i][j];
else if(d[i][j]==A[i].maxl[0]) A[i].cnt[0]++;
if(d[i][j]>A[j].maxl[1])
A[j].s[1]=i,A[j].cnt[1]=1,A[j].maxl[1]=d[i][j];
else if(d[i][j]==A[j].maxl[1]) A[j].cnt[1]++;
}
FOR(i,1,n)FOR(j,1,n){
if(d[i][j]==INF||!A[i].cnt[1]||!A[j].cnt[0]||i==j)continue;
if(A[i].s[0]==A[j].s[1]&&A[i].cnt[0]==1&&A[j].cnt[1]==1) continue;
chkmx(ans,A[i].maxl[1]+d[i][j]+A[j].maxl[0]);
}
cout<<ans<<endl;
return 0;
}