魏傳之長阪逆襲、蜀傳之單刀赴會、吳傳之火燒連營
A、魏傳之長阪逆襲
時間限制: 1 Sec 記憶體限制: 128 MB
題目描述
眾所周知,劉備在長阪坡上與他的一眾將領各種開掛,硬生生從曹操手中逃了出去,隨後與孫權一起火燒赤壁、佔有荊益、成就霸業。而曹操則在赤壁一敗後再起不能,終生無力南下。
建安二十五年(220年),曹操已到風燭殘年,但仍難忘當年長阪的失誤,霸業的破滅。他想如果在劉備逃亡的路中事先佈下一些陷阱,便能拖延劉備的逃脫時間了。你作為曹操身邊的太傅,有幸穿越到了208年的長阪坡,為大魏帝國貢獻一份力,佈置一些陷阱。但時空守衛者告訴你你不能改變歷史,不能拖增大劉備的最大逃脫時間,但你身為魏武之仕,忠心報國,希望能新增一些陷阱使得劉備不論怎麼逃跑所用的時間都一樣。
已知共有n個據點,n-1條棧道,保證據點聯通。1號據點為劉備軍逃跑的起點,當劉備軍跑到某個據點後不能再前進時視為劉備軍逃跑結束。在任意一個棧道上放置1個陷阱會使通過它的時間+1,且你可以在任意一個棧道上放置任意數量的陷阱。
現在問你在不改變劉備軍當前最大逃跑時間的前提下,需要新增最少陷阱,使得劉備軍的所有逃脫時間都儘量的大。
輸入
第一行一個數n,表示據點個數。
接下來n-1行每行三個數,ai、bi、ti,表示從據點ai通過第i個棧道到bi耗時ti
輸出
僅包含一個數,為需要新增的最少陷阱數。
樣例輸入
3 1 2 1 1 3 3
樣例輸出
2
提示
【資料規模和約定】
對於 5%的資料,1<=n<=100000,1<=ti<=200000
對於 100%的資料,1<=n<=500000,0<ti<=1000000
這是一棵樹
每次對於深度越低的點能放多少陷阱就放多少陷阱
#include<cstdio> #include<iostream> #define ll long long using namespace std; int read() { int ret=0; char ch=getchar(); while(ch<'0'||ch>'9') ch=getchar(); while(ch>='0'&&ch<='9') ret=(ret<<1)+(ret<<3)+ch-'0',ch=getchar(); return ret; } const int N=1e6+5; int n,num,id[N]; ll mx,ss[N],a[N],ans; int cnt,he[N],to[N],nxt[N],w[N]; inline void add(int u,int v,int k) { to[++cnt]=v,w[cnt]=k,nxt[cnt]=he[u],he[u]=cnt; } void dfs(int u,ll s) { bool ch=0; for(int e=he[u];e;e=nxt[e]) ch=1,dfs(to[e],s+w[e]); if(!ch) mx=max(mx,s),id[++num]=u,ss[num]=s; } void dfs1(int u) { if(he[u]) a[u]=1e18; for(int e=he[u];e;e=nxt[e]) { int v=to[e]; dfs1(v); a[u]=min(a[u],a[v]); } for(int e=he[u];e;e=nxt[e]) ans+=a[to[e]]-a[u]; } int main() { n=read(); for(int i=1;i<n;i++) { int u=read(),v=read(),k=read(); add(u,v,k); } dfs(1,0); for(int i=1;i<=num;i++)a[id[i]]=mx-ss[i]; dfs1(1); printf("%lld",ans); return 0; }
B、蜀傳之單刀赴會
時間限制: 1 Sec 記憶體限制: 128 MB
題目描述
【題目背景】
公元215年,劉備取益州,孫權令諸葛瑾找劉備索要荊州。劉備不答應,孫權極為惱恨,便派呂蒙率軍取長沙、零陵、桂陽三郡。長沙、桂陽蜀將當即投降。劉備得知後,親自從成都趕到公安(今湖北公安),派大將關羽爭奪三郡。孫權也隨即進駐陸口,派魯肅屯兵益陽,抵擋關羽。雙方劍拔弩張,孫劉聯盟面臨破裂,在這緊要關頭,魯肅為了維護孫劉聯盟,不給曹操可乘之機,決定當面和關羽商談。“肅邀羽相見,各駐兵馬百步上,但諸將軍單刀俱會”。雙方經過會談,緩和了緊張局勢。隨後,孫權與劉備商定平分荊州,“割湘水為界,於是罷軍”,孫劉聯盟因此能繼續維持。
【問題描述】
關羽受魯肅邀請,為了大局,他決定冒險赴會。他帶著侍從周倉,義子關平,騎著赤兔馬,手持青龍偃月刀,從軍營出發了,這就是歷史上赫赫有名的“單刀赴會”。關羽平時因為軍務繁重,決定在這次出行中拜訪幾個多日不見的好朋友。然而局勢緊張,這次出行要在限定時間內完成,關公希望你能夠幫助他安排一下行程,安排一種出行方式,使得從軍營出發,到達魯肅處赴會再回來,同時拜訪到儘可能多的朋友,在滿足這些條件下行程最短。注意拜訪朋友可以在赴會之前,也可以在赴會之後。現在給出地圖,請你完成接下來的任務
輸入
第一行n,m,k,t,代表有n個地點,m條道路,有k個朋友(不包括魯肅),以及限定時間t(行走1單位長度的路程用時1單位時間)。
接下來m行,每行有x,y,w三個整數,代表x和y之間有長度為w的道路相連。
接下來一行有k個整數,代表朋友所在的都城編號(保證兩兩不同,且不在1和n)
(我們約定1是關羽的營地,n是魯肅的營地)
輸出
輸出兩個整數,分別是最多可以拜訪的朋友數,以及在這種情況下最少需要耗費的時間,如果連到達魯肅再回來都無法完成,輸出一個-1就可以了。
樣例輸入
5 7 2 15 1 2 5 1 3 3 2 3 1 2 4 1 3 4 4 2 5 2 4 5 3 2 4
樣例輸出
2 14
提示
【資料規模和約定】
有10%資料,n<=10,m<=50,k<=5;
有10%資料,k=0;
有10%資料,k=1;
另30%資料,k<=5;
對於100%資料,n<=10000,m<=50000,k<=15,t<=2147483647,w<=10000
先跑一遍dijkstra,然後狀壓dp
#include<cstdio>
#include<queue>
#include<iostream>
#define ll long long
using namespace std;
int read()
{
int ret=0; char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9')
ret=(ret<<1)+(ret<<3)+ch-'0',ch=getchar();
return ret;
}
const int N=2e5+5;
int n,m,p,t,a[25],d[N],dis[25][25],num[N];
ll f[25][N];
int cnt,he[N],to[N],nxt[N],w[N],ans,anss;
bool fl[N];
struct NA{
int id,x;
};
bool operator >(NA i,NA j)
{
return i.x>j.x;
}
priority_queue<NA,vector<NA>,greater<NA> >q;
inline void add(int u,int v,int k)
{
to[++cnt]=v,nxt[cnt]=he[u],w[cnt]=k,he[u]=cnt;
}
int dist(int s,int g)
{
while(!q.empty()) q.pop();
for(int i=1;i<=n;i++) fl[i]=0,d[i]=2e9;
q.push((NA){s,d[s]=0});
for(int i=1;i<=n;i++)
{
while(!q.empty()&&fl[q.top().id]) q.pop();
if(q.empty()) break;
int u=q.top().id; q.pop(); fl[u]=1;
if(u==g) break;
for(int e=he[u];e;e=nxt[e])
{
int v=to[e];
if(!fl[v]&&d[v]>d[u]+w[e])
q.push((NA){v,d[v]=d[u]+w[e]});
}
}
return d[g];
}
int main()
{
n=read(),m=read(),p=read(),t=read();
for(int i=1;i<=m;i++)
{
int u=read(),v=read(),k=read();
add(u,v,k),add(v,u,k);
}
for(int i=1;i<=p;i++) a[i]=read();
a[p+1]=1,a[p+2]=n;
for(int i=1;i<=p+2;i++)
for(int S=0;S<1<<p+2;S++) f[i][S]=1e15;
ll x=dist(1,n);
if(x==2e9||x<<1>t)
{
puts("-1"); return 0;
}
for(int i=1;i<p+2;i++)
for(int j=i+1;j<=p+2;j++)
dis[j][i]=dis[i][j]=dist(a[i],a[j]);
f[p+1][1<<p]=0;
for(int S=0;S<1<<p+2;S++)
if(!(S&1<<p))
{
int SS=S|1<<p;
for(int i=1;i<=p+2;i++)
for(int j=1;j<=p+2;j++)
if(i!=j&&1<<j-1&SS&&f[j][SS]!=1e15&&dis[i][j]!=2e9)
f[i][SS|1<<i-1]=min(f[i][SS|1<<i-1],f[j][SS]+dis[i][j]);
}
for(int S=1;S<1<<p+2;S++) num[S]=num[S-(S&-S)]+1;
ans=0; anss=x<<1;
for(int SS=0;SS<1<<p;SS++)
{
int S=SS|1<<p|1<<p+1;
for(int i=1;i<=p+2;i++)
{
if((1<<i-1)&S&&f[i][S]!=1e15)
{
ll ti=f[i][S]+dis[i][p+1];
if(ti<=t)
if(ans==num[S]) anss=min(anss,(int)ti);
else if(ans<num[S]) ans=num[S],anss=ti;
}
}
}
printf("%d %d\n",ans-2,anss);
return 0;
}
C、吳傳之火燒連營
時間限制: 1 Sec 記憶體限制: 128 MB Special Judge
題目描述
【題目背景】
蜀漢章武元年(221年),劉備為報吳奪荊州、關羽被殺之仇,率大軍攻吳。吳將陸遜為避其鋒,堅守不戰,雙方成對峙之勢。蜀軍遠征,補給困難,又不能速戰速決,加上入夏以後天氣炎熱,以致銳氣漸失,士氣低落。劉備為舒緩軍士酷熱之苦,命蜀軍在山林中安營紮寨以避暑熱。陸遜看準時機,命士兵每人帶一把茅草,到達蜀軍營壘時邊放火邊猛攻。蜀軍營寨的木柵和周圍的林木為易燃之物,火勢迅速在各營漫延。蜀軍大亂,被吳軍連破四十餘營。陸遜火燒連營的成功,決定了夷陵之戰蜀敗吳勝的結果。
【問題描述】
劉備帶兵深入吳境,陸遜卻避而不出,蜀軍只得在山林中安營紮寨。而劉備在紮營時卻犯了兵家大忌,將兵營排列成一條直線,遠遠看去,就像是一條串著珠子的鏈,美其名曰:鏈寨。如果吳軍將領是一般人,那麼這也許不算什麼,而陸遜何許人也,他可是江東才子,能力不低於周瑜的一代儒將。他看到劉備這樣排陣,心生一計,決定用火攻破陣。然而,火計除了要有風,選定引火點也非常重要,對於劉備的佈陣,最佳引火點一定是n個兵營中的一個。而因為風水輪流轉,每天的最佳引火點都不一樣。我們給每個兵營定下一個固定不變的火攻值Ai,每天定下一個風水值K,對於每天的最佳引火點,顯然是所有兵營中火攻值與風水值異或的結果最大的那一個兵營。然而,陸遜是個謹慎的人,他要觀察時機,在m天中選定一個最佳的進攻的日期,為此他演算出了這m天每天的風水值,然後他希望你能夠告訴他這m天每天最佳引火點的兵營編號。
輸入
第一行n,m,代表有n個兵營,m天。
接下來一行有n個非負整數,代表這n個兵營的火攻值。
接下來一行有m個非負整數,代表這m天的風水值。
輸出
輸出共m行,每行輸出一個整數,代表第m天最佳引火點的編號。
如果存在多個最佳引火點使得火攻值與風水值的異或值最小,請任意輸出一組解即可。
樣例輸入
3 2 1 2 3 4 5
樣例輸出
3 2
提示
【樣例解釋】
對於第1天,由於4 xor 1=5, 4 xor 2=6, 4 xor 3=7,選擇第3個引火點是最佳的。
對於第2天,由於5 xor 1=4, 5 xor 2=7, 5 xor 3=6,選擇第2個引火點是最佳的。
【資料規模和約定】
對於30%資料,n<=1000,m<=1000
對於100%資料,n<=100000,m<=100000, 0<=k,ai<=2147483647
這不是一道常規的trie樹?
#include<cstdio>
using namespace std;
int read()
{
int ret=0; char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9')
ret=(ret<<1)+(ret<<3)+ch-'0',ch=getchar();
return ret;
}
const int N=1e7+5;
int n,m,p,now,nxt[N][2],ans[N];
int main()
{
n=read(),m=read();
p=1;
for(int i=1;i<=n;i++)
{
int x=read();
now=1;
for(int j=31;j;j--)
{
int t=(x>>(j-1))&1;
if(nxt[now][t]) now=nxt[now][t];
else now=nxt[now][t]=++p,ans[p]=i;
}
}
for(int i=1;i<=m;i++)
{
int x=read();
now=1;
for(int j=31;j;j--)
{
int t=!((x>>(j-1))&1);
if(!nxt[now][t])
{
now=nxt[now][!t];
} else now=nxt[now][t];
}
printf("%d\n",ans[now]);
}
return 0;
}