2018.9.17 离线赛 by ShimaKZ&EastKing

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Caristra/article/details/82762415

T1——faker(3920)

Description:

有一个长度为 n n 的初始为空的序列{ A A },有m个操作,每个操作有:
1. l l , r r 对区间 [ l , r ] [l,r] 1 1 .
2. l l , r r 重复操作 [ l , r ] [l,r] .
求操作完后序列{ A A }.
n 1 0 6 n \le 10^6

Solution:

  • 不难发现操作2是对于前面的操作,那么就要从后往前执行操作2.
  • 那么这样就可以记录下对于每个操作的次数 c n t i cnt_i .
  • 那么对于操作1就是 s u m i sum_i 加上当前的 c n t i cnt_i 即可.
  • 这样 s u m i sum_i , c n t i cnt_i 就是后缀遍历的.

Code:

#include<bits/stdc++.h>
using namespace std;
#define REP(i,f,top)for(int i=(f),i##_end_=(top);i<=i##_end_;++i)
#define SREP(i,f,top)for(int i=(f),i##_end_=(top);i<i##_end_;++i)
#define DREP(i,f,top)for(int i=(f),i##_end_=(top);i>=i##_end_;--i)
#define db double
#define ll long long 
#define INF 0x3f3f3f3f
#define inf 0x3f3f3f3f3f3f3f
#define MINF 0xc0c0c0c0
#define Sz(A) sizeof(A)
#define mcl(A,b) memset(A,b,Sz(A))
#define mcp(A,b) memcpy(A,b,Sz(b))
#define pb push_back
#define fi first
#define se second
template<class T>inline bool chkmin(T &x,T y){return y<x?x=y,1:0;}
template<class T>inline bool chkmax(T &x,T y){return x<y?x=y,1:0;}
typedef pair<int,int>PII;
template<class T>inline void Rd(T &x){
	x=0;char c;int f=1;
	while((c=getchar())<48)if(c=='-')f=-1;
	do x=(x<<1)+(x<<3)+(c^48);
	while((c=getchar())>47);
	x*=f;
}

#define N 1000010
#define mod 1000000007

int n,m;
struct ask{
	int op,l,r;
}Q[N];

struct p2{
	int cnt[N],add[N];
	void solve(){
		cnt[m]=1;
		DREP(i,m,1){
			cnt[i]=(cnt[i]+cnt[i+1])%mod;
			if (Q[i].op==1) {
				add[Q[i].l]=(add[Q[i].l]+cnt[i])%mod;
				add[Q[i].r+1]=(add[Q[i].r+1]+mod-cnt[i])%mod;
			} else {
				cnt[Q[i].r]=(cnt[Q[i].r]+cnt[i])%mod;
				cnt[Q[i].l-1]=(cnt[Q[i].l-1]+mod-cnt[i])%mod;
			}
		}
		int ans=0;
		REP(i,1,n){
			ans=(ans+add[i])%mod;
			printf("%d%c",ans,i==n?'\n':' ');
		}
	}
}p2;

int main(){
//	freopen("faker.in","r",stdin);
//	freopen("faker.out","w",stdout);
	Rd(n),Rd(m);
	REP(i,1,m)Rd(Q[i].op),Rd(Q[i].l),Rd(Q[i].r);
	p2.solve();
	return 0;
}

T2——poi(3921)

Description:

有n个排成一行的岛屿,有在这些岛屿上建立灯塔.需要满足 h i &gt; h i + 1 , h i 1 h_i&gt;h_{i+1},h_{i-1} ,特别地, h 1 &gt; h 2 , h n &gt; h n 1 h_1&gt;h_2 , h_n&gt;h_{n-1} .而你可以强行将一个岛屿的 h h 变小,代价即为减小的 h h 问建立 1 1 ~ n 2 \lceil \frac{n}{2} \rceil 的最小代价.
n 5000 , h i 1 0 5 n \le 5000 , h_i \le 10^5

