杂题

(AtCoder~~Regular~~Contest~~067~~Yakiniku~~Restaurants)

考虑最优移动方式,一定可以是从某个点(a)向右移动到(b)

在这个区间([a,b])内,要做到吃的最多。

考虑一餐券(j)(i)餐馆使用的情况下,移动范围([l,r])最大时不亏(即如果区间为([l-1,r]),在(l-1)位置使用餐券j更优,如果区间为([l,r+1]),在(r+1)位置使用餐券(j)更优)。

记餐券(j)(i)餐馆使用的情况下,最大移动范围([~~L[i][j],R[i][j]~~])不亏。这个东西可以用单调栈求。

所以如果确定了移动范围([a,b]),如果(aleq j AndAnd bgeq jAndAnd L[i][j]leq a AndAnd R[i][j]geq b),你就会在(i)餐馆使用(j)餐券,因为这样一定最优。

(O(n^2))枚举移动范围 ([a,b]) ,如何统计这个区间内使用餐券的贡献呢?

将移动范围([a,b])看成一个二维坐标。使用j餐券的范围([~~L[i][j],R[i][j]~~])会在一个矩形内产生贡献。这个矩形的左上角右下角分别为((L[i][j],j),(j,R[i][j]))。枚举([a,b])的时候用二维前缀和统计就好了。

(AtCoder~~Regular~~Contest~~080~~Young~~Maids)

要求字典序最小,就要使得最后留下的一对最小,在这个前提下,倒数第二对最小......

总之就是,只要能够构造成功,越靠前的位置越小越好。

考虑最后一对有什么约束构造才能成功。

显然,最后一对((a,b))中,(a)一定为奇,(b)一定大于(a)且为偶。

由于((a,b))字典序最小,我们一定要让它留在最后了,考虑要付出什么代价,即倒数第二对((c,d))有什么约束。

从序列中删掉(a,b)后,(c,d)的奇偶性约束和(a,b)一样,而且(c,d)必须同时属于三个区间([1,a-1],[a+1,b-1],[b+1,n])中同一个。

到了这里,正解很显然了。

初始区间为([1,n]),从中选出最小的合法的((a,b)),然后区间分为三个([1,a-1],[a+1,b-1],[b+1,n]),再从这三个区间内找出最小的合法的((c,d)......)一直做下去。

用单调队列维护区间,从区间中找最小的数对可以用线段树或者倍增(rmq),需要分奇偶的查询。

(codeforces~~gym~~101955~~E~~The~~Kouga~~Ninja~~Scrolls)

先假设没有修改家族的操作且所有忍者属于不同家族。

问题变成纯粹的维护区间点对最大曼哈顿距离。

这个直接做很难。

科普一个技巧:

定义两点((x1,y1),(x2,y2))的曼哈顿距离(=|x1-x2|+|y1-y2|)
定义两点((x1,y1),(x2,y2))的切比雪夫距离(=max(|x1-x2|,|y1-y2|))

将一个点((x,y))的坐标变为((x+y,x−y))后,原坐标系中的曼哈顿距离(=) 新坐标系中的切比雪夫距离

将一个点((x,y))的坐标变为(((x+y)/2,(x−y)/2)) 后,原坐标系中的切比雪夫距离(=) 新坐标系中的曼哈顿距离。

证明的话,画图或者用上面的式子变换一下均可。

上回说到曼哈顿距离转切比雪夫距离。

这回聊聊转化之后怎么做。

切比雪夫距离

[max{(|x_1'-x_2'|,|y_1'-y_2'|)} ]

其中

[(x_1',y_1')=(x_1+y_1,x_1-y_1) ]

[(x_2',y_2')=(x_2+y_2,x_2-y_2) ]

那个绝对值不好处理,我们只能枚举拆开的两种方法,因此这个式子的值有四种。在这四种里面的最大值一定是正确答案。

我们维护区间连续点的

(1).(x+y)最大值及其所属集合

(2).与(x+y)最大值所属集合不同的最大值及其所属集合

(3).(x+y)最小值及其所属集合

(4).与(x+y)最小值所属集合不同的最小值及其所属集合

(5).(x-y)最大值及其所属集合

(6).与(x-y)最大值所属集合不同的最大值及其所属集合

(7).(x-y)最小值及其所属集合

(8).与(x-y)最小值所属集合不同的最小值机器所属集合

(9).(-x+y)最大值及其所属集合

(10).与(-x+y)最大值所属集合不同的最大值及其所属集合

(11).(-x+y)最小值及其所属集合

(12).与(-x+y)最小值所属集合不同的最小值机器所属集合

(13).(-x-y)最大值及其所属集合

(14).与(-x-y)最大值所属集合不同的最大值及其所属集合

(15).(-x-y)最小值及其所属集合

(16).与(-x-y)最小值所属集合不同的最小值机器所属集合

(~~~~)

修改操作不用多说,修改点坐标和所属集合都是单点修改,叶节点改一下重新向上合并就好了。

