codeforces 920 div3 D~G

题目链接

D

题目大意

        给两个数组a,b,一个长度为n,一个长度为m,其中m\geq n

        现从b中任意取出元素构成长度为n的数组c,问D=\sum \left | a_i-c_i \right |最大能为多少。

解题思路

  • 考虑每次从a中选一个,再从b中选一个进行配对
  • a,b从小到大排序,这样先取序列两头进行处理更优。因为绝对值的差值,显然极值相差最大
  • 对于每次a选头还是尾,相应b选头还是尾,都有可能,所以采用动态规划

        定义f[i][0/1].al,f[i][0/1].ar,f[i][0/1].bl,f[i][0/1].br,f[i][0/1].x分别表示第i次选a的头(0)或尾(1)后,得到最大值时,新的a序列,b序列和最大值。

f[i][0].x=max\left\{\begin{matrix} f[i-1][0].x+max\left\{\begin{matrix} (al,bl)\\ (al,br)\\ (ar,bl)\\ (ar,br) \\ al=f[i-1][0].al,\cdots \end{matrix}\right.\\\\ f[i-1][1].x+max\left\{\begin{matrix} (al,bl)\\ (al,br)\\ (ar,bl)\\ (ar,br) \\ al=f[i-1][1].al,\cdots \end{matrix}\right.\\\\e.g.\ \ (al,bl)=\left | a[al]-b[bl] \right |\cdots \\\end{matrix}\right.

\left\{\begin{matrix} (al,bl)\Rightarrow \left\{\begin{matrix} f[i][0].al=al+1\\\\ f[i][0].ar=ar\\\\ f[i][0].bl=bl+1\\\\ f[i][0].br=br \end{matrix}\right.\\ \\ (al,br)\Rightarrow \left\{\begin{matrix} f[i][0].al=al+1\\\\ f[i][0].ar=ar\\ \\ f[i][0].bl=bl\\\\ f[i][0].br=br+1 \end{matrix}\right. \\ \cdots \cdots \end{matrix}\right.

f[i][1]同理

可以先用f[i-1][0]进行更新,若f[i-1][1]得到的结果小,则不用更新,直接break

Ps:没有很复杂吧,代码重复性挺高的,很好写





import java.io.*;
import java.util.Arrays;
import java.util.Scanner;


public class Main{
	public static void main(String[] args) throws IOException{
		Scanner input=new Scanner(System.in);
		int T=input.nextInt();
		while(true) {
			if(T==0)break;
			int n=input.nextInt();
			int m=input.nextInt();
			long[] a=new long[n];
			long[] b=new long[m];
			for(int i=0;if[i][0].ans) {
						al++;br--;
						f[i][0]=new Node(al,ar,bl,br);
						f[i][0].ans=f[i-1][1].ans+flr;
						al--;br++;
					}
					
				}else {
					long ft=f[i-1][1].ans+fll;
					if(ft>f[i][0].ans) {
						al++;bl++;
						f[i][0]=new Node(al,ar,bl,br);
						f[i][0].ans=f[i-1][1].ans+fll;
						al--;bl--;
					}
					
				}
				if(frl<=frr) {
					long ft=f[i-1][1].ans+frr;
					if(ft>f[i][1].ans) {
						ar--;br--;
						f[i][1]=new Node(al,ar,bl,br);
						f[i][1].ans=f[i-1][1].ans+frr;
						ar++;br++;
					}
					
				}else {
					long ft=f[i-1][1].ans+frl;
					if(ft>f[i][1].ans) {
						ar--;bl++;
						f[i][1]=new Node(al,ar,bl,br);
						f[i][1].ans=f[i-1][1].ans+frl;
						ar++;bl--;
					}
					
				}
			}
			
			System.out.println(Math.max(f[n-1][0].ans,f[n-1][1].ans));
			T--;
		}
	}
	
}
class Node{
	int al;
	int ar;
	int bl;
	int br;
	long ans;
	public Node(int aL,int aR,int bL,int bR) {
		al=aL;
		ar=aR;
		bl=bL;
		br=bR;
	}
}

E

题目大意

        在一个棋盘内,有两个不同的点A,BA只能往左下,下,右下走,B只能往左上,上,右上走。A先走,B后走,问最终是AB,还是BA,或者不相遇为平局。双方都不傻。

解题思路

  • 若初始AB的下面或在同一行,则不可能相遇,为平局
  • AB之间行差为奇数,则最后一步为A走出,所以若相遇则A赢,否则BA
  • AB之间行差为偶数,则最后一步为B走出,所以若相遇则B赢,否则AB
  • 由于A先手,当差为奇数时判断第一次A能不能直接吃,不给B跑的机会
  • 当差为偶数,且A,B在同一列,则A无论怎么跑都会被B
  • 当跑的人跑到边界,则不能跑了,只能乖乖等死。若追的人,在总步数内到不了边界,则平局



import java.io.*;

import java.util.Scanner;



public class Main{
	
	public static void main(String[] args) throws IOException{
		Scanner input=new Scanner(System.in);
		int T=input.nextInt();
		while(true) {
			if(T==0) break;
			int h=input.nextInt();
			int w=input.nextInt();
			int a=input.nextInt();
			int b=input.nextInt();
			int c=input.nextInt();
			int d=input.nextInt();
			//A在B下面或同一行
			if(a>=c) {
				System.out.println("Draw");
				T--;
				continue;
			}
			int x=c-a;
			if(x%2!=0) {
				//则A/D
				//A上来直接吃
				if(Math.abs(b-d)<=1) {
					System.out.println("Alice");
					T--;
					continue;
				}
				//Alice跑到边界追上Bob的步数
				int may=b

题目大意

        给一个长度为n的数组,有q次询问。查询\sum_{i=s,t=1}^{i=s+d*(k-1),t=k}a_i*t

解题思路

  • 直接暴力O(q*k),当d很小时,k取值范围会变得很大,当取最大时,可以达到O(q*n)\approx O(n^2),复杂度很高
  • 发现当依次进行取数时,取数的下标只跟d有关,即i_{nxt}=i+d。且d的大小会影响k可能的取值,进而影响复杂度。而当d> \sqrt{n}时,直接暴力的复杂度最大变为O(n\sqrt{n}),是可以接受的。所以考虑按照d的大小将问题拆为两个子问题,也就是典型的根号分治。
  • 对于d\leq \sqrt{n},知道相应序列下标,考虑前缀和优化。预处理g[i][j]表示距离为i的元素所构成的序列,结尾为j的前缀和。由于每个元素还要乘上相应系数t,所以预处理f[i][j]=f[i][j-i]+a[j]*(j/i+1)ans=(f[d][s+d*(k-1)]-f[d][s-d])-(g[d][s+d*(k-1)]-g[d][s-d])*(s/d)
  • 数据很大,要用快输

例:

已知1*a_1+2*a_{1+d}+3*a_{1+d+d}+4*a_{1+d+d+d}+5*a_{1+4*d}+\cdots,要查询\left [ 3,5 \right ]的序列,则

(3*a_{1+d+d}+4*a_{1+d+d+d}+5*a_{1+4*d})-(a_{1+2*d}+a_{1+3*d}+a_{1+4*d})*2=1*a_{1+2*d}+2*a_{1+3*d}+3*a_{1+4*d}




import java.io.*;

import java.util.Scanner;
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();
			int q=input.nextInt();
			int w=(int)Math.sqrt(n);
			long[] a=new long[n+1];
			long[][] f=new long[w+1][n+1];
			long[][] g=new long[w+1][n+1];
			for(int i=1;i<=n;++i)a[i]=input.nextLong();
            //预处理
			for(int i=1;i<=w;++i) {
				for(int t=0;tw) {//暴力
					int t=s;
					for(int i=1;i<=k;++i) {
						ans+=a[t]*i;
						t=t+d;
					}
				}else {
					if(d>s) {//s-d<0
						ans=f[d][s+d*(k-1)];
					}else {
						ans=(f[d][s+d*(k-1)]-f[d][s-d])-(g[d][s+d*(k-1)]-g[d][s-d])*(s/d);
					}
				}
				out.print(ans+" ");
				
				q--;
			}
			out.println();
			T--;
		}
		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());
        }
 
    }

	
}

G

题目大意

        给一个图,图上有一些特殊点。在图上任选一点,从这个点开始向左上,右上,左下,右下,用边长为k+1的三角形进行覆盖,问能覆盖到的最大特殊点数。

解题思路

  • 注意题中所给出的坐标系与平常的不一样
  • 改变三角形,不如旋转图本身,便于考虑
  • 图旋转后的下标对应关系,自己可以在草稿纸上推出
  • 这里给出顺时针旋转方法:swap(n,m),map2_{i,j}=map_{m-j+1,i}
  • 从4种三角形种任选一种在图上进行扫描,对于旋转过后的4种图进行4次相同操作
  • 这里选择向左下的三角形
  • 每次移动,会新增加移动后竖边那一列的特殊点,新减少移动之前三角形的斜线上的特殊点
  • 对于增加列,采用二维前缀和进行计算
  • 对于减少斜线,先预处理出斜线前缀和,然后对于每次移动时利用等腰直角三角形(在纸上多推导),求出移动前完整三角形左上的点坐标,和右下的点坐标,再进行计算。



import java.io.*;

import java.util.Scanner;
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();
			int m=input.nextInt();
			int k=input.nextInt();
			char[][] map=new char[n+1][m+1];//n*m<=1e5
			for(int i=1;i<=n;++i) {
				String s=" "+input.next();
				map[i]=s.toCharArray();
			}
			int ans=0;
			for(int turn=1;turn<=4;++turn) {
				int[][] sum=new int[n+1][m+1];
				int[][] b=new int[n+1][m+1];//斜线
                //前缀和
				for(int i=1;i<=n;++i) {
					for(int j=1;j<=m;++j) {
						if(map[i][j]=='#') {
							sum[i][j]++;
							
							b[i][j]++;
						}
						sum[i][j]+=sum[i][j-1]+sum[i-1][j]-sum[i-1][j-1];
						b[i][j]+=b[i-1][j-1];
					}
				}
				
				for(int i=1;i<=n;++i) {
					int now=0;
					for(int j=1;j<=m;++j) {
						
						int di=Math.min(n,i+k);//右下坐标的i
						//加一列
						now+=sum[di][j]-sum[i-1][j]-sum[di][j-1]+sum[i-1][j-1];
						//处理斜线
                        if(j-1<1) {//移动之前三角形还没有覆盖
							ans=Math.max(ans, now);
							continue;
						}
						int zuoshangj=Math.max(1, j-k-1);
                        //超出边界时,取边界(或边界的延长线)与三角形的交点为左上

						int zuoshangi=zuoshangj-(j-1-k)+i;
						//左上坐标的i

						int youxiaj=j-1-Math.max(i+k-n, 0);
                        //右下坐标的j

						if(youxiaj<1||zuoshangi>n) {
                        //边界与三角形的交点在图像范围之外,即斜线与图没有重叠
							ans=Math.max(ans, now);
							continue;
						}
						now-=b[di][youxiaj]-b[zuoshangi-1][zuoshangj-1];
						ans=Math.max(ans, now);
					}
					
				}
				int t=n;
				n=m;
				m=t;
				
				//顺时针旋转
				char[][] map2=new char[n+1][m+1];
				for(int i=1;i<=n;++i) {
					for(int j=1;j<=m;++j) {
						map2[i][j]=map[m-j+1][i];
					}
				}
				map=map2;
			}
			System.out.println(ans);
			T--;
		}
		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());
        }
 
    }

	
}

你可能感兴趣的:(算法,数据结构)