Solution:

  • 首先看题,就可以判断是一道经典的 d p dp .
  • 那么我们先来分析一下问题的本质.
  • 对于第 i i 个灯塔来说,第 i 1 i-1 i + 1 i+1 一定不是灯塔.
  • 而我们不难发现对于第 i i 个岛屿来说,我们只关心第 i 1 i-1 , i + 1 i+1 个.
  • 若我们做从左到右的递推,即对于第 i i 个岛屿来说,只关心第 i 2 i-2 , i 1 i-1 个.(猜测之后一定会滚动数组优化一下)
  • 对于题目问的是 1 1 ~ n 2 \lceil \frac{n}{2} \rceil .这固然不能单独解决.一定是问题的其中一维.
  • 那么我们就不难想到一个 d p dp 的状态: d p [ i ] [ j ] [ k ] dp[i][j][k] 表示前 i i 个岛屿中建立了 j j 个灯塔的最小代价.
  • k=0表示在 i 1 i-1 , i i 处都不建塔.
  • k=1表示在 i i 处建塔.
  • k=2表示在 i 1 i-1 处建塔.
  • 然后再将其分类讨论一下,不难转移.

Code:

#include<bits/stdc++.h>
using namespace std;
#define REP(i,f,top)for(int i=(f),i##_end_=(top);i<=i##_end_;++i)
#define SREP(i,f,top)for(int i=(f),i##_end_=(top);i<i##_end_;++i)
#define DREP(i,f,top)for(int i=(f),i##_end_=(top);i>=i##_end_;--i)
#define db double
#define ll long long 
#define INF 0x3f3f3f3f
#define inf 0x3f3f3f3f3f3f3f
#define MINF 0xc0c0c0c0
#define Sz(A) sizeof(A)
#define mcl(A,b) memset(A,b,Sz(A))
#define mcp(A,b) memcpy(A,b,Sz(b))
#define pb push_back
#define fi first
#define se second
template<class T>inline bool chkmin(T &x,T y){return y<x?x=y,1:0;}
template<class T>inline bool chkmax(T &x,T y){return x<y?x=y,1:0;}
typedef pair<int,int>PII;
template<class T>inline void Rd(T &x){
	x=0;char c;int f=1;
	while((c=getchar())<48)if(c=='-')f=-1;
	do x=(x<<1)+(x<<3)+(c^48);
	while((c=getchar())>47);
	x*=f;
}

#define N 5002

int n;
int A[N];

struct p100{
	int dp[3][N][2];
	
	void solve(){
		A[++n]=0;
		mcl(dp,INF);
		dp[1][0][0]=0;
		dp[0][0][0]=0;
		int f=1;
		REP(i,2,n){
			f=(f+1)%3;
			mcl(dp[f],INF);
			REP(j,0,n/2){
				dp[f][j][0]=min(dp[(f+2)%3][j][0],dp[(f+2)%3][j][1]);
				if(j>=1){
					chkmin(dp[f][j][1],dp[(f+1)%3][j-1][0]+
					max(0,A[i]-(A[i-1]-1))+max(0,A[i-2]-(A[i-1]-1)));
				}
				if(j>=1 && i>=3){
					chkmin(dp[f][j][1],dp[(f+1)%3][j-1][1]+
					max(0,A[i]-(A[i-1]-1))+max(0,min(A[i-2],A[i-3]-1)-(A[i-1]-1)));
				}
			}
		}
		REP(i,1,n/2) printf("%d ",min(dp[f][i][0],dp[f][i][1]));
	}
}p2;

int main(){
//	freopen("poi.in","r",stdin);
//	freopen("poi.out","w",stdout);

	Rd(n);
	REP(i,1,n)Rd(A[i]);

	p2.solve();
	
	return 0;
}

T3——azur(3922)

Description:

有一个 m n m*n 的网格图,每条边都有边权 w w .接下来有 q q 个操作:

  1. ( x 1 , y 1 ) (x_1,y_1) ( x 2 , y 2 ) (x_2,y_2) 的最短路径上的各点的点权加 c c .
  2. 询问 ( x , y ) (x,y) 的点权.
    n , q 1 0 5 , m 3 , w 1 0 12 , c 1 0 13 n,q \le 10^5 , m \le 3 , w \le 10^{12} , c \le 10^{13}

