Educational Codeforces Round 161 (Rated for Div. 2) C~D

文章讨论了解决一个涉及有序城市间距离问题的算法,通过构建有向图,利用最短路径和前缀和的方法,处理多次查询,寻找每个查询城市的最小代价路径。
摘要由CSDN通过智能技术生成

题目链接 


C. Closest Cities

题目大意

  • 一个数组a,表示每个城市所在位置
  • 每个城市之间距离定义为\left | a_i-a_j \right |
  • 一个城市到其他任意城市有代价为其距离大小的有向路
  • 同时一个城市到离他最近的城市有代价为1的有向路
  • 保证a由小到大有序,且每个城市离他最近的只有一个
  • m次查询,问s\rightarrow t的最小代价

解题思路 

  • 最短路?有多次查询,否
  •  由于a有序,可以将城市u\rightarrow v直接到达,看作u依次经过u,v之间的城市到达v
  • 这样走,代价只可能变小而不会变多(可能会经过代价为1的路)
  • 同时,每个城市只用记录到其相邻城市的边,只有4条
  • 建图?跑图?前缀和!
  • 编号由小到大对正向边做一遍为Suml,反过来再做一遍为Sumr
  • 查询城市编号为s,t
  • Ans=\left\{\begin{matrix} Suml[r]-Suml[s-1] &s<r \\ Sumr[s]-Sumr[t+1]&s>r \end{matrix}\right.




import java.io.*;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.StringTokenizer;



public class Main{
	
	public static void main(String[] args) throws IOException{
		
		AReader input=new AReader();
	    PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
	    int T=input.nextInt();
	    while(true) {
	    	if(T==0) break;
	    	int n=input.nextInt();
	    	cnt=0;
	    	head=new int[n+1];
	    	e=new Edge[2*n+1];
	    	long[] a=new long[n+1];
	    	for(int i=1;i<=n;++i) {
	    		a[i]=input.nextLong();
	    	}
	    	long[] zheng=new long[n+1];
	    	long[] fan=new long[n+1];
//	    	addEdge(1, 2, 1);
	    	zheng[2]=1;//正向到i的花费
	    	fan[n-1]=1;
	    	for(int i=2;i<n;++i) {
	    		int l=i-1,r=i+1;
	    		
	    		if(a[i]-a[l]>a[r]-a[i]) {
	    			zheng[r]=zheng[i]+1;
//	    			addEdge(i, r, 1);
//	    			addEdge(i, l, a[i]-a[l]);
	    		}else {
	    			zheng[r]=zheng[i]+a[r]-a[i];
//	    			addEdge(i, l, 1);
//	    			addEdge(i, r, a[r]-a[i]);
	    		}
	    	}
//	    	addEdge(n, n-1, 1);
	    	for(int i=n-1;i>=2;--i) {
	    		int l=i-1,r=i+1;
	    		if(a[i]-a[l]>a[r]-a[i]) {
	    			fan[l]=fan[i]+a[i]-a[l];
	    		}else {
	    			fan[l]=fan[i]+1;
	    		}
	    	}
	    	int m=input.nextInt();
	    	while(true) {
	    		if(m==0)break;
	    		int s=input.nextInt();
	    		int t=input.nextInt();
	    		if(s<t) {
	    			out.println(zheng[t]-zheng[s]);
	    		}
	    		else out.println(fan[t]-fan[s]);
	    		
	    		m--;
	    	}
	    	T--;
	    }
		
		out.flush();
		out.close();
	}
	static
	class Node{
		int id;
		long dis;
		public Node(int I,long D) {
			id=I;
			dis=D;
		}
	}
	static int[] head;
	static int cnt;
	static Edge[] e;
	static 
	class Edge{
		int fr;
		int to;
		int nxt;
		long val;
	}
	static void addEdge(int fr,int to,long val) {
		cnt++;
		e[cnt]=new Edge();
		e[cnt].fr=fr;
		e[cnt].to=to;
		e[cnt].val=val;
		e[cnt].nxt=head[fr];
		head[fr]=cnt;
	}
	static
    class AReader {
        private BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        private StringTokenizer tokenizer = new StringTokenizer("");
        private String innerNextLine() {
            try {
                return reader.readLine();
            } catch (IOException ex) {
                return null;
            }
        }
        public boolean hasNext() {
            while (!tokenizer.hasMoreTokens()) {
                String nextLine = innerNextLine();
                if (nextLine == null) {
                    return false;
                }
                tokenizer = new StringTokenizer(nextLine);
            }
            return true;
        }
        public String nextLine() {
            tokenizer = new StringTokenizer("");
            return innerNextLine();
        }
        public String next() {
            hasNext();
            return tokenizer.nextToken();
        }
        public int nextInt() {
            return Integer.parseInt(next());
        }
 
        public long nextLong() {
            return Long.parseLong(next());
        }
 
    }
}


 D. Berserk Monsters

题目大意

  • n个点,每个点有a_i,d_i
  • 若左右与其相邻的点的\sum a>d_i,则该点删去,在下一轮不再出现
  • 共进行n轮,问每轮有多少个点被删去

解题思路 

  • 若一个点在这一轮中没被删去,且它左右相邻的点没有变化,则在下一轮也不会被删
  • 所以下一轮可能被删去的点,只会是这一轮被删去的点的左右相邻的点
  • 很像Spfa的优化原理
  • 用一个队列或是数组记录下一轮可能要删去的点进行探查,其余点不管
  • 问题在于本轮删点x和进行其左右端点l,r的更新时同时的
  • 而当更新右端点 r 的左端 r.pre 时,可能右端点 r 也在本轮的探查队列中,但此时更新了其左端,计算出来的\sum a不是本轮的,而是下一轮的,会对判断r是否在本轮删除产生影响
  • 所以对于r.pre的更新,若r在本轮队列中,则用r.nxtpre缓存,当计算完当前轮的\sum a后,更新r.pre,否则直接更新,nxtpre,pre保持一致

import java.io.*;
import java.util.StringTokenizer;
import java.util.TreeSet;



public class Main{
	static
	class Node{
		int pre;
		int nxtpre;
		int nxt;
		boolean inqu1;
		boolean inqu2;
		boolean die;
		long a;
		long d;
	}
	public static void main(String[] args) throws IOException{
		
		AReader input=new AReader();
	    PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
	    
	    int T=input.nextInt();
	    while(true) {
	    	if(T==0) break;
	    	int n=input.nextInt();
		    Node[] mos=new Node[n+3];
		    for(int i=0;i<=n+1;++i) {
		    	mos[i]=new Node();
		    	mos[i].pre=i-1;
		    	mos[i].nxt=i+1;
		    	mos[i].nxtpre=i-1;
		    }
		    for(int i=1;i<=n;++i)mos[i].a=input.nextLong();
		    for(int i=1;i<=n;++i)mos[i].d=input.nextLong();
		    int ans=0;
		    int[] qu1=new int[n+1];
		    int[] qu2=new int[n+1];
		    int cnt1=0;
		    int cnt2=0;
		    
		    for(int i=1;i<=n;++i) {
		    	int pre=mos[i].pre;//旧的
		    	int nxt=mos[i].nxt;
		    	long A=mos[pre].a+mos[nxt].a;
		    	mos[i].pre=mos[i].nxtpre;//下一轮的
		    	pre=mos[i].pre;
		    	if(A>mos[i].d) {
		    		ans++;
		    		mos[i].die=true;
		    		if(!mos[pre].inqu1&&pre!=0) {
		    			mos[pre].inqu1=true;
		    			cnt1++;
		    			qu1[cnt1]=pre;
		    		}
		    		if(!mos[nxt].inqu2&&nxt!=n+1) {
		    			mos[nxt].inqu1=true;
		    			cnt1++;
		    			qu1[cnt1]=nxt;
		    		}
		    		mos[pre].nxt=mos[i].nxt;
		    		mos[nxt].nxtpre=mos[i].pre;//初始肯定都在
		    	}
		    }
		    out.print(ans+" ");
		    for(int t=2;t<=n;++t) {
		    	if(t%2==0) {
		    		ans=0;
		    		for(int o=1;o<=cnt1;++o) {
		    			int i=qu1[o];
		    			mos[i].inqu1=false;
		    			if(mos[i].die)continue;
		    			int pre=mos[i].pre;//旧的
		    	    	int nxt=mos[i].nxt;
		    	    	long A=mos[pre].a+mos[nxt].a;
		    	    	mos[i].pre=mos[i].nxtpre;//下一轮的
		    	    	pre=mos[i].pre;
		    	    	if(A>mos[i].d) {
		    	    		ans++;
		    	    		mos[i].die=true;
		    	    		if(!mos[pre].inqu2&&pre!=0) {
		    	    			mos[pre].inqu2=true;
		    	    			cnt2++;
		    	    			qu2[cnt2]=pre;
		    	    		}
		    	    		if(!mos[nxt].inqu2&&nxt!=n+1) {
		    	    			mos[nxt].inqu2=true;
		    	    			cnt2++;
		    	    			qu2[cnt2]=nxt;
		    	    		}
		    	    		mos[pre].nxt=mos[i].nxt;
		    	    		if(mos[nxt].inqu1)mos[nxt].nxtpre=mos[i].pre;
		    	    		else {
		    	    			mos[nxt].pre=mos[i].pre;
		    	    			mos[nxt].nxtpre=mos[i].pre;
		    	    		}
		    	    	}
		    		}cnt1=0;
		    		out.print(ans+" ");
		    	}else {
		    		ans=0;
		    		for(int o=1;o<=cnt2;++o) {
		    			int i=qu2[o];
		    			mos[i].inqu2=false;
		    			if(mos[i].die)continue;
		    			int pre=mos[i].pre;//旧的
		    	    	int nxt=mos[i].nxt;
		    	    	long A=mos[pre].a+mos[nxt].a;
		    	    	mos[i].pre=mos[i].nxtpre;//下一轮的
		    	    	pre=mos[i].pre;
		    	    	if(A>mos[i].d) {
		    	    		ans++;
		    	    		mos[i].die=true;
		    	    		if(!mos[pre].inqu1&&pre!=0) {
		    	    			mos[pre].inqu1=true;
		    	    			cnt1++;
		    	    			qu1[cnt1]=pre;
		    	    		}
		    	    		if(!mos[nxt].inqu1&&nxt!=n+1) {
		    	    			mos[nxt].inqu1=true;
		    	    			cnt1++;
		    	    			qu1[cnt1]=nxt;
		    	    		}
		    	    		mos[pre].nxt=mos[i].nxt;
		    	    		if(mos[nxt].inqu2)mos[nxt].nxtpre=mos[i].pre;
		    	    		else {
		    	    			mos[nxt].pre=mos[i].pre;
		    	    			mos[nxt].nxtpre=mos[i].pre;
		    	    		}
		    	    	}
		    		}cnt2=0;
		    		out.print(ans+" ");
		    	}
		    }
		    T--;
		    out.println();
	    }
	    out.flush();
	    out.close();
	}
	
	static
    class AReader {
        private BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        private StringTokenizer tokenizer = new StringTokenizer("");
        private String innerNextLine() {
            try {
                return reader.readLine();
            } catch (IOException ex) {
                return null;
            }
        }
        public boolean hasNext() {
            while (!tokenizer.hasMoreTokens()) {
                String nextLine = innerNextLine();
                if (nextLine == null) {
                    return false;
                }
                tokenizer = new StringTokenizer(nextLine);
            }
            return true;
        }
        public String nextLine() {
            tokenizer = new StringTokenizer("");
            return innerNextLine();
        }
        public String next() {
            hasNext();
            return tokenizer.nextToken();
        }
        public int nextInt() {
            return Integer.parseInt(next());
        }
 
        public long nextLong() {
            return Long.parseLong(next());
        }
 
    }
}

 

  • 9
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值