1. 程式人生 > 其它 >[Contest on 我又忘了] 咋回事啊

[Contest on 我又忘了] 咋回事啊

哎呦喂。 目錄

\(\text{CF480E Parking Lot}\)

解法

\(\text{Method 1}\)\(\mathcal O(nmk)\)

這個都沒想到真的不應該...

對於每次修改都暴力改,然後進行一個 \(\mathcal O(nm)\)

\(\mathtt{dp}\):令 \(dp_{i,j}\) 為以 \((i,j)\) 為右下角的最大正方形。就有:

\[dp_{i,j}=\min\{dp_{i-1,j},dp_{i,j-1},dp_{i-1,j-1}\}+1 \]

具體畫三個 \(\mathtt{dp}\) 值對應的正方形就可以感性地證明。

\(\text{Method 2}\)\(\mathcal O(n^2)\)

當修改具有永久性,我們可以嘗試將其離線,觀察某種順序遍歷是否具有性質。

比如這題如果倒序遍歷修改,就有個非常神奇的性質 —— 如果更新了最大正方形,那麼這個正方形一定包含被撤銷的障礙!

據此可以維護 \(up_{i,j},down_{i,j}\)

分別表示從 \((i,j)\) 開始向上/下能延伸的最長長度。每次撤銷障礙修改是 \(\mathcal O(n)\) 的。

如何求解此時的最大正方形?直接求解似乎不太好搞,我們可以看一下如何 \(\rm check\) 長度為 \(l\) 的正方形。由於這個正方形包含了被撤銷的障礙 \((x,y)\),不妨以第 \(x\) 行為基準線,分別往上、往下延伸,再把它們拼在一起。

比如對於往上延伸,從第一列往右掃,對於 \(up_{x,j}\) 維護一個單增的單調佇列。因為只 \(\rm check\) 長度為 \(l\) 的正方形,所以當佇列長度大於 \(l\) 時可以踢掉隊頭,使長度更大。單次是 \(\mathcal O(m)\)

的。

但是這只是 \(\rm check\) 啊?其實可以每次暴力擴充套件 \(ans+1\)。首先,可以保證 \(\rm check\) 只能 成功 \(\min\{n,m\}\) 次。其次,對於 \(k\) 次詢問,每次只可能有一次 失敗,所以總時間複雜度 \(\mathcal O(nk+m(n+k))\)

\(\text{Method 3}\)\(\mathcal O(n^2)\)

如果想到了倒序修改,但是沒有想到暴力擴充套件 \(ans+1\)?可以用尺取法來 \(\mathcal O(m)\) 地計算最大正方形。

具體而言,設定兩個指標 \(l,r\),假設當前上下能擴充套件的最長長度為 \(L\),那麼如果 \(r-l+1\ge L\),此時再擴充套件 \(r\) 指標顯然沒有任何意義。於是將 \(l\) 指標加一。反之,則繼續擴充套件。

\(\text{Method 4}\)\(\mathcal O(n^2)\)

這題有線上做法,但是我太弱沒看懂,所以就咕咕咕了。

程式碼

在具體實現時,用的是開區間。

#include <cstdio>
#define print(x,y) write(x),putchar(y)

template <class T> 
inline T read(const T sample) {
	T x=0; char s; bool f=0;
	while((s=getchar())>'9' or s<'0')
		f|=(s=='-');
	while(s>='0' and s<='9')	
		x=(x<<1)+(x<<3)+(s^48),
		s=getchar();
	return f?-x:x;
}

template <class T>
inline void write(const T x) {
	if(x<0) {
		putchar('-');
		write(-x);
		return;
	}
	if(x>9) write(x/10);
	putchar(x%10^48);
}

#include <iostream>
using namespace std;

const int maxn=2005;

char g[maxn][maxn];
int n,m,k,Qx[maxn],Qy[maxn],A[maxn];
int up[maxn][maxn],down[maxn][maxn];
class Queue {
public:
	int l,r,q[maxn],v[maxn],tp;
	
	void Clear() {l=1,r=tp=0;}
	
	void Push(int k) {
		v[++tp]=k;
		while(l<=r and v[q[r]]>=k) --r;
		q[++r]=tp; 
	}
	
	void Pop(int x) {
		while(l<=r and q[l]<=x) ++l;
	}
	
	int Top() {
		return l<=r?v[q[l]]:0;
	}
} Up,Down;

void U_update(int j) {
	for(int i=1;i<=n;++i)
		if(g[i][j]^'X')
			up[i][j]=up[i-1][j]+1;
		else up[i][j]=0;
}

void D_update(int j) {
	for(int i=n;i>=1;--i)
		if(g[i][j]^'X')
			down[i][j]=down[i+1][j]+1;
		else down[i][j]=0;
}

int FuckIt(int i) {
	if(!i) return 0;
	int ans=0,L=0;
	Up.Clear(),Down.Clear();
	for(int l=1,r=1;l<=m;++l) {
		Up.Pop(l-1),Down.Pop(l-1);
		do {
			if(r<=m) {
				Up.Push(up[i][r]);
				Down.Push(down[i][r]);
				++r;
			}
			L=Up.Top()+Down.Top()-1;
			ans=max(ans,min(L,r-l));
		} while(r<=m and r-l<L);
	}
	return ans;
}

