失分小结:
估分:100+80+40=220
实际分数:100+10+35=145
和估分相差甚远,主要是由于第一题的难度提升
又在第一题上卡了太久,导致第二题随便打了个暴力后又去水第三题
虽然主体顺序是没有错的,但是心态就不一样了,想着多水点分,又感觉时间来不及,虽然最后第二题想到了最小生成树,但又因为取边取错爆炸,暴力(dp)写错
题解:
Task 1:
第一题如果直接考虑两个数之间的关系的话,还是较为容易的,方案一共就只有n2种,我们也只用枚举这么多,然后剩下的就用排列组合推就好了
tip:当无法方案数很多时,可以把方案拆开,对单独的一个小步骤进行分析,然后利用排列组合求解
Task 2:
第二题也较为玄学,直接使用最小生成树算法可以卡过去
dp的大体转移好想,但具体的实现就较为困难
可以推出一个点有几种连接方案:
1.与自己这一列的前一个点相连
2.与自己这一列的后一个点相连
3.与自己对面一列的一个点相连
对于第三种情况,可以贪心地实现
可以推算出若其他点已经相连,那么若要把这个点加入,
就要选择离自己最近的点,然这里又有一个特殊情况:
在我们对面的右边的点实际上并没有被加入到点集里,所以连接离自己最近的右边的点可能才是最优的(在最小生成树的算法中,就是要把这两个离自己最近的点加进去)
然后先不考虑空间优化,可以发现自己在对面的转移实际只有两种情况
而在自己这一列的转移只有一种情况,所以递推式就很容易出来了
代码实现:
#include<bits/stdc++.h>
using namespace std;
#define M 1000005
#define du double
int A[M],B[M];
du dp[2][2][2];
int x3,x1,x2;
du dist(int x,int y){
return sqrt((long long)(A[x]-B[y])*(A[x]-B[y])+x3);
}
int main(){
int n,m;
scanf("%d%d%d%d",&n,&m,&x1,&x2);
x3=1ll*(x1-x2)*(x1-x2);
for(int i=1;i<=n;i++)scanf("%d",&A[i]),A[i]+=A[i-1];
for(int i=1;i<=m;i++)scanf("%d",&B[i]),B[i]+=B[i-1];
int j=1;
dp[1][1][1]=dist(1,1);
for(int i=1;i<=n;i++){// 1 i与j连通 0 i与j不连通
if(i>1){
dp[i&1][j&1][1]=min(dp[!(i&1)][j&1][1]+dist(i,j),
min(dp[!(i&1)][j&1][1]+A[i]-A[i-1],dp[!(i&1)][j&1][0]+A[i]-A[i-1]+dist(i,j)));
dp[i&1][j&1][0]=min(dp[!(i&1)][j&1][1],dp[!(i&1)][j&1][0]+A[i]-A[i-1]);
}
while(j<m&&(A[i]>=B[j]||i==n)){
min(dp[i&1][!(j&1)][1]+B[j]-B[j-1],dp[i&1][!(j&1)][0]+B[j]-B[j-1]+dist(i,j)));
dp[i&1][j&1][0]=min(dp[i&1][!(j&1)][1],dp[i&1][!(j&1)][0]+B[j]-B[j-1]);
}
}
printf("%.2f\n",dp[n&1][m&1][1]);
return 0;
}
Task 3:
感觉这次的第三题相比于第二题的正解来说更好搞一些
第三题就是dfs加玄学剪枝
如果切了m=1这一档就可以清楚地发现这个方案数是可以根据组合数递推的
这题给出的诡异的切分也很玄妙,前面五十分n小一些,后面50分m小一些
前50分可以直接状压,后50分就直接枚举m的位置,然后用组合数算就好了
代码实现(只有后50分)
#include<bits/stdc++.h>
using namespace std;
#define FOR(i,x,y) for(int i=(x);i<=(y);i++)
#define ll long long
int n,m,P;
struct node{
int id,pos;
bool operator <(const node &s)const{return id<s.id;}
}D[10];
int C[35][35],fac[35];
void init(){
FOR(i,0,30){cout<<666<<endl;
C[i][0]=C[i][i]=1;
FOR(j,1,i-1)C[i][j]=(C[i-1][j]+C[i-1][j-1])%P;
}
fac[0]=1;
FOR(i,1,30)fac[i]=1ll*fac[i-1]*i%P;
}
bool mark[35],used[35];
int ans;
void dfs(int x,int res,int tmp){
res+=D[x].pos-D[x-1].pos;
if(x>m){
tmp=1ll*tmp*fac[res]%P;
ans=(ans+tmp)%P;
return;
}
int sum=0;
for(int i=1;i<=n;i++)used[i]=mark[i];
for(int i=D[x].pos;i<=n;i++)if(!mark[i]){
sum++;
if(sum>res)break;
dfs(x+1,res-sum,tmp*fac[sum]%P*C[sum][res]%P);
mark[i]=1;
}
for(int i=1;i<=n;i++)mark[i]=used[i];
}
int main(){
init();
scanf("%d%d%d",&n,&m,&P);
FOR(i,1,m)scanf("%d%d",&D[i].id,&D[i].pos);
sort(D+1,D+1+m);
D[m+1].pos=n;
dfs(1,0,1);
printf("%d\n",ans);
return 0;
}