Solution:

  • 因为操作 1 1 需要修改一个最短路径.每次求一次最短路显然不现实.
  • 又因为边权不变,那么考虑预处理出每个点的最短路径树.
  • 但显然我们不能做 n m n*m 棵树,再来观察数据范围,发现 m m 非常小.
  • 这说明一定跟区间搭上关系.那么我们就可以考虑合并一些赘余的树.
  • 即尽可能的少建一些树.不难推出.只需要 log n \log n 棵树就可以了(合并之后).
  • 那么再来看操作 1 1 ,我们再区间上看,它的最短路径树必须会经过一个区间[l,r]的mid,即必经过点 ( i , m i d ) (i,mid) ,即 ( x 1 , y 1 ) (x_1,y_1) , ( x 2 , y 2 ) (x_2,y_2) 在以点 ( i , m i d ) (i,mid) 为根的子树下.
  • 即可以做树上差分.
  • 又因为是单点查询.那么就可以单点累加下去即可.
  • 而对于修改和查询的树上,都需要分治来找.

Code:

#pragma GCC optimize("Ofast")

#include<bits/stdc++.h>
using namespace std;
#define REP(i,f,top)for(int i=(f),i##_end_=(top);i<=i##_end_;++i)
#define SREP(i,f,top)for(int i=(f),i##_end_=(top);i<i##_end_;++i)
#define DREP(i,f,top)for(int i=(f),i##_end_=(top);i>=i##_end_;--i)
#define db double
#define ll long long 
#define INF 0x3f3f3f3f
#define inf 0x3f3f3f3f3f3f3f
#define MINF 0xc0c0c0c0
#define Sz(A) sizeof(A)
#define mcl(A,b) memset(A,b,Sz(A))
#define mcp(A,b) memcpy(A,b,Sz(b))
#define pb push_back
#define fi first
#define se second
template<class T>inline bool chkmin(T &x,T y){return y<x?x=y,1:0;}
template<class T>inline bool chkmax(T &x,T y){return x<y?x=y,1:0;}
typedef pair<int,int>PII;
template<class T>inline void Rd(T &x){
	x=0;char c;int f=1;
	while((c=getchar())<48)if(c=='-')f=-1;
	do x=(x<<1)+(x<<3)+(c^48);
	while((c=getchar())>47);
	x*=f;
}

#define N 300010 
#define M 20

int n,m,q;

int qwq,head[N];
struct edge{
	int to,nxt;
	ll cost;
}E[N*10];
inline void addedge(int x,int y,ll z){E[qwq]=(edge){y,head[x],z};head[x]=qwq++;}

inline int getid(int x,int y){return (x-1)*n+y;}

struct p30{
	ll val[N];
	ll dis[M];
	bool vis[M];
	int stk[M],top;
	int S,T;
	queue<int>Q;
	
	void SPFA(){
		while(!Q.empty())Q.pop();
		mcl(dis,INF);
		Q.push(T);
		vis[T]=1;
		dis[T]=0;
		
		while(!Q.empty()){
			int x=Q.front();Q.pop();
			vis[x]=0;
			for(int i=head[x];~i;i=E[i].nxt){
				int y=E[i].to;
				if(chkmin(dis[y],dis[x]+E[i].cost)){
					if(!vis[y]){
						vis[y]=1;
						Q.push(y);
					}
				}
			}
		}
	}
	
	struct Anode{
		int to;
		ll d,all;
		bool operator<(const Anode &_)const{
			return all==_.all?d>_.d:all>_.all;
		}
	};
	priority_queue<Anode>Star;
	
	void Astar(){
		
		while(!Star.empty())Star.pop();
	
		Star.push((Anode){S,0,dis[S]});
		
		while(!Star.empty()){
			Anode now=Star.top();Star.pop();
			int x=now.to;
			stk[++top]=x;
			if(x==T)break;
			for(int i=head[x];~i;i=E[i].nxt){
				int y=E[i].to;
				Star.push((Anode){y,now.d+E[i].cost,now.d+E[i].cost+dis[y]});
			}
		}
	}
	
	void solve(){
	
		mcl(head,-1);
		SREP(i,1,m){
			REP(j,1,n){
				ll x;Rd(x);
//				printf("i=%d j=%d id1=%d id2=%d\n",i,j,getid(i,j),getid(i+1,j));
				addedge(getid(i,j),getid(i+1,j),x);
				addedge(getid(i+1,j),getid(i,j),x);
			}
		}
	
		REP(i,1,m){
			SREP(j,1,n){
				ll x;Rd(x);
//				printf("i=%d j=%d id1=%d id2=%d\n",i,j,getid(i,j),getid(i,j+1));
				addedge(getid(i,j),getid(i,j+1),x);
				addedge(getid(i,j+1),getid(i,j),x); 
			}
		}
		
		
		int op,x1,y1,x2,y2;ll c;
		while(q--){
			Rd(op),Rd(x1),Rd(y1);
			if(op==1){
				Rd(x2),Rd(y2),Rd(c);
				
				top=0;
				S=getid(x1,y1),T=getid(x2,y2);
				
				SPFA();
				Astar();
				
//				Dij();
				
				REP(i,1,top)val[stk[i]]+=c;
			}
			else printf("%lld\n",val[getid(x1,y1)]);
		}
	}
}p1;	