int main() {
	n=read(9),m=read(9),k=read(9);
	for(int i=1;i<=n;++i)
		scanf("%s",g[i]+1);
	for(int i=1;i<=k;++i)
		Qx[i]=read(9),Qy[i]=read(9),
		g[Qx[i]][Qy[i]]='X';
	for(int i=1;i<=m;++i)
		U_update(i),D_update(i);
	for(int i=1;i<=n;++i)
		A[k+1]=max(A[k+1],FuckIt(i));
	for(int i=k;i>=1;--i) {
		A[i]=max(A[i+1],FuckIt(Qx[i+1]));
		g[Qx[i]][Qy[i]]='.';
		U_update(Qy[i]);
		D_update(Qy[i]);
	}
	for(int i=1;i<=k;++i)
		print(A[i],'\n');
	return 0;
}

\(\text{CF575A Fibonotci}\)

解法

當時一場考試好像三個半小時,我調這道題調了整場考試… 最後發現思路都掛了。

先令

\[a_i=\begin{bmatrix}0 & s_{i-1}\\1 & s_i\end{bmatrix} \]

那麼就有

\[\begin{bmatrix}f_k & f_{k+1} \end{bmatrix}=\begin{bmatrix}0 &1\end{bmatrix}\times a_1\times ...\times a_k \]

用線段樹維護 \(a_1\)\(a_n\) 矩陣的乘積,將每個修改拆成兩個,這樣對於在 \([kn+1,kn+n]\) 中的修改線上段樹上修改即可。如果在 \([ln+1,rn]\) 之間都沒有修改的話,直接快速冪就行了。

\(n,m\) 同階,總時間複雜度 \(\mathcal O(8n\log n)\)

程式碼

#include <cstdio>
#define print(x,y) write(x),putchar(y)

template <class T>
inline T read(const T sample) {
	T x=0; bool f=0; char s;
	while((s=getchar())>'9' or s<'0')
		f|=(s=='-');
	while(s>='0' and s<='9')
		x=(x<<1)+(x<<3)+(s^48),
		s=getchar();
	return f?-x:x;
}

template <class T>
inline void write(const T x) {
	if(x<0) {
		putchar('-');
		write(-x);
		return;
	}
	if(x>9) write(x/10);
	putchar(x%10^48);
}

#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;

const int maxn=5e4+5;

int n,m,mod,val[maxn];
ll K;
struct Modi {
	ll x; int y;
	bool op;
} q[maxn<<1];

inline int inc(int x,int y) {
	return x+y>=mod?x+y-mod:x+y;
}

struct mat {
	int a[2][2];
	mat() {memset(a,0,sizeof a);}
 	
	mat operator * (const mat &t) const {
		mat r;
		for(int i=0;i<2;++i)
		for(int j=0;j<2;++j)
			if(a[i][j])
				for(int k=0;k<2;++k)
					r.a[i][k]=inc(
						r.a[i][k],
						1ll*a[i][j]*t.a[j][k]%mod);
		return r;
	}
} t[maxn<<2],cur,e,what,a[maxn],b[maxn];

mat qkpow(ll y) {
	mat r;
	for(int i=0;i<2;++i)
		r.a[i][i]=1;
	while(y) {
		if(y&1) r=r*e;
		e=e*e; y>>=1;
	}
	return r;
}

void build(int o,int l,int r) {
	if(l==r) 
		return t[o]=a[l],void();
	int mid=l+r>>1;
	build(o<<1,l,mid);
	build(o<<1|1,mid+1,r);
	t[o]=t[o<<1]*t[o<<1|1];
}

void modify(int o,int l,int r,int p) {
	if(l==r) 
		return t[o]=what,void();
	int mid=l+r>>1;
	if(p<=mid)
		modify(o<<1,l,mid,p);
	else modify(o<<1|1,mid+1,r,p);
	t[o]=t[o<<1]*t[o<<1|1];
}

inline int ID(ll x) {
	return x%n==0?n:x%n;
}

int main() {
	K=read(9ll),mod=read(9);
	n=read(9);
	for(int i=0;i<n;++i)
		val[i]=read(9)%mod;
	for(int i=1;i<=n;++i) {
		a[i].a[1][0]=1;
		a[i].a[0][1]=val[i-1];
		a[i].a[1][1]=val[i%n];
		b[i]=a[i];
	}
	m=read(9);
	for(int i=1;i<=m;++i)
		q[i].x=read(9ll),
		q[i+m].x=q[i].x+1,
		q[i].op=1,
		q[i+m].y=q[i].y=read(9)%mod;
	m<<=1;
	sort(q+1,q+m+1,[](Modi a,Modi b) {
		return a.x<b.x;
	});
	build(1,1,n);
	cur.a[0][1]=1;
	ll pos=0; int j;
	for(int i=1;i<=m;i=j+1) {
		if(q[i].x>K) break;
		if(pos+n<q[i].x) {
			e=t[1];
			cur=cur*qkpow((q[i].x-1-pos)/n);
		}
		pos=(q[i].x-1)/n*n+n;
		j=i;
		while(j+1<=m and (q[i].x-1)/n==(q[j+1].x-1)/n)
			++j;
		for(int k=i;k<=j;++k) {
			b[ID(q[k].x)].a[q[k].op][1]=q[k].y;
			what=b[ID(q[k].x)];
			modify(1,1,n,ID(q[k].x));
		}
		if((q[i].x-1)/n==(K-1)/n) {
			pos-=n;
			break;
		}
		cur=cur*t[1];
		for(int k=i;k<=j;++k)
			what=b[ID(q[k].x)]=a[ID(q[k].x)],
			modify(1,1,n,ID(q[k].x));
	}
	if(pos+n<K) { 
		e=t[1];
		cur=cur*qkpow((K-1-pos)/n);
		pos=(K-1)/n*n;
	}
	for(ll i=pos+1;i<=K;++i)
		cur=cur*b[ID(i)];
	print(cur.a[0][0],'\n');
	return 0;
}