查询答案时,要把整个区间的(16)个数据项都处理出来,答案就是:

[max{(1)-(2),(3)-(4),(5)-(6),(7)-(8),(9)-(10),(11)-(12),(13)-(14),(15)-(16)} ]

用面向对象的思想写逻辑会顺畅一些,逃。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<ctime>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<string>
#include<queue>
#include<vector>
#include<map>
#include<set>
#include<utility>
#include<iomanip>
using namespace std;
int read(){
    int xx=0,ff=1;char ch=getchar();
    while(ch>'9'||ch<'0'){if(ch=='-')ff=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){xx=xx*10+ch-'0';ch=getchar();}
    return xx*ff;
}
long long READ(){
    long long xx=0,ff=1;char ch=getchar();
    while(ch>'9'||ch<'0'){if(ch=='-')ff=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){xx=xx*10+ch-'0';ch=getchar();}
    return xx*ff;
}
char one(){
	char ch=getchar();
	while(ch==' '||ch=='
')
		ch=getchar();
	return ch;
}
const int maxn=100010;
const long long INF=1LL<<60;
int N,M,C[maxn];
long long X[maxn],Y[maxn];
struct obj{
	long long v[2];
	int id[2];
	bool mx;
	void init(long long x,int y,bool m){
		v[0]=x,id[0]=y,mx=m;
		id[1]=0;
		if(mx)v[1]=-INF;
		else v[1]=INF;
	}
	obj friend operator+(obj A,const obj&B){
		if(A.mx){
			if(A.v[0]<B.v[0]){
				if(A.id[0]!=B.id[0])
					A.v[1]=A.v[0],A.id[1]=A.id[0];
				A.v[0]=B.v[0],A.id[0]=B.id[0];
			}
			else if(A.v[1]<B.v[0]&&A.id[0]!=B.id[0])
				A.v[1]=B.v[0],A.id[1]=B.id[0];
			if(A.v[1]<B.v[1]&&A.id[0]!=B.id[1])
				A.v[1]=B.v[1],A.id[1]=B.id[1];
		}
		else{
			if(A.v[0]>B.v[0]){
				if(A.id[0]!=B.id[0])
					A.v[1]=A.v[0],A.id[1]=A.id[0];
				A.v[0]=B.v[0],A.id[0]=B.id[0];
			}
			else if(A.v[1]>B.v[0]&&A.id[0]!=B.id[0])
				A.v[1]=B.v[0],A.id[1]=B.id[0];
			if(A.v[1]>B.v[1]&&A.id[0]!=B.id[1])
				A.v[1]=B.v[1],A.id[1]=B.id[1];
		}
		return A;
	}
	long long friend operator-(const obj&A,const obj&B){
		long long ret=0;
		if(A.id[0]!=B.id[0])
			return A.v[0]-B.v[0];
		if(A.id[0]!=B.id[1])
			ret=max(ret,A.v[0]-B.v[1]);
		if(A.id[1]!=B.id[0])
			ret=max(ret,A.v[1]-B.v[0]);
		if(A.id[1]!=B.id[1])
			ret=max(ret,A.v[1]-B.v[1]);
		return ret;
	}
	void print(){
		printf("%d %I64d %d %I64d %d
",mx,v[0],id[0],v[1],id[1]);
	}
};
struct Seg{
	obj o[8];
	Seg friend operator+(Seg A,const Seg&B){
		for(int i=0;i<8;i++)
			A.o[i]=A.o[i]+B.o[i];
		return A;
	}
	long long calc(){
		long long ret=0;
		for(int i=0;i<8;i+=2)
			ret=max(ret,o[i]-o[i+1]);
		return ret;
	}
	void print(){
		for(int i=0;i<8;i++)
			o[i].print();
		puts("");
	}
}T[maxn*4];
void build(int L,int R,int root){
	if(L==R){
		T[root].o[0].init(X[L]+Y[L],C[L],1);
		T[root].o[1].init(X[L]+Y[L],C[L],0);
		T[root].o[2].init(X[L]-Y[L],C[L],1);
		T[root].o[3].init(X[L]-Y[L],C[L],0);
		T[root].o[4].init(-X[L]+Y[L],C[L],1);
		T[root].o[5].init(-X[L]+Y[L],C[L],0);
		T[root].o[6].init(-X[L]-Y[L],C[L],1);
		T[root].o[7].init(-X[L]-Y[L],C[L],0);
		return;
	}
	int mid=(L+R)/2;
	build(L,mid,root*2);
	build(mid+1,R,root*2+1);
	T[root]=T[root*2]+T[root*2+1];
}
void upd(int L,int R,int root,int k,int x){
	if(L==R){
		C[L]=x;
		T[root].o[0].init(X[L]+Y[L],C[L],1);
		T[root].o[1].init(X[L]+Y[L],C[L],0);
		T[root].o[2].init(X[L]-Y[L],C[L],1);
		T[root].o[3].init(X[L]-Y[L],C[L],0);
		T[root].o[4].init(-X[L]+Y[L],C[L],1);
		T[root].o[5].init(-X[L]+Y[L],C[L],0);
		T[root].o[6].init(-X[L]-Y[L],C[L],1);
		T[root].o[7].init(-X[L]-Y[L],C[L],0);
		return;
	}
	int mid=(L+R)/2;
	if(k<=mid)
		upd(L,mid,root*2,k,x);
	else
		upd(mid+1,R,root*2+1,k,x);
	T[root]=T[root*2]+T[root*2+1];
}
void upd(int L,int R,int root,int k,int x,int y){
	if(L==R){
		X[L]+=x,Y[L]+=y;
		T[root].o[0].init(X[L]+Y[L],C[L],1);
		T[root].o[1].init(X[L]+Y[L],C[L],0);
		T[root].o[2].init(X[L]-Y[L],C[L],1);
		T[root].o[3].init(X[L]-Y[L],C[L],0);
		T[root].o[4].init(-X[L]+Y[L],C[L],1);
		T[root].o[5].init(-X[L]+Y[L],C[L],0);
		T[root].o[6].init(-X[L]-Y[L],C[L],1);
		T[root].o[7].init(-X[L]-Y[L],C[L],0);
		return;
	}
	int mid=(L+R)/2;
	if(k<=mid)
		upd(L,mid,root*2,k,x,y);
	else
		upd(mid+1,R,root*2+1,k,x,y);
	T[root]=T[root*2]+T[root*2+1];
}
Seg query(int L,int R,int root,int x,int y){
	if(x<=L&&y>=R)
		return T[root];
	int mid=(L+R)/2;
	if(x<=mid&&y>mid)
		return query(L,mid,root*2,x,y)+query(mid+1,R,root*2+1,x,y);
	else if(x<=mid)
		return query(L,mid,root*2,x,y);
	return query(mid+1,R,root*2+1,x,y);
}
void print(int L,int R,int root){
	printf("%d %d %d:
",L,R,root);
	T[root].print();
	if(L==R)
		return;
	int mid=(L+R)/2;
	print(L,mid,root*2);
	print(mid+1,R,root*2+1);
}
int main(){
	//freopen("in","r",stdin);
	for(int _=read(),cas=1;_;_--,cas++){
		printf("Case #%d:
",cas);
		N=read(),M=read();
		for(int i=1;i<=N;i++)
			X[i]=read(),Y[i]=read(),C[i]=read();
		build(1,N,1);
		while(M--){
			int opt=read(),x,y,k;
			if(opt==1){
				k=read(),x=read(),y=read();
				upd(1,N,1,k,x,y);
			}
			else if(opt==2){
				k=read(),x=read();
				upd(1,N,1,k,x);
			}
			else{
				x=read(),y=read();
				Seg ans=query(1,N,1,x,y);
				printf("%I64d
",ans.calc());
				//ans.print();
			}
		}
		//print(1,N,1);
	}
	return 0;
}
 

(AtCoder~~Grand~~Contest~~012~~Splatter~~Painting)

发现(dleq 10),可以做突破口。

铺地毯应该还记得吧,倒序处理修改。

一个操作记为(v,d,c)

(u)周围距离(d)以内的点染色可以转化为:将(u)染色,并把与(u)相邻的点(v)距离(d-1)以内的点染色。

显然这个操作可以做如下递归(边界自己处理):

    def opt(v,d,c):
        if color[v]==0:
            color[v]=c
    	for u in (u,v):
    		opt(u,d-1,c)

然后我们给这个递归加上记忆化,做过的操作不需要再做。

    def opt(v,d,c):
    	if vis[v][d]==true:
    		return
    	for i in [0,d]:
    		vis[v][d]=true
        if color[v]==0:
            color[v]=c
    	for u in (u,v):
    		opt(u,d-1,c)

每层递归至少标记(vis)的一个元素,复杂度正确。

(codeforces~~567~~E~~President~~and~~Roads)

我还没写,不过看起来大家都过了啊。

(codeforces~~1205~~B~~ Shortest~~Cycle)

首先统计每一位分别出现了多少次,如果存在某一位出现了(3)次或以上,那最小环的大小就为(3)

如果没有大小为(3)的环,你会发现这个图一定非常小,因为每一位最多出现两次,也就是说这个图最多只有约(100)个点。然后就可以用各种姿势求最小环了。

(codeforces~~gym~~101257~~F~~ISlands~~II)

方便起见,最外层套一圈(0)。然后遍历这个矩阵,如果相邻两个元素不同,将这两个元素值连边。

结论:对于一个点(u),如果他的子节点(v)是割点(更自然的:((u,v))是割边),那么(v)的子树中的节点一定是(u)(sub-iland)

(Tarjan)一遍求割点/边顺便统计答案即可。

原文地址:https://www.cnblogs.com/lzhAFO/p/11781447.html