考的是不太擅长的DP……
赛时
题目有错误!
导致用了很长时间才看全题目……
四道题都不太会……
赛时还是写上了一些暴力,拿到了一些分数……
见赛后吧……
赛后
四道赛题,订正了很长时间。
T1
明显是数位dp。但是考场上没写出来
与一般的dp不同,发现从最高位到最低位很难维护。
想到逆向,从最低位到最高位。
问题变得好解决:两个bool
判断当前后缀是否为倍数和此数是否可选取(是否均为0).
#include <bits/stdc++.h>
#define fo(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
using namespace std;
const int INF = 0x3f3f3f3f , N = 1e3+5 , M = 1e2+5;
typedef long long ll;
typedef unsigned long long ull;
inline ll read(){
ll ret = 0 ;char ch = ' ' , c = getchar();
while(!(c >= '0' && c <= '9'))ch = c , c = getchar();
while(c >= '0' && c <= '9')ret = (ret << 1) + (ret << 3) + c - '0' , c = getchar();
return ch == '-' ? -ret : ret;
}
int n,k,mod;
int dp[N][M][2],Pow[N];
int dfs(int pos,int num,bool jud,bool zero){
if(pos > n)return jud;
if(!zero && dp[pos][num][jud] != -1)return dp[pos][num][jud];
int ans = 0;
for(int i = 0 ; i <= 9 ; i ++){
if(pos == n && !i)continue;
int nnum = (i * Pow[pos] + num) % k,
nzero = zero && !i;
(ans += dfs(pos+1 , nnum , jud || (!nnum && !nzero),nzero)) %= mod;
}
if(!zero)dp[pos][num][jud] = ans;
return ans;
}
signed main(){
memset(dp,-1,sizeof(dp));
n = read() , k = read() , mod = read();
Pow[1] = 1;
for(int i = 2 ; i <= n ; i ++)
Pow[i] = (Pow[i-1] * 10) % k;
printf("%d",dfs(1,0,0,1));
}
T2
CF601C Kleofáš and the n-thlon
一个期望dp啦
设dp[i][j]
表示前(i)个人获得(j)分数的概率,处理期望即可。
#include <bits/stdc++.h>
#define fo(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
using namespace std;
const int INF = 0x3f3f3f3f , N = 1e2+5 , M = 1e3+5;
typedef long long ll;
typedef unsigned long long ull;
inline ll read(){
ll ret = 0 ;char ch = ' ' , c = getchar();
while(!(c >= '0' && c <= '9'))ch = c , c = getchar();
while(c >= '0' && c <= '9')ret = (ret << 1) + (ret << 3) + c - '0' , c = getchar();
return ch == '-' ? -ret : ret;
}
int n,m;
int a[N],sum;
double dp[N][M*N],s[M*N];
double ans;
signed main(){
// fo("nthlon");
n = read() , m = read();
for(int i = 1 ; i <= n ; i ++)
a[i] = read() ,
sum += a[i];
for(int i = 1 ; i <= m ; i ++){
if(i != a[1])
dp[1][i] = 1.0 / (m-1);
}
for(int i = 2 ; i <= n ; i ++){
for(int j = 1 ; j <= i*m ; j ++)
s[j] = s[j-1] + dp[i-1][j];
for(int j = i ; j <= i*m ; j ++){
if(j <= m+1) dp[i][j] = s[j-1] - dp[i-1][j-a[i]] * (1 <= j-a[i]);
else dp[i][j] = s[j-1] - s[j-m-1] - dp[i-1][j-a[i]] * (j-m <= j-a[i]);
dp[i][j] /= (m-1);
// for(int k = 1 ; k <= min(j-1,m) ; k ++)
// if(k != a[i])
// dp[i][j] += dp[i-1][j-k]/(m-1),
// printf(" dp[%d][%d] = s[%d][%d](%.3lf) - s[%d][%d](%.3lf)
",i,j,i-1,j-1,s[i-1][j-1],i-1,max(1,j-m)-1,s[i-1][max(1,j-m)-1]);
}
}
// for(int i = 1 ; i <= n ; i ++)
// for(int j = 1 ; j <= i*m ; j ++)
// printf(" dp[%d][%d] = %lf
",i,j,dp[i][j]);
for(int i = 1 ; i < sum ; i ++)
ans += dp[n][i];
printf("%.15lf",ans * (m-1) + 1 );
return 0;
}
/*
2 2 1000
*/
T3
跑两遍dij,找出(i o b+1)及(b+1 o i)的最短路。设(dis[i])为二者之和。
则接下来的任务就是将dis[1],dis[2],...dis[b]分成s 组,每组的代价为该组内dis 之和*(该组内dis 元素个数-1),目标是最小化所有组的代价之和。
便是排序之后,运用分段dp的思想进行转移。
在最优解中,dis 从小到大依次划分所得到的段的长度一定是单调不增的,则上述DP 转移中k 的最优取
值一定在([i-dfrac ij,i))之间,于是总转移复杂度就是(O(n²(1/1+1/2+1/3+...+1/n)) =O(n²logn)),足以通过本题。
#include <bits/stdc++.h>
#define fo(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
using namespace std;
const int INF = 0x3f3f3f3f,N = 5e3+5 , M = 5e4+5;
typedef long long ll;
typedef unsigned long long ull;
inline ll read(){
ll ret = 0 ;char ch = ' ' , c = getchar();
while(!(c >= '0' && c <= '9'))ch = c , c = getchar();
while(c >= '0' && c <= '9')ret = (ret << 1) + (ret << 3) + c - '0' , c = getchar();
return ch == '-' ? -ret : ret;
}
int n,b,s,m;
int ecnt1 = -1 ,ecnt2 = -1 , head1[N] , head2[N];
struct Edge{int to,nxt,w;}e1[M],e2[M];
inline void add_edge1(int u,int v,int w){
e1[++ecnt1] = (Edge){v,head1[u],w};
head1[u] = ecnt1;
}
inline void add_edge2(int u,int v,int w){
e2[++ecnt2] = (Edge){v,head2[u],w};
head2[u] = ecnt2;
}
int dis1[N],dis2[N];ll dis[N];bool vis1[N],vis2[N];
typedef pair<int,int> pr;
priority_queue<pr,vector<pr>,greater<pr> >q;
void dij1(int s){
while(!q.empty())q.pop();
memset(dis1,0x3f,sizeof(dis1));
memset(vis1,0,sizeof(vis1));
dis1[s] = 0 ; q.push(make_pair(0,s));
while(!q.empty()){
int u = q.top().second;q.pop();
if(vis1[u])continue;
vis1[u] = 1;
for(int i = head1[u] ; ~i ; i = e1[i].nxt){
int v = e1[i].to , w = e1[i].w;
if(dis1[v] > dis1[u] + e1[i].w)
dis1[v] = dis1[u] + e1[i].w,
q.push(make_pair(dis1[v],v));
}
}
}
void dij2(int s){
while(!q.empty())q.pop();
memset(dis2,0x3f,sizeof(dis2));
memset(vis2,0,sizeof(vis2));
dis2[s] = 0 ; q.push(make_pair(0,s));
while(!q.empty()){
int u = q.top().second;q.pop();
if(vis2[u])continue;
vis2[u] = 1;
for(int i = head2[u] ; ~i ; i = e2[i].nxt){
int v = e2[i].to , w = e2[i].w;
if(dis2[v] > dis2[u] + e2[i].w)
dis2[v] = dis2[u] + e2[i].w,
q.push(make_pair(dis2[v],v));
}
}
}
ll dp[N][N],sum[N];
void work(){
// fo("assignment");
memset(head1,-1,sizeof(head1));memset(head2,-1,sizeof(head2));
ecnt1 = ecnt2 = -1;
for(int i = 1 ; i <= m ; i ++){
int u = read() , v = read() , w = read();
add_edge1(u,v,w);
add_edge2(v,u,w);
}
dij1(b+1);dij2(b+1);
for(int i = 1 ; i <= b ; i ++)
// printf("%d : %d,%d
",i,dis1[i],dis2[i]),
dis[i] = dis1[i] + dis2[i];
sort(dis+1,dis+b+1);
for(int i = 1 ; i <= b ; i ++)
sum[i] = sum[i-1] + dis[i];
// memset(dp,0x3f,sizeof(dp));
dp[0][0] = 0;
for(int i = 1 ; i <= b ; i ++){
dp[i][0]= 1LL * INF * INF;
for(int j = 1 ; j <= min(i,s) ; j ++){
dp[i][j] = 2e15;
for(int k = i-i/j ; k <= i ; k ++)
dp[i][j] = min(dp[i][j],dp[k][j-1] + (sum[i]-sum[k]) * (i-k-1));
// printf(" dp[%d][%d] = min(dp[%d][%d](%lld) + sum[%d,%d](%lld) * (%d-%d-1)))
",i,j,k,j-1,dp[k][j-1],i,k,sum[i]-sum[k],i,k);
}
}
// for(int i = 1 ; i <= b ; i ++)
// for(int j = 1 ; j <= min(i,s) ; j ++)
// printf("dp[%d][%d] = %lld
",i,j,dp[i][j]);
printf("%lld
",dp[b][s]);
}
signed main(){
while(scanf("%d %d %d %d",&n,&b,&s,&m) != EOF)
work();
return 0;
}