zoukankan      html  css  js  c++  java
  • 杂题

    (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)一遍求割点/边顺便统计答案即可。

  • 相关阅读:
    JavaScript tips:innerHTML与document.write的差异
    JavaScript tips:window.onload与$(document).ready()的差异
    剑指offer:重建二叉树
    算法:数组去重
    JavaScript tips:Function调用模式对this的影响
    P1217 [USACO1.5]回文质数 Prime Palindromes
    HDU 1002 A + B Problem II
    单片机及其工作原理粗略介绍
    Markdown格式及语法
    Kubernetes入门(三)——使用Deployment运行一个无状态应用
  • 原文地址:https://www.cnblogs.com/lzhAFO/p/11781447.html
Copyright © 2011-2022 走看看