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;
}
这道题目有一个性质,答案只关心每个人拿了什么奖牌,不关心他们的最终排名和最终成绩
所以要想让一个人拿金牌,那么他就应该在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;
}
相关文章推荐
- Codeforces 38H The Great Marathon [dp]
- codeforces April Fools Day Contest 2014 A. The Great Game
- CodeForces 294E Shaass the Great 树形DP
- BFS (图)——Codeforces 788 C. The Great Mixing
- codeforces 788C The Great Mixing (bitset优化dp、bfs)
- 7_6_M题 The Values You Can Make题解[Codeforces 687C](DP)
- 小白题解 Codeforces 266A Stones on the Table
- 【Codeforces 789E】 The Great Mixing 【数学推导+bfs】
- Codeforces 788 C. The Great Mixing
- 【CF331C】【Codeforces 331C】 The Great Julya Calendar
- codeforces:A. The Great Game
- codeforces 294E Shaass the Great (树形dp,好题)
- [hdu-4006]The kth great number 题解
- (codeforces) The Great Mixing
- 20140501 Anindilyakwa 题解&&The Great Team 题解
- Codeforces 294E Shaass the Great 树形dp(水
- codeforces 741 D. Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths (dsu on the tree)
- 【codeforces 782B】The Meeting Place Cannot Be Changed(二分)
- codeforces 919非官方题解(不完整版)
- Codeforces 599 C Day at the Beach