1. 程式人生 > >2018.9.17 離線賽 by ShimaKZ&EastKing

2018.9.17 離線賽 by ShimaKZ&EastKing

T1——faker(3920)

Description:

有一個長度為nn的初始為空的序列{AA},有m個操作,每個操作有: 1. ll,rr對區間[l,r][l,r]11. 2. ll,rr重複操作[l,r][l,r]. 求操作完後序列{AA}. n106n \le 10^6

Solution:

  • 不難發現操作2是對於前面的操作,那麼就要從後往前執行操作2.
  • 那麼這樣就可以記錄下對於每個操作的次數cnticnt_i.
  • 那麼對於操作1就是sumisum_i加上當前的cnticnt_i即可.
  • 這樣sumisum_i,cnticnt_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個排成一行的島嶼,有在這些島嶼上建立燈塔.需要滿足hi&gt;hi+1,hi1h_i&gt;h_{i+1},h_{i-1},特別地,h1&gt;h2,hn&gt;hn1h_1&gt;h_2 , h_n&gt;h_{n-1}.而你可以強行將一個島嶼的hh變小,代價即為減小的hh問建立11~n2\lceil \frac{n}{2} \rceil的最小代價. n5000,hi105n \le 5000 , h_i \le 10^5

Solution:

  • 首先看題,就可以判斷是一道經典的dpdp.
  • 那麼我們先來分析一下問題的本質.
  • 對於第ii個燈塔來說,第i1i-1i+1i+1一定不是燈塔.
  • 而我們不難發現對於第ii個島嶼來說,我們只關心第i1i-1,i+1i+1個.
  • 若我們做從左到右的遞推,即對於第ii個島嶼來說,只關心第i2i-2,i1i-1個.(猜測之後一定會滾動陣列優化一下)
  • 對於題目問的是11~n2\lceil \frac{n}{2} \rceil.這固然不能單獨解決.一定是問題的其中一維.
  • 那麼我們就不難想到一個dpdp的狀態:dp[i][j][k]dp[i][j][k]表示前ii個島嶼中建立了jj個燈塔的最小代價.
  • k=0表示在i1i-1,ii處都不建塔.
  • k=1表示在ii處建塔.
  • k=2表示在i1i-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:

有一個mnm*n的網格圖,每條邊都有邊權ww.接下來有qq個操作:

  1. (x1,y1)(x_1,y_1)(x2,y2)(x_2,y_2)的最短路徑上的各點的點權加cc.
  2. 詢問(x,y)(x,y)的點權. n,q105,m3,w1012,c1013n,q \le 10^5 , m \le 3 , w \le 10^{12} , c \le 10^{13}

Solution:

  • 因為操作11需要修改一個最短路徑.每次求一次最短路顯然不現實.
  • 又因為邊權不變,那麼考慮預處理出每個點的最短路徑樹.
  • 但顯然我們不能做nmn*m棵樹,再來觀察資料範圍,發現mm非常小.
  • 這說明一定跟區間搭上關係.那麼我們就可以考慮合併一些贅餘的樹.
  • 即儘可能的少建一些樹.不難推出.只需要logn\log n棵樹就可以了(合併之後).
  • 那麼再來看操作11,我們再區間上看,它的最短路徑樹必須會經過一個區間[l,r]的mid,即必經過點(i,mid)(i,mid),即(x1,y1)(x_1,y_1),(x2,y2)(x_2,y_2)在以點(i,mid)(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; 
}

總結:

  • 送分題+dpdp題+資料結構題.還是比較標準的一套noipnoip.
  • 但T3的切分以及碼量問題,導致選手們都自閉…
  • 但考場上自己也是對這個記憶體範圍沒感覺,沒想到此題要建樹.
  • 以及T2的分類討論和debug花了較長時間…
  • 還是有待提高的.
  • 評價:較毒瘤出題人.