題目
樹的難題
Time Limits: 2000 ms
Memory Limits: 262144 KB
Description
Input
輸入文件爲。
第一行 包含 一個正整數 T,表示有T組測試數據 。接下來 依次是 T組測試數 據。
每組測試數 據的第一行包含個正整數N。
第二行包含 N個 0、1、2之一 的整數,依次 表示點 1到點 N的顏色。其中0表示黑色, 1表示白色, 2表示灰色。
接下來 N-1行 ,每行爲三個整數 ui、vi、ci,表示 一條權值等於 ci的邊。
Output
輸出文件爲。
輸出 T行 ,每一個整數, 依次 表示 每組測試數據 的答案。
Sample Input
1
5
0 1 1 1 0
1 2 5
1 3 3
5 2 5
2 4 16
Sample Output
10
【樣例解釋】
花費 10 的代價刪去 邊(1, 2)和邊(2, 5)。
Data Constraint
對於 10% 的數據: 1 ≤ N ≤ 10。
對於 30% 的數據: 1 ≤ N ≤ 50 0。
對於 60% 的數據: 1 ≤ N ≤ 50 000 。
對於 100% 的數據: 1 ≤ N ≤ 300 000 ,1 ≤ T ≤ 5,0 ≤ ci ≤ 10^9。
祕密任務
Time Limits: 1000 ms
Memory Limits: 262144 KB
Description
Input
輸入文件爲:
第一行 包含一 個正整數 T,表示有 T組測試數據。接下來 依次是 T組測試數 據。
每組測試數 據的第一行包含兩個整N、M。
第二行包含 N - 1個正整數,依次表示 A1,A2, …,AN-1。
接下來 M行,每 行爲三個 整數: ui、vi、ci,表示一條連接城市ui和城市 vi的路程等於 ci的高速公路。
Output
輸出文件爲。
輸出 T行, 依次 表示 每組測試數據 的答案 。若最優 方案 唯一 則輸出 ”Yes”和 最小 代價 ,否則 輸出 ”No ”和最小 代價 。字符串 和整數 之間 請用一個 空格 隔開 。
Sample Input
3
3 3
2 4
1 3 23
3 2 12
2 1 11
4 4
3 2 2
1 2 1
2 3 1
3 4 1
4 1 1
3 4
3 2
1 2 1
2 3 2
2 3 19
3 1 4
Sample Output
Yes 4
Yes 3
No 2
【樣例解釋】
第 1組測試數據: 最優 方案 是在城市 1設立 兩個 檢查點 。
第 2組測試數據: 最優 方案 是城市 1的高速公路 (1, 4 )的出入口設立 檢查點。
第 3組測試數據: 最優 方案 是在城市 2設立 一個 檢查點 ,不過 既可以 設置 在 高速公路 (1, 2)的出入 口,也可以 設置 在高速公路 (2, 3)的出入口 。
Data Constraint
對於 10% 的數據: 2 ≤ N ≤ 10 , 1 ≤ M ≤ 20。
另有 40% 的數據: 最優 方案 是唯一 的。
對於 10 0% 的數據: 2 ≤ N ≤ 400, 1 ≤ M ≤ 4 00 0,1 ≤ T ≤ 5,1 ≤ Ai, c ≤ 10^9。無向圖可能有重邊。
查詢
Time Limits: 3000 ms
Memory Limits: 524288 KB
Description
對於一個整數序列,查詢區間第k大數可以在O(logN)的時間內輕鬆完成。現在我們對這個問題進行推廣。
考慮帶重複數的集合(multiset)。定義在該類集合上的並操作“+”爲兩個集合的所有數不剔除重複得到的結果。比如,若A={1,2,2,3},B={2,3,4,4},則C={1,2,2,2,3,3,4,4}。
對於一個給定序列A[1…N],定義A[x…y]爲包含y-x+1個元素的集合{A[x],A[x+1],…,A[y]}。現在,給定整數序列A,你需要回答很多詢問,其中第i個詢問要求集合A[x[i,1]…y[i,1]]+A[x[i,2]…y[i,2]]+…+A[x[i,ki]…y[i,ki]]中第pi小的元素。
Input
輸入的第一行包含兩個整數N和M,分別表示整數序列的長度和詢問的個數。
第二行N個整數給出整數序列A。
第3到第M+2行給出了所有的詢問。第i+2行前兩個整數爲ki和pi,接下來2ki個整數給出x[i, 1], y[i, 1], x[i,2], …, x[i, ki], y[i, ki]。這些數按照題目中的定義描述了第i個詢問。
Output
對於每一個詢問,輸出相應的結果,即從小到大排序後的第pi個元素。
Sample Input
8 3
1 2 3 1 2 3 1 2
2 4 1 4 3 7
2 4 1 3 6 8
5 31 1 8 1 8 1 8 1 8 1 8
Sample Output
1
2
3
Data Constraint
第1、2個測試數據滿足N ≤ 1 000,M ≤ 1 000。
第3、4個測試數據滿足ki = 1。
所有的測試點滿足N ≤ 200 000,M ≤ 200 000,0 ≤ A[i] ≤ 1 000 000,ki ≤ 5。
保證。
Hint
【樣例說明】
第一個詢問集合爲,第4小元素爲1。
總結
今天的題目難度適中,數據終於不水了。
得分:0+0+100=100
讀了一遍題,感覺T3最水,裸的主席樹,不切都不好意思了,於是開始剛。。。
先把原數列離散化,建樹時動態開點(詳見線段樹詳解),其中第 i 棵權值線段樹儲存中每一個數出現的次數。詢問時把第y棵線段樹減去第棵線段樹,再查詢。
10點左右,程序打完了,交了上去,不料跑了2min+都沒有跑完。
完美卡OJヾ(⌐■_■)ノ♕!
後來發現,可以一邊減一邊查詢,因爲我們只需要一條從根到底的路徑,而相減會得出許多無用的路徑。
然後就AC了。
接着打了1個多小時的T2暴力,卻連SPFA都沒有調出來,完美爆〇(後來發現是因爲我建邊時有下標爲0的邊,而遍歷一個點連接的邊時把這條邊判掉了)
正解
T1
不妨把無根樹轉化成以1爲根的樹。
設表示 i 所在的子樹含有0個黑色節點的最優解,表示 i 所在的子樹含有0個白點的最優解,表示 i 所在的子樹含有1個白色節點的最優解。
- 那麼當 i 爲黑色點時
- 當 i 爲白色點時
- 當 i 爲灰色點時
然後從底到根DP。
這樣子做似乎就可以了,但是
題目中有一句話:
對於 100% 的數據: 1 ≤ N ≤ 300 000
於是@#%#¥%@%@#*)¥~#¥@……
其實解決方法也很容易:BFS一遍,然後把隊列反過來就可以了。
T2
首先可以發現不在最短路上的點都是沒有用的。
於是只保留那些可以更新某個點的最短路的邊。
然後答案要求用最小的費用使1~n不連通,這就是最小割,即最大流。
於是答案就出來了,現在的關鍵就是判斷它是否唯一。
有一個不顯然的性質:
有向邊必定出現在任意最小割中當且僅當殘量網絡中存在S到u的路徑和v到T的路徑(此處的路徑指的是由沒有滿流的弧組成的路徑)。
那麼如果出現在任意最小割中的邊的原始容量之和不等於答案,方案就不唯一。
這裏可以從1和n分別做一次DFS,最後判斷一下就可以了。
特殊地,當一條邊的時,答案不唯一。
CODE
T1
#include<cstdio>
using namespace std;
#define ll long long
#define N 300005
#define M 1000005
#define inf 300000000000000
int fir[N],end[N<<1],nex[N<<1],a[N],data[N],fa[N];
ll f[N],g[N],h[N],cost[N<<1],temp;char ch;
inline ll min(ll x,ll y){return x<y?x:y;}
inline ll min(ll x,ll y,ll z){return x<z&&x<y?x:min(y,z);}
inline char gc()
{
static char buf[M],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,N,stdin),p1==p2)?EOF:*p1++;
}
inline void read(int &x)
{
while(ch=gc(),ch<'0'||ch>'9');x=ch-'0';
while(ch=gc(),ch>='0'&&ch<='9') x=x*10+ch-'0';
}
int main()
{
int t,n,k,i,l,x,y,z,m,head,tail;
read(t);
while(t--)
{
read(n),m=0;
for(i=1;i<=n;i++) read(a[i]),fir[i]=0;
for(i=1;i<n;i++)
{
read(x),read(y),read(z);
end[++m]=y,cost[m]=z,nex[m]=fir[x],fir[x]=m;
end[++m]=x,cost[m]=z,nex[m]=fir[y],fir[y]=m;
}
head=0,tail=1,data[1]=1;
while(head<tail)
{
k=data[++head];
for(i=fir[k];i;i=nex[i]) if(end[i]!=fa[k])
{
fa[end[i]]=k;
data[++tail]=end[i];
}
}
for(l=n;l;l--)
{
k=data[l];
if(!a[k])//black
{
f[k]=h[k]=inf,g[k]=0;
for(i=fir[k];i;i=nex[i])
if(end[i]!=fa[k])
g[k]+=min(f[end[i]]+cost[i],g[end[i]],h[end[i]]+cost[i]);
for(i=fir[k];i;i=nex[i])
if(end[i]!=fa[k])
h[k]=min(h[k],h[end[i]]+g[k]-min(f[end[i]]+cost[i],g[end[i]],h[end[i]]+cost[i]));
}
else if(a[k]<2)//white
{
f[k]=h[k]=0,g[k]=inf;
for(i=fir[k];i;i=nex[i]) if(end[i]!=fa[k])
{
f[k]+=min(f[end[i]],g[end[i]]+cost[i],h[end[i]]+cost[i]);
h[k]+=min(f[end[i]]+cost[i],g[end[i]],h[end[i]]+cost[i]);
}
}
else//gray
{
h[k]=inf,f[k]=g[k]=0;
for(i=fir[k];i;i=nex[i]) if(end[i]!=fa[k])
{
f[k]+=min(f[end[i]],g[end[i]]+cost[i],h[end[i]]+cost[i]);
g[k]+=min(f[end[i]]+cost[i],g[end[i]],h[end[i]]+cost[i]);
}
for(i=fir[k];i;i=nex[i])
if(end[i]!=fa[k])
h[k]=min(h[k],h[end[i]]+g[k]-min(f[end[i]]+cost[i],g[end[i]],h[end[i]]+cost[i]));
}
}
printf("%lld\n",min(f[1],g[1],h[1]));
}
return 0;
}
T2
#include<cstdio>
#include<cstring>
using namespace std;
#define ll long long
#define inf 400000000000
#define M 8005
#define N 405
struct EDGE{ll end,lenth,next;}edge[M];
ll n,m,s,sum,a[N],fir[N],lenth[M],end[M],nex[M],dis[N],GAP[N],data[200000];
bool exist[N],b[N],bz[N];
inline ll min(ll x,ll y){return x<y?x:y;}
inline void inc(ll x,ll y,ll z)
{
end[++s]=y,lenth[s]=z,nex[s]=fir[x],fir[x]=s;
end[++s]=x,lenth[s]=z,nex[s]=fir[y],fir[y]=s;
}
inline void add(ll x,ll y,ll z)
{
edge[++s]=(EDGE){y,z,fir[x]},fir[x]=s;
edge[++s]=(EDGE){x,0,fir[y]},fir[y]=s;
}
inline void spfa()
{
int i,head=0,tail=1,u,v;
for(i=2;i<=n;i++) dis[i]=inf,exist[i]=1;
data[1]=1,dis[1]=0;
while(head<tail)
{
u=data[++head],exist[u]=1;
for(i=fir[u];i;i=nex[i])
{
v=end[i];
if(dis[v]>dis[u]+lenth[i])
{
dis[v]=dis[u]+lenth[i];
if(exist[v]) exist[v]=0,data[++tail]=v;
}
}
}
}
ll sap(ll k,ll flow)
{
if(k==n) return flow;
ll i,j,t,have=flow;
for(i=fir[k];i;i=edge[i].next)
if(edge[i].lenth&&dis[k]==dis[j=edge[i].end]+1)
{
t=sap(edge[i].end,min(edge[i].lenth,have));
have-=t,edge[i].lenth-=t,edge[i^1].lenth+=t;
if(!have) return flow;
}
if(!(--GAP[dis[k]])) GAP[1]=n;
++GAP[++dis[k]];
return flow-have;
}
void dfs1(ll k)
{
b[k]=0;
for(ll i=fir[k];i;i=edge[i].next)
if(edge[i].lenth&&b[edge[i].end])
dfs1(edge[i].end);
}
void dfs2(ll k)
{
bz[k]=0;
for(ll i=fir[k];i;i=edge[i].next)
if(edge[i^1].lenth&&bz[edge[i].end])
dfs2(edge[i].end);
}
int main()
{
ll task,i,j,x,y,z,ans=0;
scanf("%lld",&task);
while(task--)
{
scanf("%lld%lld",&n,&m);
for(i=1;i<n;i++) scanf("%lld",a+i),fir[i]=0;
a[n]=inf,fir[n]=0,s=1;
for(i=1;i<=m;i++)
{
scanf("%lld%lld%lld",&x,&y,&z);
inc(x,y,z);
}
spfa();
memset(fir,0,sizeof(fir));
for(i=s,s=1;i>1;i--)
if(dis[end[i]]+lenth[i]==dis[end[i^1]])
add(end[i],end[i^1],min(a[end[i]],a[end[i^1]]));
memset(dis,0,sizeof(dis));
memset(GAP,0,sizeof(GAP)),ans=0;
while(dis[1]<n) ans+=sap(1,inf);
memset(b,1,sizeof(b));
memset(bz,1,sizeof(bz));
dfs1(1),dfs2(n);
for(sum=0,i=2;i<=s;i+=2)
if((!b[edge[i^1].end])&&(!bz[edge[i].end]))
{
if(a[edge[i^1].end]==a[edge[i].end])
{sum=0;break;}
sum+=min(a[edge[i^1].end],a[edge[i].end]);
}
if(sum==ans) printf("Yes %lld\n",ans);
else printf("No %lld\n",ans);
}
return 0;
}
T3
#include<cstdio>
#include<algorithm>
using namespace std;
#define M 7200005
#define N 200005
struct number
{
int num,id;
}rec[N];
struct node
{
int sum,lson,rson;
node(){sum=lson=rson=0;}
}a[M];
int cnt,num[N],b[N],x[10],y[10];char ch;
inline char gc()
{
static char buf[M],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,N,stdin),p1==p2)?EOF:*p1++;
}
inline void read(int &x)
{
while(ch=gc(),ch<'0'||ch>'9');x=ch-'0';
while(ch=gc(),ch>='0'&&ch<='9') x=x*10+ch-'0';
}
bool cmp(number x,number y){return x.num<y.num;}
void add(int x,int y,int l,int r,int num)
{
a[x].sum=a[y].sum+1;
if(l==r) return;
int mid=l+r>>1;
if(num<=mid)
{
a[x].rson=a[y].rson,a[x].lson=++cnt;
add(a[x].lson,a[y].lson,l,mid,num);
}
else
{
a[x].lson=a[y].lson,a[x].rson=++cnt;
add(a[x].rson,a[y].rson,mid+1,r,num);
}
}
int query(int k,int l,int r,int num)
{
if(l==r) return l;
int mid=l+r>>1,i,s=0;
for(i=1;i<=k;i++) s+=a[a[y[i]].lson].sum-a[a[x[i]].lson].sum;
if(s>=num)
{
for(i=1;i<=k;i++) x[i]=a[x[i]].lson,y[i]=a[y[i]].lson;
query(k,l,mid,num);
}
else
{
for(i=1;i<=k;i++) x[i]=a[x[i]].rson,y[i]=a[y[i]].rson;
query(k,mid+1,r,num-s);
}
}
int main()
{
int n,m,i,j,k,t;
read(n),read(m),cnt=n+1;
for(i=1;i<=n;i++) read(rec[i].num),rec[i].id=i;
sort(rec+1,rec+n+1,cmp),rec[0].num=-1;
for(i=1,j=0;i<=n;i++)
{
if(rec[i].num>rec[i-1].num) b[++j]=rec[i].num;
num[rec[i].id]=j;
}
for(i=1;i<=n;i++) add(i,i-1,1,n,num[i]);
while(m--)
{
read(j),read(k);
for(i=1;i<=j;i++)
{
read(x[i]),read(y[i]),x[i]--;
if(x[i]>=y[i]) i--,j--;
}
printf("%d\n",b[query(j,1,n,k)]);
}
return 0;
}