您的位置:首页 > 其它

Codeforces #38H: The Great Marathon 题解

2018-03-11 23:20 519 查看
非常好的题目,考验思维和代码技巧
这道题目有一个性质,答案只关心每个人拿了什么奖牌,不关心他们的最终排名和最终成绩
所以要想让一个人拿金牌,那么他就应该在1~n中选最短路径最短的点作为终点,反之,要想让一个人拿铜牌,就应该选1~n中最短路径最长的点作为终点
于是我们可以获得n个最短点和n个最长点,我们可以确定,最终的金牌成绩线和铜牌成绩线一定在这些数中产生
所以我们可以枚举金牌线和铜牌线,O(n^2)
对于当前的线,G[i]表示第i个人可不可能拿到金牌,S[i]表示第i个人可不可能拿到银牌,B[i]表示第i个人可不可能拿到铜牌
G[i]只要判断最短时间是否比金牌线短
B[i]只要判断最长时间是否比铜牌线长
S[i]要判断是否存在一个时间在金牌线和铜牌线之间

然后考虑dp[i][j][k]为考虑到第i个人,当前有j个金牌和k个银牌的方案数,很好转移
总复杂度O(n^5)

有若干个注意点:
1.题目中提到如果dist相同则按照选手的编号从小到大排序
这里有一个trick: dist[i][j]=dist[i][j]*n+i
因为编号都不大于n,所以这个变换不会破坏原来不同的数之间的顺序,相同的数则会根据i的大小排序
这样枚举金牌线和铜牌线就方便了
2.这个dp状态是存在重复计数的,事实上,当金牌线为i,铜牌线为j的时候,有大量的方案是没有选手压线的,这样就会重复,dp状态计算的实际上是金牌线不超过i,铜牌线不小于j的方案数
所以我们可以考虑容斥原理,用(i,j)-(i-1,j)-(i,j+1)+(i-1,j+1)来得到最后的答案#include <cstdio>
#include <iostream>
#include <cstring>
#include <string>
#include <cstdlib>
#include <utility>
#include <cctype>
#include <algorithm>
#include <bitset>
#include <set>
#include <map>
#include <vector>
#include <queue>
#include <deque>
#include <stack>
#include <cmath>
#define LL long long
#define LB long double
#define x first
#define y second
#define Pair pair<int,int>
#define pb push_back
#define pf push_front
#define mp make_pair
using namespace std;

const int MOD=1e9+7;
const LL LINF=2e16;
const int INF=1e9;
const int magic=3048;
const double eps=1e-10;
const double pi=3.14159265;

inline int getint()
{
char ch;int res;bool f;
while (!isdigit(ch=getchar()) && ch!='-') {}
if (ch=='-') f=false,res=0; else f=true,res=ch-'0';
while (isdigit(ch=getchar())) res=res*10+ch-'0';
return f?res:-res;
}

int n,m;
int ga[58][58];
int g1,g2,s1,s2;
int fastest[58],slowest[58];
bool G[58],S[58],B[58];
LL dp[58][58][58];

LL solve(int goldline,int bronzeline,int silverup,int silverdown)
{
memset(G,false,sizeof(G));
memset(S,false,sizeof(S));
memset(B,false,sizeof(B));
int i,j,k;
for (i=1;i<=n;i++)
for (j=1;j<=n;j++)
if (i!=j)
{
if (ga[i][j]<=goldline) G[i]=true;
if (ga[i][j]>=bronzeline) B[i]=true;
if (ga[i][j]>silverup && ga[i][j]<silverdown) S[i]=true;
}
memset(dp,0,sizeof(dp));
dp[0][0][0]=1;
for (i=1;i<=n;i++)
for (j=0;j<=i;j++)
for (k=0;j+k<=i;k++)
{
if (G[i]) dp[i][j][k]+=dp[i-1][j-1][k];
if (S[i]) dp[i][j][k]+=dp[i-1][j][k-1];
if (B[i]) dp[i][j][k]+=dp[i-1][j][k];
}
LL res=0;
for (i=g1;i<=g2;i++)
for (j=s1;j<=s2;j++)
res+=dp
[i][j];
re
c8e0
turn res;
}

void floyd()
{
int i,j,k;
for (k=1;k<=n;k++)
for (i=1;i<=n;i++)
for (j=1;j<=n;j++)
if (k!=i && k!=j && i!=j && ga[i][k]+ga[k][j]<ga[i][j])
ga[i][j]=ga[i][k]+ga[k][j];
for (i=1;i<=n;i++)
for (j=1;j<=n;j++)
if (ga[i][j]<INF) ga[i][j]=ga[i][j]*n+i;
}

int main ()
{
int i,j,x,y,c;
n=getint();m=getint();
for (i=1;i<=n;i++)
for (j=1;j<=n;j++)
ga[i][j]=(i==j?0:INF);
for (i=1;i<=m;i++)
{
x=getint();y=getint();c=getint();
ga[x][y]=c;ga[y][x]=c;
}
g1=getint();g2=getint();s1=getint();s2=getint();
floyd();
for (i=1;i<=n;i++)
{
int maxlen=-INF,minlen=INF;
for (j=1;j<=n;j++)
if (i!=j) maxlen=max(maxlen,ga[i][j]),minlen=min(minlen,ga[i][j]);
fastest[i]=minlen;slowest[i]=maxlen;
}
LL ans=0;
for (i=1;i<=n;i++)
for (j=1;j<=n;j++)
ans=ans+solve(fastest[i],slowest[j],fastest[i],slowest[j])-solve(fastest[i]-1,slowest[j],fastest[i],slowest[j])-solve(fastest[i],slowest[j]+1,fastest[i],slowest[j])+solve(fastest[i]-1,slowest[j]+1,fastest[i],slowest[j]);
printf("%lld\n",ans);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: