比賽鏈接:
http://acm.hdu.edu.cn/search.php?field=problem&key=2019+Multi-University+Training+Contest+1&source=1&searchmode=source
hdu6582
題意:
刪除某些邊,讓$1$到$n$的最短路徑發生變化
刪除某條邊的費用是邊的長度
分析:
先用迪傑斯特拉跑一遍整個圖,滿足$dis[a]+w=dis[b]$的邊,肯定是最短路徑上的邊
選出這些邊,找到一個最小割集,Dinic比EK快很多,雖然漸進復雜度相同,都是$O(nm^2)$
Dinic加上弧優化后速度更快
ac代碼(Dinic沒有弧優化):
#include<bits/stdc++.h> #define ll long long #define ull unsigned long long #define pa pair<ll,ll> using namespace std; const ll maxn=1e4+5; const ll maxm=1e7+10; const ll mod=1e9+7; vector<pair<int,int>>ve[maxn]; ll dis[maxn]; int n,m,edge_num=-1,head[maxn],dep[maxn]; bool vis[maxn]; struct Edge{ int to,nex,v; }edge[maxn*2];//maxn=1e4 void add_edge(int a,int b,int c)//邊數從0開始,每次加上反向邊,這樣奇偶交替加邊,重邊無所謂 { edge[++edge_num].to=b; edge[edge_num].nex=head[a]; edge[edge_num].v=c; head[a]=edge_num; } bool bfs() { for(int i=1;i<=n;i++)dep[i]=1e9; dep[1]=0; queue<int>que; que.push(1); while(que.size()){ int now=que.front(); que.pop(); for(int i=head[now];i!=-1;i=edge[i].nex){ int to=edge[i].to; int v=edge[i].v; if(v&&dep[to]>dep[now]+1){ dep[to]=dep[now]+1; que.push(to); } } } if(dep[n]==1e9)return false; else return true; } int dfs(int x,int lowflow) { if(x==n||lowflow==0)return lowflow; int reslow=0; int used=0; for(int i=head[x];i!=-1;i=edge[i].nex){ int to=edge[i].to; if(edge[i].v&&dep[x]+1==dep[to]){ if(reslow=dfs(to,min(lowflow,edge[i].v))){ edge[i].v-=reslow; edge[i^1].v+=reslow; used+=reslow; lowflow-=reslow; if(lowflow<=0)break; } } } return used; } void Dinic() { ll maxflow=0; int lowflow; while(bfs()){ while(lowflow=dfs(1,1e9)){ maxflow+=lowflow; } } printf("%lld\n",maxflow); } void dj() { for(int i=1;i<=n;i++)dis[i]=1e18,vis[i]=false; dis[1]=0; priority_queue<pair<ll,int>>que; que.push(make_pair(0,1)); while(que.size()){ pair<ll,int>now=que.top(); que.pop(); if(vis[now.second])continue; vis[now.second]=true; for(int i=0;i<ve[now.second].size();i++){ int to=ve[now.second][i].first; int w=ve[now.second][i].second; if(dis[to]>dis[now.second]+w){ dis[to]=dis[now.second]+w; que.push(make_pair(-dis[to],to)); } } } } int main() { // freopen("D:/a.txt","w",stdout); // freopen("D:/2.txt","r",stdin); int T; scanf("%d",&T); while(T--){ scanf("%d %d",&n,&m); for(int i=1;i<=m;i++){ int a,b,c; scanf("%d %d %d",&a,&b,&c); ve[a].push_back(make_pair(b,c)); } dj(); if(dis[n]==1e18){ printf("0\n"); continue; } for(int i=1;i<=n;i++)head[i]=-1; edge_num=-1; for(int i=1;i<=n;i++){ for(int j=0;j<ve[i].size();j++){ int a=i,b=ve[i][j].first,c=ve[i][j].second; if(dis[a]+c==dis[b])add_edge(a,b,c),add_edge(b,a,0); } } Dinic(); for(int i=1;i<=n;i++)ve[i].clear(); } return 0; }
ac代碼(Dinic弧優化):
#include<bits/stdc++.h> #define ll long long #define ull unsigned long long #define pa pair<ll,ll> using namespace std; const ll maxn=1e4+5; const ll maxm=1e7+10; const ll mod=1e9+7; vector<pair<int,int>>ve[maxn]; ll dis[maxn]; int n,m,edge_num=-1,head[maxn],dep[maxn],cur[maxn]; bool vis[maxn]; struct Edge{ int to,nex,v; }edge[maxn*2];//maxn=1e4 void add_edge(int a,int b,int c)//邊數從0開始,每次加上反向邊,這樣奇偶交替加邊,重邊無所謂 { edge[++edge_num].to=b; edge[edge_num].nex=head[a]; edge[edge_num].v=c; head[a]=edge_num; } bool bfs()//分層 { for(int i=1;i<=n;i++)dep[i]=1e9; dep[1]=0; queue<int>que; que.push(1); while(que.size()){ int now=que.front(); que.pop(); for(int i=head[now];i!=-1;i=edge[i].nex){ int to=edge[i].to; int v=edge[i].v; if(v&&dep[to]>dep[now]+1){ dep[to]=dep[now]+1; que.push(to); } } } if(dep[n]==1e9)return false; else return true; } int dfs(int x,int lowflow) { if(x==n||lowflow==0)return lowflow; int reslow=0; int used=0; for(int &i=cur[x];i!=-1;i=edge[i].nex){ int to=edge[i].to; if(edge[i].v&&dep[x]+1==dep[to]){ if(reslow=dfs(to,min(lowflow,edge[i].v))){ edge[i].v-=reslow; edge[i^1].v+=reslow; used+=reslow; lowflow-=reslow; if(lowflow<=0)break; } } } return used; } void Dinic() { ll maxflow=0; int lowflow; while(bfs()){ for(int i=1;i<=n;i++)cur[i]=head[i]; while(lowflow=dfs(1,1e9)){ maxflow+=lowflow; } } printf("%lld\n",maxflow); } void dj() { for(int i=1;i<=n;i++)dis[i]=1e18,vis[i]=false; dis[1]=0; priority_queue<pair<ll,int>>que; que.push(make_pair(0,1)); while(que.size()){ pair<ll,int>now=que.top(); que.pop(); if(vis[now.second])continue; vis[now.second]=true; for(int i=0;i<ve[now.second].size();i++){ int to=ve[now.second][i].first; int w=ve[now.second][i].second; if(dis[to]>dis[now.second]+w){ dis[to]=dis[now.second]+w; que.push(make_pair(-dis[to],to)); } } } } int main() { // freopen("D:/a.txt","w",stdout); // freopen("D:/2.txt","r",stdin); int T; scanf("%d",&T); while(T--){ scanf("%d %d",&n,&m); for(int i=1;i<=m;i++){ int a,b,c; scanf("%d %d %d",&a,&b,&c); ve[a].push_back(make_pair(b,c)); } dj(); if(dis[n]==1e18){ printf("0\n"); continue; } for(int i=1;i<=n;i++)head[i]=-1; edge_num=-1; for(int i=1;i<=n;i++){ for(int j=0;j<ve[i].size();j++){ int a=i,b=ve[i][j].first,c=ve[i][j].second; if(dis[a]+c==dis[b])add_edge(a,b,c),add_edge(b,a,0); } } Dinic(); for(int i=1;i<=n;i++)ve[i].clear(); } return 0; }
hdu6581
題意:
給出車子的位置,長度,最大速度,不能超車,求最后面車子的最快通過時間
分析:
對於時間$t$,最前面的車因為沒有阻擋,可以直接得到它的位置,第二輛車,要么粘在上一輛車后面,要么以自己的速度運行$t$時間
對時間二分,可以得到每輛車的位置,驗證最后一輛車的位置即可
ac代碼:
#include<bits/stdc++.h> #define ll long long #define ull unsigned long long #define pa pair<ll,ll> using namespace std; const ll maxn=1e5+5; const ll maxm=1e7+10; const ll mod=1e9+7; double pos[maxn]; int s[maxn],l[maxn],v[maxn],n; bool check(double t) { pos[n]=s[n]-t*v[n]; for(int i=n-1;i>=0;i--){ pos[i]=max(s[i]-t*v[i],pos[i+1]+l[i+1]); } if(pos[0]<0)return true; return false; } int main() { while(scanf("%d",&n)==1){ for(int i=0;i<=n;i++)scanf("%d",&l[i]); for(int i=0;i<=n;i++)scanf("%d",&s[i]); for(int i=0;i<=n;i++)scanf("%d",&v[i]); double st=0,en=1e9; for(int i=1;i<=100;i++){ double md=(st+en)/2; if(check(md))en=md; else st=md; } printf("%.10f\n",st); } return 0; }
hdu6579
題意:
有兩種操作,1,在數組最后添加一位,2,求區間的最大異或和
分析:
如果對每個區間求一次線性基,肯定超時
我們可以求出一個前綴線性基,不同的是,如果$b[i]$(線性基數組)位置已經有一個數,我們把位置大的數放進去,並且拿出$b[i]$
對於每次詢問,我們只需要看這個基底位置是不是大於$l$即可
ac代碼:
#include<bits/stdc++.h> #define ll long long #define PI acos(-1.0) #define pa pair<int,int> using namespace std; const int maxn=1e6+10; const ll mod=1e9+7; pa b[31],base[maxn][31]; void add(int x,int pos) { for(int i=30;i>=1;i--){ if(x&(1<<(i-1))){ if(b[i].first==0){ b[i].first=x; b[i].second=pos; break; } if(pos>b[i].second){//核心代碼,每次保留位置最大的基 swap(pos,b[i].second); swap(x,b[i].first); } x^=b[i].first; } } } int main() { int T; scanf("%d",&T); while(T--){ for(int i=1;i<=30;i++)b[i]=make_pair(0,0); int n,m; scanf("%d %d",&n,&m); for(int i=1;i<=n;i++){ int x; scanf("%d",&x); add(x,i); for(int j=1;j<=30;j++)base[i][j]=b[j]; } int lastan=0; for(int i=1;i<=m;i++){ int comd,l,r; scanf("%d",&comd); if(comd){ scanf("%d",&l); l^=lastan; add(l,++n); for(int j=1;j<=30;j++)base[n][j]=b[j]; }else{ scanf("%d %d",&l,&r); l=(l^lastan)%n+1,r=(r^lastan)%n+1; if(l>r)swap(l,r); lastan=0; for(int j=30;j>=1;j--) if(base[r][j].second>=l) lastan=max(lastan,lastan^base[r][j].first); printf("%d\n",lastan); } } } return 0; }