NOI2018部分題解
T1:歸程
解法一:
1.首先想到離線做法:將邊和詢問從大到小排序,並查集維護連通塊以及每個連通塊中所有點到1號點的最短距離。$O(n\log n)$
配合暴力等可以拿到75分。
2.很容易想到在線做法,使用可持久化並查集,詢問時二分即可。
不能使用路徑壓縮,應該按秩合並,註意秩是樹的深度而不是大小。$O((E+Q)\log^2 N)$
由於常數過大,基本過不去。
3.考慮優化算法二,發現訪問歷史版本並不需要修改而只需要詢問,所以一開始只使用普通的並查集,用可持久化數組記錄並查集的修改情況。
$O((N+E)\log N+Q\log^2 N)$,卡時通過。
4.算法三的復雜度已經難以優化,考慮優化常數。不需要可持久化數據結構,直接對並查集的每個點用vector存下修改情況,詢問時二分即可。
$O(n\log^2 n)$,常數很小,輕松通過。
解法二:
考慮Kruskal重構樹,對海拔跑一次Kruskal同時對每條邊新建一個節點,權值為邊的海拔,並對每個點存下重構樹的子樹中到1號點的最小值。
問題實際上是求一個點能通過走海拔不低於某個值的邊到達的點中離1好號點最近的距離,也就是重構樹上點權大於某個值的節點的子樹中到1號點的最小值。倍增查詢即可。
1 #include<cstdio> 2 #include<queue> 375分#include<cstring> 4 #include<algorithm> 5 #define rep(i,l,r) for (int i=(l); i<=(r); i++) 6 using namespace std; 7 8 const int N=1000010,inf=2000000000; 9 bool b[N]; 10 int n,m,T,q,K,S,v,p,cnt,Ans,mn[N],fa[N],ans[N],to[N],nxt[N],dis[N],val[N],h[N]; 11 struct E{ int u,v,l,a; }e[N]; 12struct Que{ int v,p,id; }que[N]; 13 struct P{ int x,d; }; 14 bool operator <(const P &a,const P &b){ return a.d>b.d; } 15 priority_queue<P>Q; 16 17 void add(int u,int v,int w){ to[++cnt]=v; val[cnt]=w; nxt[cnt]=h[u]; h[u]=cnt; } 18 int get(int x){ return (fa[x]==x) ? x : fa[x]=get(fa[x]); } 19 20 bool cmp(E a,E b){ return a.a>b.a; } 21 bool cmp1(Que a,Que b){ return a.p>b.p; } 22 23 void Dij(){ 24 rep(i,1,n) dis[i]=inf,b[i]=0; dis[1]=0; 25 Q.push((P){1,0}); 26 while (!Q.empty()){ 27 int x=Q.top().x; Q.pop(); 28 if (b[x]) continue; 29 b[x]=1; 30 for (int i=h[x],k; i; i=nxt[i]) 31 if (!b[k=to[i]] && dis[k]>dis[x]+val[i]) 32 Q.push((P){k,dis[k]=dis[x]+val[i]}); 33 } 34 } 35 36 int main(){ 37 freopen("return.in","r",stdin); 38 freopen("return.out","w",stdout); 39 for (scanf("%d",&T); T--; ){ 40 scanf("%d%d",&n,&m); 41 rep(i,0,n) h[i]=0; cnt=0; Ans=0; 42 rep(i,1,m){ 43 scanf("%d %d %d %d",&e[i].u,&e[i].v,&e[i].l,&e[i].a); 44 add(e[i].u,e[i].v,e[i].l); add(e[i].v,e[i].u,e[i].l); 45 } 46 sort(e+1,e+m+1,cmp); Dij(); 47 scanf("%d%d%d",&q,&K,&S); 48 if (K){ 49 rep(i,1,q){ 50 rep(i,1,n) mn[i]=dis[i],fa[i]=i; 51 scanf("%d%d",&v,&p); 52 v=(v+K*Ans-1)%n+1; p=(p+K*Ans)%(S+1); int st=1; 53 while (e[st].a>p && st<=m){ 54 int u=get(e[st].u),v=get(e[st].v); 55 if (u!=v){ fa[u]=v; mn[v]=min(mn[v],mn[u]); } 56 st++; 57 } 58 printf("%d\n",Ans=mn[get(v)]); 59 } 60 continue; 61 } 62 rep(i,1,q) scanf("%d%d",&que[i].v,&que[i].p),que[i].id=i; 63 sort(que+1,que+q+1,cmp1); int st=1; 64 rep(i,1,n) mn[i]=dis[i],fa[i]=i; 65 rep(i,1,q){ 66 while (e[st].a>que[i].p && st<=m){ 67 int u=get(e[st].u),v=get(e[st].v); 68 if (u!=v){ fa[u]=v; mn[v]=min(mn[v],mn[u]); } 69 st++; 70 } 71 ans[que[i].id]=mn[get(que[i].v)]; 72 } 73 rep(i,1,q) printf("%d\n",ans[i]); 74 } 75 return 0; 76 }
1 #include<queue> 2 #include<cstdio> 3 #include<vector> 4 #include<algorithm> 5 #define pb push_back 6 #define rep(i,l,r) for (int i=(l); i<=(r); i++) 7 using namespace std; 8 9 const int N=400010,inf=2000000000; 10 bool b[N]; 11 int n,m,q,k,s,T,ans,cnt,dis[N],fa[N],mn[N],h[N],dep[N],to[N<<1],val[N<<1],nxt[N<<1]; 12 struct E{ int u,v,l,a; }e[N]; 13 struct S{ int p,v; }; 14 struct P{ int x,d; }; 15 bool operator <(const E &a,const E &b){ return a.a>b.a; } 16 bool operator <(const P &a,const P &b){ return a.d>b.d; } 17 bool operator <(const S &a,const S &b){ return a.p>b.p; } 18 vector<S>Fa[N],Mn[N]; 19 priority_queue<P>Q; 20 21 void add(int u,int v,int w){ to[++cnt]=v; val[cnt]=w; nxt[cnt]=h[u]; h[u]=cnt; } 22 int find(int x){ return (x==fa[x]) ? x : find(fa[x]); } 23 24 void Dij(){ 25 rep(i,1,n) dis[i]=inf,b[i]=0; dis[1]=0; Q.push((P){1,0}); 26 while (!Q.empty()){ 27 int x=Q.top().x; Q.pop(); 28 if (b[x]) continue; 29 b[x]=1; 30 for (int i=h[x],k; i; i=nxt[i]) 31 if (!b[k=to[i]] && dis[k]>dis[x]+val[i]) 32 Q.push((P){k,dis[k]=dis[x]+val[i]}); 33 } 34 } 35 36 void init(){ 37 cnt=0; while (!Q.empty()) Q.pop(); 38 rep(i,1,n) h[i]=0,Fa[i].clear(),Mn[i].clear(); 39 } 40 41 void work(){ 42 scanf("%d%d",&n,&m); 43 rep(i,1,m){ 44 scanf("%d%d%d%d",&e[i].u,&e[i].v,&e[i].l,&e[i].a); 45 add(e[i].u,e[i].v,e[i].l); add(e[i].v,e[i].u,e[i].l); 46 } 47 Dij(); sort(e+1,e+m+1); 48 rep(i,1,n) Fa[i].pb((S){inf,fa[i]=i}),Mn[i].pb((S){inf,mn[i]=dis[i]}),dep[i]=1; 49 rep(i,1,m){ 50 int p=e[i].a,u=find(e[i].u),v=find(e[i].v); 51 if (u==v) continue; 52 if (dep[u]>dep[v]) swap(u,v); 53 fa[u]=v; dep[v]=max(dep[u]+1,dep[v]); mn[v]=min(mn[v],mn[u]); 54 Fa[u].pb((S){p,fa[u]}); Mn[v].pb((S){p,mn[v]}); 55 } 56 scanf("%d%d%d",&q,&k,&s); ans=0; 57 rep(i,1,q){ 58 int v,p; scanf("%d%d",&v,&p); v=(v+k*ans-1)%n+1; p=(p+k*ans)%(s+1); 59 for (int f; v!=(f=(--lower_bound(Fa[v].begin(),Fa[v].end(),(S){p,0}))->v); v=f); 60 printf("%d\n",ans=(--lower_bound(Mn[v].begin(),Mn[v].end(),(S){p,0}))->v); 61 } 62 } 63 64 int main(){ 65 freopen("return.in","r",stdin); 66 freopen("return.out","w",stdout); 67 for (scanf("%d",&T); T--; ) init(),work(); 68 return 0; 69 }解法一
1 #include<queue> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #define rep(i,l,r) for (int i=(l); i<=(r); i++) 6 using namespace std; 7 8 const int N=400010,inf=2000000000; 9 bool b[N]; 10 int n,m,q,K,v,p,S,T,ans,tot,cnt,dis[N],fa[N],s[N],mn[N],h[N],Fa[N][21],to[N<<1],val[N<<1],nxt[N<<1]; 11 struct E{ int u,v,l,a; }e[N]; 12 struct P{ int x,d; }; 13 bool operator <(const P &a,const P &b){ return a.d>b.d; } 14 bool operator <(const E &a,const E &b){ return a.a>b.a; } 15 priority_queue<P>Q; 16 17 void add(int u,int v,int w){ to[++cnt]=v; val[cnt]=w; nxt[cnt]=h[u]; h[u]=cnt; } 18 int find(int x){ return (x==fa[x]) ? x : fa[x]=find(fa[x]); } 19 20 void Dij(){ 21 rep(i,1,n) dis[i]=inf,b[i]=0; dis[1]=0; Q.push((P){1,0}); 22 while (!Q.empty()){ 23 int x=Q.top().x; Q.pop(); 24 if (b[x]) continue; 25 b[x]=1; 26 for (int i=h[x],k; i; i=nxt[i]) 27 if (!b[k=to[i]] && dis[k]>dis[x]+val[i]) 28 Q.push((P){k,dis[k]=dis[x]+val[i]}); 29 } 30 } 31 32 void dfs(int x){ 33 if (x>n) mn[x]=inf; else mn[x]=dis[x]; 34 for (int i=h[x],k; i; i=nxt[i]) 35 Fa[k=to[i]][0]=x,dfs(k),mn[x]=min(mn[x],mn[k]); 36 } 37 38 void Kruskal(){ 39 rep(i,1,n) h[i]=0; cnt=0; tot=n; 40 rep(i,1,n) fa[i]=i; 41 rep(i,1,m){ 42 int u=find(e[i].u),v=find(e[i].v); 43 if (u==v) continue; 44 s[++tot]=e[i].a; fa[u]=fa[v]=fa[tot]=tot; 45 add(tot,u,0); add(tot,v,0); 46 } 47 dfs(tot); 48 } 49 50 void work(){ 51 scanf("%d%d",&n,&m); 52 rep(i,1,m){ 53 scanf("%d%d%d%d",&e[i].u,&e[i].v,&e[i].l,&e[i].a); 54 add(e[i].u,e[i].v,e[i].l); add(e[i].v,e[i].u,e[i].l); 55 } 56 Dij(); sort(e+1,e+m+1); Kruskal(); 57 rep(i,1,20) rep(x,1,tot) Fa[x][i]=Fa[Fa[x][i-1]][i-1]; 58 scanf("%d%d%d",&q,&K,&S); ans=0; 59 while (q--){ 60 scanf("%d%d",&v,&p); 61 v=(v+K*ans-1)%n+1; p=(p+K*ans)%(S+1); 62 for (int i=20; ~i; i--) if (Fa[v][i] && s[Fa[v][i]]>p) v=Fa[v][i]; 63 printf("%d\n",ans=mn[v]); 64 } 65 } 66 67 void init(){ cnt=0; memset(Fa,0,sizeof(Fa)); memset(h,0,sizeof(h)); } 68 69 int main(){ 70 freopen("return.in","r",stdin); 71 freopen("return.out","w",stdout); 72 for (scanf("%d",&T); T--; ) init(),work(); 73 return 0; 74 }解法二
T4:屠龍勇士
首先很明顯劍的選擇是唯一的,直接用multiset即可。
接下來可以發現每條龍都是一個模線性方程。設攻擊第i條龍的劍的攻擊力為$s_i$,則$s_ix\equiv a_i\ (mod\ p_i)$。
現在需要將方程化成$x\equiv c_i\ (mod\ m_i)$的形式,從而使用exCRT解決。
變式:$s_ix+p_iy=a_i$,先同除以$gcd(s_i,p_i)$,再使用exgcd解不定方程,求x的最小正整數解。
註意判無解,exCRT結束之後註意要使$x\geqslant max(\frac{a_i}{s_i})$
1 #include<set> 2 #include<cstdio> 3 #include<algorithm> 4 #define rep(i,l,r) for (int i=(l); i<=(r); i++) 5 typedef long long ll; 6 using namespace std; 7 8 const int N=100010; 9 multiset<ll>S; 10 bool mark; 11 int n,k,T; 12 ll mx,a[N],p[N],more[N],s[N],c[N],m[N]; 13 14 ll gcd(ll a,ll b){ return b ? gcd(b,a%b) : a; } 15 16 ll mul(ll a,ll b,ll mod){ 17 ll res=0; a%=mod; b%=mod; 18 for (; b; a=(a<<1)%mod,b>>=1) 19 if (b & 1) res=(res+a)%mod; 20 return res; 21 } 22 23 void exgcd(ll a,ll b,ll &x,ll &y){ 24 if (!b){ x=1; y=0; return; } 25 exgcd(b,a%b,y,x); y-=(a/b)*x; 26 } 27 28 ll inv(ll a,ll b){ ll x,y; exgcd(a,b,x,y); x=(x%b+b)%b; return x; } 29 30 bool merge(ll c1,ll c2,ll m1,ll m2,ll &c3,ll &m3){ 31 ll d=gcd(m1,m2); m3=m1/d*m2; 32 if (c2<c1) swap(c1,c2),swap(m1,m2); 33 if ((c2-c1)%d) return 0; 34 c3=(mul(mul(inv(m1/d,m2/d),(c2-c1)/d,m2/d),m1,m3)+c1)%m3; 35 return 1; 36 } 37 38 void work(){ 39 scanf("%d%d",&n,&k); mark=1; mx=0; S.clear(); 40 rep(i,1,n) scanf("%lld",&a[i]); 41 rep(i,1,n) scanf("%lld",&p[i]); 42 rep(i,1,n) scanf("%lld",&more[i]); 43 rep(i,1,k) scanf("%lld",&s[i]),S.insert(s[i]); 44 rep(i,1,n){ 45 multiset<ll>::iterator it=S.upper_bound(a[i]); 46 if (it!=S.begin()) it--; 47 ll s=*it; S.erase(it); S.insert(more[i]); 48 mx=max(mx,(a[i]-1)/s+1); 49 ll d=gcd(s,p[i]); 50 if (a[i]%d) { mark=0; break; } 51 m[i]=p[i]/d; c[i]=mul(inv(s/d,m[i]),a[i]/d,m[i]); 52 } 53 if (!mark) { puts("-1"); return; } 54 rep(i,2,n) if (!merge(c[i-1],c[i],m[i-1],m[i],c[i],m[i])) { mark=0; break; } 55 if (!mark) { puts("-1"); return; } 56 if (c[n]<mx) c[n]+=((mx-c[n]-1)/m[n]+1)*m[n]; 57 printf("%lld\n",c[n]); 58 } 59 60 int main(){ 61 freopen("dragon.in","r",stdin); 62 freopen("dragon.out","w",stdout); 63 for (scanf("%d",&T); T--; ) work(); 64 return 0; 65 }T4
NOI2018部分題解