struct p50{
	#define lson L,mid,p<<1
	#define rson mid+1,R,p<<1|1
	
	struct node{
		int L,R;
		ll sum;
	}tree[100010<<2];
	
	void build(int L,int R,int p){
		tree[p].L=L,tree[p].R=R;
		tree[p].sum=0;
		if(L==R)return;
		int mid=(L+R)>>1;
		build(lson),build(rson);
	}
	
	void update(int L,int R,int p,ll v){
		if(tree[p].L==L && tree[p].R==R){
			tree[p].sum+=v;
			return;
		}
		int mid=(tree[p].L+tree[p].R)>>1;
		if(R<=mid)update(L,R,p<<1,v);
		else if(L>mid)update(L,R,p<<1|1,v);
		else update(lson,v),update(rson,v);
	}
	
	ll query(int p,int x){
		if(tree[p].L==tree[p].R)return tree[p].sum;
		int mid=(tree[p].L+tree[p].R)>>1;
		if(x<=mid)return tree[p].sum+query(p<<1,x);
		else return tree[p].sum+query(p<<1|1,x);
	}
	
	void solve(){
		SREP(i,1,n){
			ll x;Rd(x);
		}
		build(1,n,1); 
		int op,x1,y1,x2,y2;ll c;
		while(q--){
			Rd(op),Rd(x1),Rd(y1);
			if(op==1){
				Rd(x2),Rd(y2),Rd(c);
				if(y1>y2)swap(y1,y2);
				update(y1,y2,1,c);
			}
			else printf("%lld\n",query(1,y1));
		}
	}
}p2;

ll sum[N*M*5],*tmp=sum;
int pre[M][4][N];
ll dis[M][4][N];



struct p100{
	
	vector<int>V[N];
	int id[4][N],sz[4][N];
	ll *rt[4][N];
	int Lt[M][4][N],Rt[M][4][N];
	int D;
	ll val[N];
	
	ll mn,res;
	
	#define lowbit(x) (x&-x)
	
	void add(ll *bit,int n,int x,ll v){
		while(x<=n){
			bit[x]+=v;
			x+=lowbit(x);
		}
	}
	
	ll query(ll *bit,int x){
		ll res=0;
		while(x){
			res+=bit[x];
			x-=lowbit(x);
		}
		return res;
	}
	
	struct node{
		int x;
		ll d;
		bool operator>(const node&_)const{
			return d>_.d;
		}
	};
//	priority_queue<node>Q;
	struct Heap_Sort {
	    node Heap[N];
	    int size;
	    void down(int k) {
	        while(2*k<=size) {
	            int a=2*k;
	            if(a+1<=size && Heap[a]>Heap[a+1])a++;
	            if(Heap[k]>Heap[a])swap(Heap[k],Heap[a]);
	            else break;
	            k=a;
	        }
	    }
	    node top() {return Heap[1];}
	    void pop() {
	        Heap[1]=Heap[size--];
	        down(1);
	    }
	    void up(int k) {
	        while(k>1) {
	            int a=k/2;
	            if(Heap[a]>Heap[k])swap(Heap[k],Heap[a]);
	            else break;
	            k=a;
	        }
	    }
	    bool empty(){return !size;}
	    void push(node x) {
	        Heap[++size]=x;
	        up(size);
	    }
	    void clear(){
	          
	        size=0;
	    }
	}Q;
	
	void dfs(int x,int *Lt,int *Rt,int&sz){
		Lt[x]=++sz;
		SREP(i,0,V[x].size()) dfs(V[x][i],Lt,Rt,sz);
		Rt[x]=sz;
	}
	
