T1:简单的线段树维护。
T2:这题正解很简单,几乎所有人都想出来了,但是我想了很久都没思路。
一开始我以为是一个网络流,想了一会发现不会建图。
比赛结束后发现其实就是一个最小生成树。因为我们要把图连成许多个联通块,每一个联通块都要有一个被选中的点,那么我们就新建一个节点,这个节点向其他点连边的边权就是选中那个点的代价。建好新点之后,直接做最小生成树就可以了。
总结:以后遇到这种花最小代价使图联通的题目就要往最小生成树方向想。因为最小生成树的定义就是这个。
T3:首先利用经过x的个数进行分层SPFA。完了之后我们发现所有可能的最短路都可以表示成kx+b的形式。
那么这题就转化成了有许多条直线,我们要求x在一个范围内的最小值的个数和最小值的和。
再分析一下发现题目要求的就是一个上凸壳。上凸壳的求法:先按k从小到大(从大到小也行,只要是有序的就可以了)排序,然后如果之前两条直线的交点在当前直线的左边,那么就弹栈。
(拓展:求下凸壳。先按k排序,然后加边,不过这次是交点在右边才弹栈。)
最后求答案时要用等差数列求和,不能枚举x(因为x会很大)。
细节较多,贴一下代码:
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#define ll long long
#define db long double
#define MAXN 510
#define MAXM 10010
using namespace std;
struct map
{
ll x;
ll y;
ll l;
ll k;
};
map way[MAXM];
struct dot
{
db px;
db py;
db vx;
db vy;
};
dot c[MAXN];
ll first[MAXN],next[MAXM],dis[MAXN][MAXN],bz[MAXN][MAXN],d[MAXN*MAXN*5][2],n,m,T,sd,td,ans1,ans2;
ll inf,t,g[MAXN],s,lim;
db x,y;
int SPFA()
{
ll i,j,h,t;
memset(dis,0x7f,sizeof(dis));inf=dis[0][0];
memset(bz,0,sizeof(bz));
dis[sd][0]=0;bz[sd][0]=1;
d[1][0]=sd;d[1][1]=0;
h=0;t=1;
while(h<t)
{
h++;
i=first[d[h][0]];
while(i>=1&&i<=m)
{
if(way[i].k==0)
{
if(dis[d[h][0]][d[h][1]]+way[i].l<dis[way[i].y][d[h][1]])
{
dis[way[i].y][d[h][1]]=dis[d[h][0]][d[h][1]]+way[i].l;
if(bz[way[i].y][d[h][1]]==0)
{
t++;d[t][0]=way[i].y;d[t][1]=d[h][1];
bz[d[t][0]][d[t][1]]=1;
}
}
}
else
if(d[h][1]<lim)
if(dis[d[h][0]][d[h][1]]<dis[way[i].y][d[h][1]+1])
{
dis[way[i].y][d[h][1]+1]=dis[d[h][0]][d[h][1]];
if(bz[way[i].y][d[h][1]+1]==0)
{
t++;d[t][0]=way[i].y;d[t][1]=d[h][1]+1;
bz[d[t][0]][d[t][1]]=1;
}
}
i=next[i];
}
bz[d[h][0]][d[h][1]]=0;
}
}
ll read()
{
ll s=0;
char x=getchar();
while(x<'0'||x>'9')
{
if(x=='x')return -1;
x=getchar();
}
while('0'<=x&&x<='9')s=s*10+x-'0',x=getchar();
return s;
}
db cro(db x1,db y1,db x2,db y2){return x1*y2-x2*y1;}
db inter(ll i,ll j)
{
db c1,c2,g;
c1=cro(c[i].vx,c[i].vy,c[i].px-c[j].px,c[i].py-c[j].py);
c2=cro(c[i].vx,c[i].vy,c[j].vx,c[j].vy);
g=c1/c2;
x=c[j].px+c[j].vx*g;
y=c[j].py+c[j].vy*g;
}
db val(db x,ll i){return c[i].py+(x-c[i].px)*c[i].vy;}
ll up(db x)
{
if(x==(ll)x)return x;
else return (ll)x+1;
}
ll down(db x){return (ll)x;}
int main()
{
ll i,j,tim,tf;
db lx,ly;
n=read();m=read();
for(i=1;i<=m;i++)
{
way[i].x=read();way[i].y=read();way[i].l=read();
}
for(i=1;i<=m;i++)
if(way[i].l==-1)way[i].k=1;
for(i=m;i>=1;i--)next[i]=first[way[i].x],first[way[i].x]=i;
if(n>m)lim=m;
else lim=n;
scanf("%lld",&T);
while(T>=1)
{
scanf("%lld %lld\n",&sd,&td);
SPFA();
tf=0;
for(i=0;i<=lim;i++)
if(dis[td][i]!=inf){tf=1;break;}
if(tf==0){printf("0 0\n");T--;continue;}
if(dis[td][0]==inf){printf("inf\n");T--;continue;}
s=0;
for(i=0;i<=lim;i++)
{
if(dis[td][i]==inf)continue;
s++;
c[s].px=0;c[s].py=dis[td][i];
c[s].vx=1;c[s].vy=i;
}
t=0;
for(i=1;i<=s;i++)
{
while(t>1)
{
inter(g[t],g[t-1]);
if(cro(c[i].vx,c[i].vy,x-c[i].px,y-c[i].py)>0)t--;
else break;
}
t++;g[t]=i;
}
ans1=0;ans2=0;
lx=1;tim=0;
for(i=t-1;i>=1;i--)
{
inter(g[i+1],g[i]);
if(up(lx)>down(x))continue;
tim++;
ans1=ans1+down(x)-up(lx)+1;
ans2=ans2+(val(up(lx),g[i+1])+val(down(x),g[i+1]))*(down(x)-up(lx)+1)/2;
if(tim!=1&&up(lx)==lx){ans1--;ans2-=val(up(lx),g[i+1]);}
lx=x;
}
if(val(down(x),g[2])!=c[g[1]].py){ans1++;ans2=ans2+c[g[1]].py;}
printf("%lld %lld\n",ans1,ans2);
T--;
}
}