• 牛客CSP-S提高组赛前集训营5 解题报告


    Linker

    总分 : 100 + 100 + 40 = 240

    T1

    结论题。无论如何神J都会赢。

    最优决策:神树变化了我就不变,神树不变我就变化。

    #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
    ",POW(2,n));
    	return 0;
    }
    

    T2

    状压DP水题。

    暴力都没有去切,因为k太小了,直接状压走过那些边即可。

    //代码有点丑,但是考试的时候挺快就敲出来了。
    #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
    ",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
    ",ans);
    	return 0;
    }
    

    T3

    (pre_dfs与dfs_top是树链剖分的部分。)

    P30

    直接暴力(O(n^3))处理处每两个点间最小Dis值

    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
    ",Dis[s][t]);
    		}
    		return;
    	}
    }p30;
    

    P40

    可以发现询问数量很小,我们直接对于每个询问暴力向上跳,而我们总共就会处理(n^2)个点之间的距离,中间重复处理的还可以省去,因此总复杂度不到(O(n^2))

    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
    ",Dis[s][t]);
    		}
    		return;
    	}
    }p40;
    
    

    P70

    可以发现在向下跳的过程中,我们经过的点是单调递减的,因此利用倍增思想,记录下一个走到的点,以及其花费,然后倍增处理处即可。

    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
    ",ans);
    		}
    		return;
    	}
    }p_chain;
    
    

    P100

    其实跟链的差不多。

    我们把整棵树分成多个链,再处理链跳到链的情况即可。

    
    //这里重新写了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
    ",ans);
    		}
    		return;
    	} 
    }p100;
    

    总结

    链的30分没拿到真的不应该。思维还得更加活跃才可以。

  • 相关阅读:
    Java并发实现一(并发的实现之Thread和Runnable的区别)
    Java中的enum
    Eclipse+Maven创建webapp项目
    手机上最简洁的"云笔记"软件
    工具与艺术的结合:浅谈博客的排版规范与样式设计
    页面定制CSS代码初探(四):cnblogs使用Github引用样式
    脑图工具MindNode"附属节点"是什么意思 图解
    页面定制CSS代码初探(三):设置正文最小高度
    Sublime 是自动检测而非自动设置缩进
    苹果操作系统名称演变史 新名称macOS
  • 原文地址:https://www.cnblogs.com/ppp204-is-a-VC/p/11818284.html
Copyright © 2020-2023  润新知