	void Dijkstra(int dep,int x,int y,int l,int r){
		ll *dis=::dis[dep][x];
		int *pre=::pre[dep][x];
		SREP(i,0,m) REP(j,l,r){
			dis[id[i][j]]=inf;
			pre[id[i][j]]=-1;
			V[id[i][j]].clear();
		}
		
		dis[id[x][y]]=0;
		Q.push((node){id[x][y],0});
		
		while(!Q.empty()){
			node now=Q.top();Q.pop();
			if(dis[now.x]!=now.d)continue;
			for(int i=head[now.x];~i;i=E[i].nxt){
				int y=E[i].to;
				if(chkmin(dis[y],dis[now.x]+E[i].cost)){
					pre[y]=now.x;
					Q.push((node){y,dis[y]});
				}
			}
		}
		
		SREP(i,0,m) REP(j,l,r) if(~pre[id[i][j]]) V[pre[id[i][j]]].pb(id[i][j]);
		
		dfs(id[x][y],Lt[dep][x],Rt[dep][x],sz[x][y]);
		rt[x][y]=tmp;
		tmp+=sz[x][y]+1;
	}
	
	void build(int L,int R,int dep){
		if(L>R)return;
		int mid=(L+R)>>1;
		SREP(i,0,m) Dijkstra(dep,i,mid,L,R);
		build(L,mid-1,dep+1);
		build(mid+1,R,dep+1);
	}
	
	int x,y,x1,Y1,x2,y2;
	
	void update(int L,int R,int dep){
		if(L>R)return;
		int mid=(L+R)>>1;
		SREP(i,0,m){
			if(chkmin(mn,dis[dep][i][id[x1][Y1]]+dis[dep][i][id[x2][y2]])){
				D=dep;
				x=i;
				y=mid;
			}
		}
		if(Y1<mid && y2<mid) update(L,mid-1,dep+1);
		if(Y1>mid && y2>mid) update(mid+1,R,dep+1);
	}
	
	void divide(int L,int R,int dep){
		if(L>R)return;
		int mid=(L+R)>>1;
		SREP(i,0,m){
			res+=query(rt[i][mid],Rt[dep][i][id[x][y]]);
			res-=query(rt[i][mid],Lt[dep][i][id[x][y]]-1);
		}
		if(y<mid) divide(L,mid-1,dep+1);
		else if(y>mid) divide(mid+1,R,dep+1);
	}
	
	void solve(){
		mcl(head,-1);
		int num=0;
		SREP(i,0,m) REP(j,1,n)id[i][j]=++num;
		SREP(i,0,m-1) REP(j,1,n){
			ll w;Rd(w);
			addedge(id[i][j],id[i+1][j],w);
			addedge(id[i+1][j],id[i][j],w);
		}
		SREP(i,0,m) SREP(j,1,n){
			ll w;Rd(w);
			addedge(id[i][j],id[i][j+1],w);
			addedge(id[i][j+1],id[i][j],w);
		}
		build(1,n,0);
		while(q--){
			int op;ll c;
			Rd(op);
			if(op==1){
				Rd(x1),Rd(Y1),Rd(x2),Rd(y2),Rd(c);
				x1--,x2--;
				
				mn=inf;
				update(1,n,0);
				add(rt[x][y],sz[x][y],Lt[D][x][id[x1][Y1]],c);
				add(rt[x][y],sz[x][y],Lt[D][x][id[x2][y2]],c);
				val[id[x][y]]-=c;
			} 
			else{
				Rd(x),Rd(y);
				x--;
				
				res=val[id[x][y]];
				divide(1,n,0);
				printf("%lld\n",res);
			}
		}
	}
}p3;

int main(){
//	freopen("azur.in","r",stdin);
//	freopen("azur.out","w",stdout);
	Rd(m),Rd(n),Rd(q);
	
//	if(m==1)p2.solve();
//	else if(n<=1000 && q<=1000)p1.solve();
//	else 
	p3.solve();
	
	return 0; 
}

总结:

  • 送分题+ d p dp 题+数据结构题.还是比较标准的一套 n o i p noip .
  • 但T3的切分以及码量问题,导致选手们都自闭…
  • 但考场上自己也是对这个内存范围没感觉,没想到此题要建树.
  • 以及T2的分类讨论和debug花了较长时间…
  • 还是有待提高的.
  • 评价:较毒瘤出题人.

猜你喜欢

转载自blog.csdn.net/Caristra/article/details/82762415