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--;
}
}