Linkernode
總分 : 100 + 100 + 40 = 240c++
結論題。不管如何神J都會贏。spa
最優決策:神樹變化了我就不變,神樹不變我就變化。debug
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int mod=998244353; inline ll POW(ll pre, int x){ ll res=1; for(; x; x>>=1,pre=pre*pre%mod) if(x&1) res=res*pre%mod; return res; } int n; int main(){ scanf("%d",&n); printf("%lld\n",POW(2,n)); return 0; }
狀壓DP水題。code
暴力都沒有去切,由於k過小了,直接狀壓走過那些邊便可。ip
//代碼有點醜,可是考試的時候挺快就敲出來了。 #include<bits/stdc++.h> #define re register #define rep(i,a,b) for(re int i=a,i##end=b; i<=i##end; i++) #define drep(i,a,b) for(re int i=a,i##end=b; i>=i##end; i--) #define repp(i,a,b) for(re int i=a,i##end=b; i<i##end; i++) #define drepp(i,a,b) for(re int i=a,i##end=b; i>i##end; i--) #define Erep(i,x) for(re int i=head[x]; i; i=Edge[i].nxt) #define lowbit(x) ((x)&-(x)) #define debug(x) cerr<<#x<<" = "<<x<<endl #define ms(x,a) memset(x,a,sizeof x) #define PII pair<int,int> #define PLL pair<ll,ll> #define fi first #define se second #define coint const int #define coll const ll #define CM cerr<<(&S2-&S1)/1024./1024<<"MB"<<endl typedef long long ll; using namespace std; template<class T>inline T rd(){ static char ch;static bool neg;static T x; for(ch=0, neg=0; ch>'9'||ch<'0'; neg|=(ch=='-'),ch=getchar()); for(x=0; ch>='0'&&ch<='9'; x=(x<<1)+(x<<3)+(ch^'0'),ch=getchar()); return neg?-x:x; } template<class T>inline T Max(const T &x, const T &y) { return x>y?x:y; } template<class T>inline T Min(const T &x, const T &y) { return x<y?x:y; } bool S1; coint N=50000+5,M=200000+5,K=12+5; struct edge{ int to,val,nxt; }Edge[M<<1]; int head[N],tcnt; inline void AddEdge(coint u, coint v, coint w){ Edge[++tcnt]=(edge)<%v,w,head[u]%>; head[u]=tcnt; return; } struct edge2{ int to,val,id; }; vector<edge2>G[N]; vector<int>vec; int n,m,k; int mark[N],ID[N]; ll dis[K<<1][N]; ll dp[(1<<K)|5][K<<1]; struct node{ int x; ll val; bool operator < (const node &_) const { return val>_.val; } }; struct Heap{ node sum[N+M]; int sz; inline void pop(){ sum[1]=sum[sz--]; int now=1,nxt; while(nxt=now<<1,nxt<=sz){ if(nxt<sz && sum[nxt]<sum[nxt|1]) nxt|=1; if(sum[now]<sum[nxt]) swap(sum[nxt],sum[now]),now=nxt; else break; } return; } inline void push(node x){ sum[++sz]=x; int now=sz,nxt; while(nxt=now>>1,nxt){ if(sum[nxt]<sum[now]) swap(sum[now],sum[nxt]),now=nxt; else break; } return; } inline bool empty() { return !sz; } inline void clear() { sz=0; return; } inline int size() { return sz; } inline node top() { return sum[1]; } }Q; //priority_queue<node>Q; inline void Dijkstra(coint id, coint st){ Q.clear(); Q.push((node)<%st,0%>); dis[id][st]=0; while(!Q.empty()){ node now=Q.top(); Q.pop(); if(mark[now.x]==id) continue; mark[now.x]=id; Erep(i,now.x){ edge y=Edge[i]; coll res=y.val+now.val; if(res<dis[id][y.to]){ dis[id][y.to]=res; Q.push((node)<%y.to,res%>); } } } // rep(i,1,n) printf("%d -> %d : %lld\n",st,i,dis[id][i]); return; } bool S2; int main(){ // CM; // freopen("test.in","r",stdin); // freopen("test.out","w",stdout); n=rd<int>(),m=rd<int>(),k=rd<int>(); rep(i,1,m){ coint u=rd<int>(),v=rd<int>(),w=rd<int>(); AddEdge(u,v,w); AddEdge(v,u,w); if(i<=k){ G[u].push_back((edge2)<%v,w,i%>); if(u!=v) G[v].push_back((edge2)<%u,w,i%>); mark[u]=mark[v]=1; } } mark[1]=1; rep(i,1,n) if(mark[i]) vec.push_back((int)i),ID[i]=((int)vec.size())-1; ms(mark,-1); ms(dis,0x3f); repp(i,0,vec.size()) Dijkstra(i,vec[i]); ms(dp,0x3f); coll INF=dp[0][0]; dp[0][0]=0; repp(sta,0,(1<<k)-1){ repp(i,0,vec.size()){ repp(j,0,vec.size()){ dp[sta][j]=Min(dp[sta][j],dp[sta][i]+dis[i][vec[j]]); } } repp(i,0,vec.size()){ if(dp[sta][i]>=INF) continue; int at=vec[i]; repp(j,0,(int)G[at].size()){ coint to=G[at][j].to,val=G[at][j].val,id=G[at][j].id; if(sta&(1<<id>>1)) continue; dp[sta|(1<<id>>1)][ID[to]]=Min(dp[sta|(1<<id>>1)][ID[to]],dp[sta][i]+val); } } } ll ans=INF; repp(i,0,vec.size()) ans=Min(ans,dp[(1<<k)-1][i]+dis[i][1]); printf("%lld\n",ans); return 0; }
(pre_dfs與dfs_top是樹鏈剖分的部分。)get
直接暴力\(O(n^3)\)處理處每兩個點間最小Dis值it
struct P30{ static coint N=300+5; ll Dis[N][N]; inline void solve(){ pre_dfs(1,0); dfs_top(1,0,1); rep(i,1,n){ int x=i; while(x) Dis[x][i]=x*(dis[i]-dis[x]),x=f[x]; } rep(i,1,n){ int x=i; while(x){ int y=x; while(y){ Dis[y][i]=Min(Dis[y][i],Dis[y][x]+Dis[x][i]); y=f[y]; } x=f[x]; } } rep(i,1,m){ int s=rd<int>(),t=rd<int>(); if(LCA(s,t)!=s) { puts("-1"); continue; } printf("%lld\n",Dis[s][t]); } return; } }p30;
能夠發現詢問數量很小,咱們直接對於每一個詢問暴力向上跳,而咱們總共就會處理\(n^2\)個點之間的距離,中間重複處理的還能夠省去,所以總複雜度不到\(O(n^2)\)io
struct P40{ static coint N=3000+5; ll Dis[N][N]; bool mark[N][N]; inline void solve(){ pre_dfs(1,0); dfs_top(1,0,1); rep(i,1,n){ Dis[i][i]=0; int x=f[i]; while(x) Dis[x][i]=x*(dis[i]-dis[x]),x=f[x]; } rep(i,1,m){ int s=rd<int>(),t=rd<int>(); if(LCA(s,t)!=s) { puts("-1"); continue; } int x=t,fa=f[s]; if(!mark[s][t]) while(x!=fa){ if(mark[x][t]) { x=f[x]; continue; } int y=t; while(y!=x){ Dis[x][t]=Min(Dis[x][t],Dis[x][y]+Dis[y][t]); y=f[y]; } mark[x][t]=1; x=f[x]; } printf("%lld\n",Dis[s][t]); } return; } }p40;
能夠發如今向下跳的過程當中,咱們通過的點是單調遞減的,所以利用倍增思想,記錄下一個走到的點,以及其花費,而後倍增處理處便可。class
struct P_Chain{ ll dis[N]; ll Dis[20][N]; int tp[20][N]; int dep[N],f[N]; void dfs(coint x, coint fa){ Erep(i,x){ edge y=Edge[i]; if(y.to==fa) continue; dep[y.to]=dep[x]+1; f[y.to]=x; dis[y.to]=dis[x]+y.val; dfs(y.to,x); if(y.to<x) tp[0][x]=y.to; else{ int z=tp[0][y.to]; while(z>=x && z) z=tp[0][z]; if(z) tp[0][x]=z; } if(tp[0][x]) Dis[0][x]=x*(dis[tp[0][x]]-dis[x]); } return; } inline void solve(){ dfs(1,0); rep(j,1,19){ rep(i,1,n){ if(!tp[j-1][i] || !tp[j-1][tp[j-1][i]]) continue; tp[j][i]=tp[j-1][tp[j-1][i]]; Dis[j][i]=Dis[j-1][i]+Dis[j-1][tp[j-1][i]]; } } rep(i,1,m){ int x=rd<int>(),y=rd<int>(); if(dep[x]>dep[y]) { puts("-1"); continue; } ll ans=0; drep(j,19,0){ if(dep[tp[j][x]]<dep[y] && tp[j][x]){ ans+=Dis[j][x]; x=tp[j][x]; } } ans+=1ll*(dis[y]-dis[x])*x; printf("%lld\n",ans); } return; } }p_chain;
其實跟鏈的差很少。
咱們把整棵樹分紅多個鏈,再處理鏈跳到鏈的狀況便可。
//這裏從新寫了pre_dfs與dfs_top,由於有不一樣的東西要用到 struct P100{ int dep[N],f[N],sz[N],son[N],top[N],L[N],R[N],dfn; ll dis[N],Dis[22][N]; int to[22][N]; int A[N],stk[N]; void pre_dfs(coint x, coint fa){ sz[x]=1; Erep(i,x){ edge y=Edge[i]; if(y.to==fa) continue; dep[y.to]=dep[x]+1; f[y.to]=x; dis[y.to]=dis[x]+y.val; pre_dfs(y.to,x); sz[x]+=sz[y.to]; if(sz[son[x]]<sz[y.to]) son[x]=y.to; } return; } void dfs_top(coint x, coint fa, coint tp){ top[x]=tp; L[x]=++dfn; coint SON=son[x]; if(SON) dfs_top(SON,x,tp); Erep(i,x){ int y=Edge[i].to; if(y==fa || y==SON) continue; dfs_top(y,x,y); } R[x]=dfn; if(top[x]==x){ int cnt=0,tail=0; for(int i=x; i; i=son[i]) A[++cnt]=i; drep(i,cnt,1){ int x=A[i]; while(tail && stk[tail]>=x) tail--; if(tail){ Dis[0][x]=x*(dis[stk[tail]]-dis[x]); to[0][x]=stk[tail]; } stk[++tail]=x; } } return; } struct Element{ int l,r; }B[N]; inline int Get_Element(int x, int y){ int tpx=top[x],tpy=top[y],cnt=0; while(tpx!=tpy){ B[++cnt]=(Element)<%tpy,y%>; y=f[tpy]; tpy=top[y]; } B[++cnt]=(Element)<%x,y%>; return cnt; } inline void solve(){ pre_dfs(1,0); dfs_top(1,0,1); rep(j,1,19){ rep(i,1,n){ to[j][i]=to[j-1][to[j-1][i]]; Dis[j][i]=Dis[j-1][i]+Dis[j-1][to[j-1][i]]; } } while(m--){ int s=rd<int>(),t=rd<int>(); if(s==t) { puts("0"); continue; } if(L[t]<L[s] || L[t]>R[s]) { puts("-1"); continue; } int cnt=Get_Element(s,t); ll ans=0; int mn=s; drep(i,cnt,1){ int l=B[i].l,r=B[i].r; mn=Min(mn,l); drep(j,19,0){ int nxt_l=to[j][l]; if(nxt_l && L[nxt_l]<=L[r] && nxt_l>=mn) ans+=mn*(dis[nxt_l]-dis[l]),l=nxt_l; } if(to[0][l] && L[to[0][l]]<=L[r]) ans+=mn*(dis[to[0][l]]-dis[l]),l=to[0][l],mn=l; drep(j,19,0){ int nxt_l=to[j][l]; if(nxt_l && L[nxt_l]<=L[r]) ans+=Dis[j][l],mn=l=to[j][l]; } if(i>1) ans+=mn*(dis[B[i-1].l]-dis[l]); else ans+=mn*(dis[r]-dis[l]); } printf("%lld\n",ans); } return; } }p100;
鏈的30分沒拿到真的不該該。思惟還得更加活躍才能夠。