D.剪纸游戏
题目大意
- 找图案中,存在多少个标准方形
解题思路
- 对于一个未访问的点,递归找它所能到点,记录点个数
,
注意
- 递归中能不定义数组就不定义,因为递归只有在返回时会回收,所以相当于每层都开辟一次空间,空间会出问题
- 递归中调用数组同样耗费时间,像探查方向这类的简短操作,直接分着写要比
合着遍历数组写要快很多
import java.io.*;
import java.util.Scanner;
public class Main{
static int n;
static int m;
static char[][] map;
static boolean[][] us;
static int mxi,mxj,mii,mij,cnt;
public static void main(String[] args) throws IOException{
Scanner input=new Scanner(System.in);
n=input.nextInt();
m=input.nextInt();
map=new char[n][m];
for(int i=0;i<n;++i) {
map[i]=input.next().toCharArray();
}
int ans=0;
us=new boolean[n][m];
for(int i=0;i<n;++i) {
for(int j=0;j<m;++j) {
if(map[i][j]=='.'&&us[i][j]==false) {
mii=1001;mij=1001;cnt=0;
mxi=0;mxj=0;
getFang(i, j);
int num=(mxi-mii+1)*(mxj-mij+1);
if(num==cnt)ans++;
}
}
}
System.out.println(ans);
}
// static int[] nxti= {1,0,-1,0};
// static int[] nxtj= {0,1,0,-1};//移到递归里试试?
static void getFang(int x,int y) {
if(x<0||y<0||x>=n||y>=m||us[x][y]||map[x][y]!='.')return;
us[x][y]=true;
cnt++;
if(mii>x)mii=x;
if(mij>y)mij=y;
if(mxi<x)mxi=x;
if(mxj<y)mxj=y;
getFang(x+1,y);
getFang(x,y+1);
getFang(x-1,y);
getFang(x,y-1);//for 下试试?
}
}
E.可口蛋糕
题目大意
- 每一点
有
,求对于所有满足
的区间
,能得到的最大
解题思路
- 区间和,考虑前缀和
- 选择定右端点
,找到能满足限制的最大的
,则小于
的也是满足限制的可选的左端点
- 在找的过程中,记录最小的前缀和
- 则以
为右端点的
- 然后
,去计算以
为右端点的
,最后取
- 由于
,所以
满足限制,则
也满足限制,自然
之前的也满足
- 所以
均不减
- 若选择定左端点
,则
均要从
开始往前走
- 若
从1开始,则
- 无法保证
也满足限制
- 且
的区间变小无法维护最小值
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.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 n=input.nextInt();
long W=input.nextLong();
long[] w=new long[n+1];
long[] d=new long[n+1];
for(int i=1;i<=n;++i) {
w[i]=input.nextLong();
w[i]+=w[i-1];
}
for(int i=1;i<=n;++i) {
d[i]=input.nextLong();
d[i]+=d[i-1];
}
int i=0;//i-1
int j=1;
long mi=Long.MAX_VALUE/10;
long ans=Long.MIN_VALUE;
while(j<=n&&i<=j) {
if(w[j]-w[i]>=W) {//i+1,j
mi=Math.min(mi, d[i]);
i++;
}else {
ans=Math.max(ans, d[j]-mi);
j++;
}
}
out.println(ans);
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());
}
}
}
F.喜欢序列
题目大意
- 将一个数组分段,每段段内满足
- 进行
次操作,每次对
的数值加
- 若设分完段后,每段长度为
,则输出每次操作后的
解题思路
- 区间加值,考虑差分
- 由于最后结果与数值大小无关,所以只用记录数值与它前一个数的相对大小
- (由于不需要知道实际大小,所以差分不用求前缀和得出数值,也就不需要树状数组等数据结构)
- 对于一次修改,区间内的无影响,区间外的只可能会影响到
所在的段和
- 而影响只有两种
- 当
与
在一个区间,即
(此时差分已经修改过,原先
),则断开
- 当
与
不在一个区间,但修改后
(
不算修改,不考虑),则拼接
与
同理,但由于维护的是向前差分,所以对
的相对值
,看作判断
所在的区间是拼接还是断开(
,无意义不考虑)
- (将一次修改看作两次拼接/断开操作,先进行l,产生新的一轮分段,在此基础上,再进行r+1,产生新的分段)
- 例:
- 怎么记录每个点所在的区间,并且避免产生新的区间后,需要重新对区间内的点进行映射更新呢?
- 由于若一个点在某一区间内,则该区间的左端点一定是所有区间中比它小且离它最近的
- 所以不直接记录每个点所在的区间,而是知道点的位置去找区间
- 怎么找?这里引入神奇的
内部自动按照键值由小到大进行排序,其内置找小于
的最大值
- 完美
import java.io.*;
import java.util.StringTokenizer;
import java.util.TreeMap;
/*
* 函数中要使用数组。将其定义为全局,写在主函数上面
* 递归中能分开写的尽量不用数组
*/
public class Main{
static long ans;
static long[] pre;
static TreeMap<Integer, Integer> qu;
public static void main(String[] args) throws IOException{
AReader input=new AReader();
// Scanner input=new Scanner(System.in);
PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
int n=input.nextInt();
int m=input.nextInt();
long[] a=new long[n+1];
pre=new long[n+2];
a[1]=input.nextLong();
for(int i=2;i<=n;++i) {
a[i]=input.nextLong();
pre[i]=a[i]-a[i-1];
}
ans=0;
qu=new TreeMap<Integer, Integer>();
for(int i=2,l=1,r=1;i<=n;++i) {
if(a[i]==a[i-1]+1) r++;
else {
qu.put(l, r);
ans+=(long)(r-l+1)*(r-l+1);
l=i;r=i;
}
if(i==n) {
qu.put(l, r);
ans+=(long)(r-l+1)*(r-l+1);
}
}
while(m>0) {
int l=input.nextInt();
int r=input.nextInt();
long w=input.nextLong();
if(w!=0) {
if(l!=1)update(l, w);
if(r!=n)update(r+1, -w);
}
out.println(ans);
out.flush();
m--;
}
out.flush();
out.close();
}
static void update(int x,long w) {
pre[x]+=w;
if(pre[x]==1) {//拼
int l=qu.lowerKey(x);//x-1与x原先是断开的,l<x,l即x-1的左端点
int r=qu.get(x);//即x的右端点
qu.remove(x);//删去x区间
ans-=(long)(x-l)*(x-l);//x-1-l+1
ans-=(long)(r-x+1)*(r-x+1);
ans+=(long)(r-l+1)*(r-l+1);
qu.put(l, r);//替换x-1区间
}else if(pre[x]==w+1) {//断
int l=qu.lowerKey(x);
int r=qu.get(l);
//qu.remove(l);反正接着要替换
ans-=(long)(r-l+1)*(r-l+1);
ans+=(long)(x-l)*(x-l);//x-1-l+1
ans+=(long)(r-x+1)*(r-x+1);
qu.put(l, x-1);
qu.put(x,r);
}
}
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());
}
}
}