2018-hyy的練習賽總結

hyy的練習賽總結

  • 題目爲hyy大佬原創。

  • 以下代碼默認開 O2\ O2

hyy有魚系列(序章)

在這裏插入圖片描述

題解

顯然,這是一個圖論最短路的題目,建圖之後 dijstra\ dijstra即可解決。

#include<bits/stdc++.h>
using namespace std;
inline int read()
{int s=0,w=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){s=s*10+ch-'0';ch=getchar();}
return s*w;}
int n,m,w,h;
int a[100100],len=0,hea[100100];
struct node
{
	int ne,to,l,fr;
}e[1600800];
inline void adde(int u,int v,int l)
{
	e[++len].ne=hea[u];
	hea[u]=len;
	e[len].to=v;
	e[len].l=l;;
}
struct noee
{
    int u;
	long long d;
    bool operator <(const noee& rhs) const
	{
        return d>rhs.d;
    }
};
int dis[100100];
priority_queue<noee> wq;
void dij(int s)
{
	memset(dis,63,sizeof(dis));
	dis[s]=0;
	wq.push((noee){s,0});
	while(!wq.empty())
	{
		noee fr=wq.top();
		wq.pop();
		int u=fr.u;
		long long d=fr.d;
		int i;
		if(d!=dis[u]) continue;
		i=hea[u];
		while(i)
		{
			int v=e[i].to;
			if(dis[v]>dis[u]+e[i].l)
			{
				dis[v]=dis[u]+e[i].l;
				wq.push((noee){v,dis[v]});
			}
			i=e[i].ne;
		}
		
	}
}
int main()
{
	n=read();
	int i=1;
	while(i<=n)
	{
		scanf("%d",&a[i]);
		if(i+a[i]<=n) adde(i,i+a[i],2);
		if(i^n) adde(i,i+1,1);
		if(i^1) adde(i,i-1,2);
		++i;
	}
	dij(1);
	printf("%d\n",dis[n]);
    return 0;
}


hyy有魚系列(1)

在這裏插入圖片描述

題解

覆蓋整個區間,問最少使用數,這顯然是可以用 dp\ dp來寫的。

我們設 toi\ to_{i},表示以 i\ i爲右端點的所有區間中,最小的左端點;設 fi\ f_{i}表示 [1,i]\ [1,i]都被覆蓋的最小使用數。我們可以得到以下方程:

fi=(minj=toi1i1fj)+1 f_{i}= (\min_{j=to_{i}-1}^{i-1} f_{j}) + 1

可以證明這是正確的。

唯一的問題是這麼做的複雜度是 O(n2)\ O(n^{2})的,不過很多數據結構都支持求區間求最小值,這裏我使用的是線段樹。

#include<bits/stdc++.h>
using namespace std;
inline int read()
{int s=0,w=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){s=s*10+ch-'0';ch=getchar();}
return s*w;}
int n,to[1000100],minn[4000400],m;
inline void build(int l,int r,int k)
{
	if(l>r) return ;
	if(l==r)
	{
		if(l==0) minn[k]=0;
		else minn[k]=999999999;
		return ;
	}
	int mid=(l+r)>>1;
	build(l,mid,k<<1);
	build(mid+1,r,k<<1|1);
	minn[k]=min(minn[k<<1],minn[k<<1|1]);
}
inline int query(int l,int r,int k,int ll,int rr)
{
	if(l>r) return 999999999;
	if((l>=ll)&&(r<=rr)) return minn[k];
	if((l>rr)||(r<ll)) return 999999999;
	int mid=(l+r)>>1;
	return min(query(l,mid,k<<1,ll,rr),query(mid+1,r,k<<1|1,ll,rr));
}
inline void change(int l,int r,int k,int x,int val) 
{
	if(l>r) return ;
	if(l==r)
	{
		minn[k]=val;
		return ;
	}
	int mid=(l+r)>>1;
	if(x<=mid) change(l,mid,k<<1,x,val);
	else change(mid+1,r,k<<1|1,x,val);
	minn[k]=min(minn[k<<1],minn[k<<1|1]);
}
int main()
{
	memset(to,63,sizeof(to));
	n=read();
	m=read();
	int i=1;
	while(i<=m)
	{
		int l,r;
		l=read();
		r=read();
		--l;
		to[r]=min(to[r],l);
		++i;
	}
	build(0,n,1);
	i=1;
	while(i<=n)
	{
		int f=query(0,n,1,to[i],i-1);
		change(0,n,1,i,f+1);
		++i;
	}
	int ans=query(0,n,1,n,n);
	if(ans>1000010) printf("-1\n");
	else printf("%d\n",ans);
    return 0;
}


當然

這種寫法雖然無腦,但是確實不怎麼巧妙。不過稍加思索之後,它可以轉化爲最短路的模型。

每一個區間 [l,r]\ [l,r]都建一條邊 l1r\ l-1 \rightarrow r,但是這樣的話,怎麼允許區間的覆蓋呢?很簡單,每個節點 i\ i都向 i1\ i-1建一條邊即可。

#include<bits/stdc++.h>
using namespace std;
inline int read()
{int s=0,w=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){s=s*10+ch-'0';ch=getchar();}
return s*w;}
int n,m,w,h;
int len=0,hea[1001001];
struct node
{
	int ne,to,l,fr;
}e[2002002];
inline void adde(int u,int v,int l)
{
	e[++len].ne=hea[u];
	hea[u]=len;
	e[len].to=v;
	e[len].l=l;;
}
struct noee
{
    int u;
	long long d;
    bool operator <(const noee& rhs) const
	{
        return d>rhs.d;
    }
};
int dis[1001001];
priority_queue<noee> wq;
void dij(int s)
{
	memset(dis,63,sizeof(dis));
	dis[s]=0;
	wq.push((noee){s,0});
	while(!wq.empty())
	{
		noee fr=wq.top();
		wq.pop();
		int u=fr.u;
		long long d=fr.d;
		int i;
		if(d!=dis[u]) continue;
		i=hea[u];
		while(i)
		{
			int v=e[i].to;
			if(dis[v]>dis[u]+e[i].l)
			{
				dis[v]=dis[u]+e[i].l;
				wq.push((noee){v,dis[v]});
			}
			i=e[i].ne;
		}
		
	}
}
int main()
{
	n=read();
	m=read();
	int i=1,l,r;
	while(i<=m)
	{
		l=read();
		r=read();
		adde(l-1,r,1);
		++i;
	}
	i=1;
	while(i<=n)
	{
		adde(i,i-1,0);
		++i;
	}
	dij(0);
	if(dis[n]<999999999) printf("%d\n",dis[n]);
	else printf("-1\n");
    return 0;
}

hyy有魚系列(2)

在這裏插入圖片描述

題解

顯然是一個五維的完全揹包,也許有大佬可以使用狀態壓縮,但實際上裸的揹包也可以過這道題。

#include<bits/stdc++.h>
using namespace std;
inline int read()
{int s=0,w=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){s=s*10+ch-'0';ch=getchar();}
return s*w;}
int f[21][21][21][21][21],m,a[21],b[21],c[21],d[21],e[21],aa,bb,cc,dd,ee,g[21];
int main()
{
	aa=read();
	bb=read();
	cc=read();
	dd=read();
	ee=read();
	m=read();
	int i=1;
	while(i<=m)
	{
		a[i]=read();
		b[i]=read();
		c[i]=read();
		d[i]=read();
		e[i]=read();
		g[i]=read();
		++i;
	}
	memset(f,0,sizeof(f));
	int aaa=1,bbb=1,ccc=1,ddd=1,eee=1;
	i=1;
	while(i<=m)
	{
		aaa=a[i];
		while(aaa<=aa)
		{
			bbb=b[i];
			while(bbb<=bb)
			{
				ccc=c[i];
				while(ccc<=cc)
				{
					ddd=d[i];
					while(ddd<=dd)
					{
						eee=e[i];
						while(eee<=ee)
						{
							f[aaa][bbb][ccc][ddd][eee]=max(f[aaa][bbb][ccc][ddd][eee],f[aaa-a[i]][bbb-b[i]][ccc-c[i]][ddd-d[i]][eee-e[i]]+g[i]);
							++eee;
						}
						++ddd;
					}
					++ccc;
				}
				++bbb;
			}
			++aaa;
		}
		++i;
	}
	int ans=f[aa][bb][cc][dd][ee];
	aaa=aa;
	bbb=bb;
	ccc=cc;
	ddd=dd;
	eee=ee;
	while(1)
	{
		if(aa==0) break;
		if(f[aa-1][bb][cc][dd][ee]^ans) break;
		--aa;
	}
	while(1)
	{
		if(bb==0) break;
		if(f[aa][bb-1][cc][dd][ee]^ans) break;
		--bb;
	}
	while(1)
	{
		if(cc==0) break;
		if(f[aa][bb][cc-1][dd][ee]^ans) break;
		--cc;
	}
	while(1)
	{
		if(dd==0) break;
		if(f[aa][bb][cc][dd-1][ee]^ans) break;
		--dd;
	}
	while(1)
	{
		if(ee==0) break;
		if(f[aa][bb][cc][dd][ee-1]^ans) break;
		--ee;
	}
	printf("%d\n%d %d %d %d %d\n",ans,aaa-aa,bbb-bb,ccc-cc,ddd-dd,eee-ee);
    return 0;
}

hyy有魚系列(3)

在這裏插入圖片描述

題解

一道還算不錯的 dp\ dp題。

如果不看保質期,這道題就是個完全揹包。因爲每天喫的食物是互不影響的,所以只需要每天取最小的可以讓小魚喫飽的錢就行了。

而加上保質期,這個問題就變成了瞭如何將完全揹包中的某個物品刪除。這個問題無疑是非常困難的。所以不如倒着來,從最後一天開始,一個個將物品加入完全揹包。

#include<bits/stdc++.h>
using namespace std;
inline int read()
{int s=0,w=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){s=s*10+ch-'0';ch=getchar();}
return s*w;}
int n,v,day=0,f[5050],val[3000300],mon,cost=0;
struct nobe
{
	int c,t,d;
}a[5050];
inline bool mmp(nobe a,nobe b)
{
	return a.t>b.t;
}
int main()
{
	mon=read();
	v=read();
	n=read();
	int i=1,j=n;
	while(i<=n)
	{
		a[i].c=read();
		a[i].t=read();
		a[i].d=read();
		day=max(day,a[i].t);
		++i;
	}
	sort(a+1,a+n+1,mmp);
	memset(f,63,sizeof(f));
	f[0]=0;
	i=day;
	int now=1;
	while(i>=1)
	{
		while((a[now].t>=i)&&(now<=n))
		{
			j=0;
			while(j<=v+1)
			{
				if(f[j]<=9999999) f[min(v+1,j+a[now].d)]=min(f[min(v+1,j+a[now].d)],f[j]+a[now].c);
				++j;
			}
			++now;
		}
		val[i]=min(f[v+1],f[v]);
		--i;
	}
	i=1;
	while(i<=day)
	{
		if(cost+val[i]<=mon) cost+=val[i];
		else break;
		++i;
	}
	printf("%d %d\n",i-1,mon-cost);
    return 0;
}

hyy有魚系列(4)

在這裏插入圖片描述

題解

由於 T1000\ T \leq 1000,所以每一行建一棵線段樹就可以了。

如果將 T\ T變爲 50000\ 50000,二維線段樹可以解決一切問題。

#include<bits/stdc++.h>
using namespace std;
inline int read()
{int s=0,w=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){s=s*10+ch-'0';ch=getchar();}
return s*w;}
int maxx[4040][1010],laz[4040][1010],c[1010][1010],n,m,tt;
inline void build(int l,int r,int k,int i)
{
	if(l>r) return ;
	laz[k][i]=0;
	if(l==r)
	{
		maxx[k][i]=c[l][i];
		return ;
	}
	int mid=(l+r)>>1;
	build(l,mid,k<<1,i);
	build(mid+1,r,k<<1|1,i);
	maxx[k][i]=max(maxx[k<<1][i],maxx[k<<1|1][i]);
}
inline void pushdown(int k,int i)
{
	laz[k<<1][i]+=laz[k][i];
	laz[k<<1|1][i]+=laz[k][i];
	maxx[k<<1][i]+=laz[k][i];
	maxx[k<<1|1][i]+=laz[k][i];
	laz[k][i]=0;
}
inline void update(int l,int r,int k,int x,int ll,int rr,int i)
{
	if((l>=ll)&&(r<=rr))
	{
		laz[k][i]+=x;
		maxx[k][i]+=x;
		return ;
	}
	if((l>rr)||(r<ll)) return ;
	int mid=(l+r)>>1;
	pushdown(k,i);
	update(l,mid,k<<1,x,ll,rr,i);
	update(mid+1,r,k<<1|1,x,ll,rr,i);
	maxx[k][i]=-2147483648;
	maxx[k][i]=max(maxx[k<<1][i],maxx[k<<1|1][i]);
}
inline int query(int l,int r,int k,int ll,int rr,int i)
{
	if(l>r) return -2147483648;
	if((l>=ll)&&(r<=rr)) return maxx[k][i];
	if((l>rr)||(r<ll)) return -2147483648;
	int mid=(l+r)>>1;
	pushdown(k,i);
	maxx[k][i]=max(maxx[k<<1][i],maxx[k<<1|1][i]);
	return max(query(l,mid,k<<1,ll,rr,i),query(mid+1,r,k<<1|1,ll,rr,i));
}
int main()
{
	n=read();
	m=read();
	register int i=1,j=1,k=1,l=1,q=1,x,ii=1,jj=1;
	i=1;
	while(i<=n)
	{
		j=1;
		while(j<=m)
		{
			c[i][j]=read();
			++j;
		}
		++i;
	}
	i=1;
	while(i<=m)
	{
		build(1,n,1,i);
		++i;
	}
	tt=read();
	while(tt--)
	{
		q=read();
		i=read();
		j=read();
		k=read();
		l=read();
		if(q==1)
		{
			x=read();
			ii=j;
			while(ii<=l)
			{
				update(1,n,1,x,i,k,ii);
				++ii;
			}
		}
		else
		{
			int ans=-2147483648;
			ii=j;
			while(ii<=l)
			{
				ans=max(ans,query(1,n,1,i,k,ii));
				++ii;
			}
			printf("%d\n",ans);
		}
		
	}
	return 0;
}


hyy有魚系列(5)

在這裏插入圖片描述

題解

乍一看很蒙,但是我們細細想一想就不是很蒙了。

 c=1\ c=1

我們先對一個二進制數考慮:

A=10101xxxxxx,A&lt;10101100000 A=10101xxxxxx,A &lt; 10101100000

 x\ x是不確定的數字,有 num\ num位,已經確定的 1\ 1 sum\ sum個,從左往右第一個 x\ x恆爲 0\ 0
因爲要計算所有的1的總和,我們先考慮後面未知的部分。如果設某一位爲 1\ 1,其他的部分有 2num2\ 2^{num-2}種可能,所以這一部分的總和爲 (num1)2num2\ (num-1) \cdot 2^{num-2};接着考慮前面的部分。未知的部分可能性有2num12^{num-1}種,所以是 sum2num1\ sum \cdot 2^{num-1}

現在得出結論,設 n\ n的二進制寫法中 1\ 1的寫法爲 n=stotstot1stot2s3s2s1,si{0,1}\ n=s_{tot}s_{tot-1}s_{tot-2} \cdots s_{3}s_{2}s_{1},s_{i} \in \{ 0,1 \}

答案爲:

(i=1totsi)+i=1tot{si[(j=i+1totsj)2i1+(i1)2i2]} (\sum_{i=1}^{tot}s_{i}) + \sum_{i=1}^{tot} \{ s_{i} \cdot [(\sum_{j=i+1}^{tot} s_{j})2^{i-1} + (i-1) 2^{i-2}] \}

 c=2\ c=2

和剛纔的考慮方法類似,但是隻用考慮後面未知的部分

A=10101xxxxxx,A&lt;10101100000 A=10101xxxxxx,A &lt; 10101100000

 x\ x是不確定的數字,有 num\ num位,已經確定的 1\ 1 sum\ sum個,從左往右第一個 x\ x恆爲 0\ 0

當有 j\ j個不確定的數字爲 1\ 1時,所有的情況有 (sum1j)\ \binom{sum-1}{j}種,顯然這一部分的結果爲 (j+sum)(sum1j)\ (j+sum)^{\binom{sum-1}{j}},即可得出結論。

 n\ n的二進制寫法中 1\ 1的寫法爲 n=stotstot1stot2s3s2s1,si{0,1}\ n=s_{tot}s_{tot-1}s_{tot-2} \cdots s_{3}s_{2}s_{1},s_{i} \in \{ 0,1 \}

答案爲:

(i=1totsi)i=1tot[sij=0j&lt;i(j+sum)(sum1j)] (\sum_{i=1}^{tot}s_{i}) \cdot \prod_{i=1}^{tot} [ s_{i} \cdot \prod_{j=0}^{j&lt;i} (j+sum)^{\binom{sum-1}{j}} ]

通過一些優化可以減少一些複雜度。

#include<bits/stdc++.h>
using namespace std;
inline int read()
{int s=0,w=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){s=s*10+ch-'0';ch=getchar();}
return s*w;}
inline void write(int x)
{
     if(x<0) putchar('-'),x=-x;
     if(x>9) write(x/10);
     putchar(x%10+'0');
}
const int mod=100000007;
int ttt,c;
long long n,ans=0,f[70][70],cc[130];
long long pow2[70];
inline int bitcount(long long n)
{
	register int c=0;
	while(n)
	{
		n&=(n-1);
		++c;
	}
	return c;
}
inline long long ppow(long long a,int b)
{
	long long res=1;
	while(b)
	{
		if(b&1) res=(res*a)%mod;
		a=(a*a)%mod;
		b>>=1;
	}
	return res%mod;
}
signed main()
{
	register int i=1,j,sum;
	pow2[0]=1;
	while(i<=52)
	{
		pow2[i]=pow2[i-1]*2ll;
		++i;
	}
	f[0][0]=1;
	i=1;
	while(i<=52)
	{
		f[i][0]=f[i][i]=1;
		j=1;
		while(j<i)
		{
			f[i][j]=f[i-1][j]+f[i-1][j-1];
			if(f[i][j]>=(mod-1)) f[i][j]-=mod-1;
			++j;
		}
		++i;
	}
	ttt=read();
	while(ttt--)
	{
		c=read();
		scanf("%lld",&n);
		if(c==1)
		{
			ans=0;
			i=51;
			while((i!=0)&&((n>>(i-1))&1)==0) --i;
			sum=0;
			while(i)
			{
				ans+=pow2[i-1]*sum;
				ans%=mod;
				if(i^1) ans+=pow2[i-2]*(i-1);
				ans%=mod;
				++sum;
				--i;
				while((i>0)&&((n>>(i-1))&1)==0) --i;
			}
			ans+=bitcount(n);
			if(ans>=mod) ans-=mod;
			printf("%lld\n",ans);
		}
		else
		{
			ans=1;
			i=51;
			memset(cc,0,sizeof(cc));
			while((i)&&((n>>(i-1))&1)==0) --i;
			sum=0;
			while(i)
			{
				j=(sum==0) ? 1 : 0;
				while(j<i)
				{
					cc[j+sum]+=f[i-1][j];
					cc[j+sum]%=(mod-1);
					++j;
				}
				++sum;
				--i;
				while((i)&&((n>>(i-1))&1)==0) --i;
			}
			i=1;
			while(cc[i])
			{
				ans*=ppow(i,cc[i]);
				ans%=mod;
				++i;
			}
			ans*=bitcount(n);
			ans%=mod;
			printf("%lld\n",ans);
		}
	}
	return 0;
}


hyy有魚系列(5)

在這裏插入圖片描述

題解

展開這個式子,就會得到這樣一個形式的東西:

fn=b+ab+a2b+a3b++an2b+an1 f_{n}=b+ab+a^{2}b+a^{3}b+ \cdots +a^{n-2}b+a^{n-1}

是一個等差數列與一個冪的和,這兩個都有 O(logn)\ O( \log n)的做法。

 sump,c=1+p+p2++pc\ sum_{p,c}=1+p+p^{2}+ \cdots +p^{c},有

sump,c={1,c==0p+1c==1(1+pc2)sump,c21+pcother sum_{p,c}= \begin{array}{rcl} \begin{cases} 1, &amp; &amp; c==0\\ p + 1 &amp; &amp; c==1\\ (1+p^{\frac{c}{2}})sum_{p,\frac{c}{2}-1}+p^{c} &amp; &amp; other \end{cases} \end{array}

證明略

#include <cstdio>
#include <cmath>
#include <iostream>
using namespace std;
#define int long long
inline int read()
{int s=0,w=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){s=s*10+ch-'0';ch=getchar();}
return s*w;}
int m,bb;
int a,b,mod,n;
long long qpow(long long a,long long b)
{
	long long r=1,base=a;
	while(b)
	{
		if(b&1) r=(r*base)%mod;
		base=(base*base)%mod;
		b>>=1;
	}
	return r;
}
long long sum(long long p,long long c)
{
	if(c==0) return 1ll;
	if(c==1) return p%mod+1ll;
	if(c&1) return ((1ll+qpow(p,(c+1ll)/2ll))*sum(p,(c-1ll)/2ll))%mod;
	else return ((1ll+qpow(p,c/2))*sum(p,c/2ll-1ll)+qpow(p,c))%mod;
}
signed main()
{
	int a,b;
	a=read();
	b=read();
	mod=read();
	n=read();
	if(n==1)
	{
		printf("1\n");
		return 0;
	}
	int res=1;
	res*=sum(a,n-2)%mod;
	res%=mod;
	res*=b;
	res%=mod;
	res+=qpow(a,n-1);
	res%=mod;
	cout<<res<<endl;
	return 0;
}

hyy有魚系列(7)

在這裏插入圖片描述

題解

 STL\ STL O2\ O2下巨快。
 lcp\ lcp kmp\ kmp啊!

#include<bits/stdc++.h>
using namespace std;
int m,next[5050];
string s,s2;
void getnext(string p)
{
	int plen=p.length();
	next[0]=-1;
	int k=-1;
	int j=0;
	while(j<plen-1)
	{
		if((k==-1)||(p[j]==p[k]))
		{
			++j;
			++k;
			if(p[j]!=p[k]) next[j]=k;
			else next[j]=next[k];
		}
		else
		{
			k=next[k];
		}
	}
}
int kmpsearch(string s, string p)
{
	int i=0;
	int j=0;
	int slen=s.length();
	int plen=p.length();
	while((i<slen)&&(j<plen))
	{
		if((j==-1)||(s[i]==p[j]))
		{
			++i;
			++j;
		}
		else j=next[j];
	}
	if(j==plen) return i-j;
	else return -1;
}
int main()
{
	scanf("%d",&m);
	cin>>s;
	int cz,p,len;
	while(m--)
	{
		scanf("%d",&cz);
		if(cz==4) cout<<s<<endl;
		else if(cz==2)
		{
			scanf("%d%d",&p,&len);
			s.erase(p-1,len);
		}
		else if(cz==1)
		{
			scanf("%d",&p);
			cin>>s2;
			s.insert(p,s2);
		}
		else
		{
			cin>>s2;
			getnext(s2);
			if(kmpsearch(s,s2)!=-1) printf("yes\n");
			else printf("no\n");
		}
	} 
	return 0;
}

hyy有魚系列(8)

在這裏插入圖片描述

題解

先考慮 m=1\ m=1的情況, ai\ a_{i}表示第 i\ i個數字。這時矩陣變成區間。

首先:

  •  [l,l]\ [l,l]一定是波瀾區間。
  •  [l,r],l&lt;r\ [l,r],l &lt; r是波瀾區間的要求是任意 x,lx&lt;r\ x,l \leq x &lt; r [l,x]\ [l,x]是波瀾區間, [x+1,r]\ [x+1,r]是波瀾區間,且 ax=ax+1\ a_{x}= \neq a_{x+1}

正確性是顯然的。這兩條使我們可以用線段樹維護。

 m&gt;1\ m&gt;1時呢?容易想到建 m\ m棵線段樹,但是這樣複雜度是過不去的 (我卡過9九點)。實際上狀態壓縮,用二進制表示就可以了。

#include<bits/stdc++.h>
using namespace std;
char buf[1<<20],*fs,*ft;
inline int read()
{int s=0,w=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){s=s*10+ch-'0';ch=getchar();}
return s*w;}
long long is[400001],maxl,a[100001];
int n,m,q;
inline void pushup(int l,int r,int k)
{
	int mid=(l+r)>>1;
	long long aa=a[mid]^a[mid+1],ss=is[k<<1]&is[k<<1|1];
	is[k]=aa&ss;
}
inline void build(int l,int r,int k)
{
	if(l==r)
	{
		is[k]=(1ll<<(m))-1ll;
		return ;
	}
	int mid=(l+r)>>1;
	build(l,mid,k<<1);
	build(mid+1,r,k<<1|1);
	pushup(l,r,k);
}
inline void change(int l,int r,int k,int x)
{
	if(l==r) return ;
	int mid=(l+r)>>1;
	if(x<=mid) change(l,mid,k<<1,x);
	else change(mid+1,r,k<<1|1,x);
	pushup(l,r,k);
}
inline long long query(int l,int r,int k,int ll,int rr,int x,int len)
{
	if((ll<=l)&&(rr>=r))
	{
		return ((is[k]>>(x-1))&((1ll<<(len))-1));
	}
	if((r<ll)||(l>rr)) return (1ll<<(len))-1;
	int mid=(l+r)>>1;
	long long f1=query(l,mid,k<<1,ll,rr,x,len),f2=query(mid+1,r,k<<1|1,ll,rr,x,len),aa,aa1,aa2;
	if(mid+1>rr) return f1;
	if(mid<ll) return f2;
	aa1=((a[mid]>>(x-1))&((1ll<<(len))-1ll));
	aa2=((a[mid+1]>>(x-1))&((1ll<<(len))-1ll));
	aa=aa1^aa2;
	return ((f1&f2)&aa);
}
int main()
{
	n=read();
	m=read();
	q=read();
	maxl=(1ll<<(m+1))-1;
	register int i=1,j=1,k,l,cz,ii;
	bool f;
	while(i<=n)
	{
		j=1;
		while(j<=m)
		{
			a[i]|=(1ll*read())<<(j-1);
			++j;
		}
		++i;
	}
	build(1,n,1);
	while(q--)
	{
		cz=read();
		if(cz)
		{
			i=read();
			j=read();
			k=read();
			l=read();
			ii=j;
			if(query(1,n,1,i,k,j,l-j+1)) printf("1\n");
			else printf("0\n");
		}
		else
		{
			i=read();
			j=1;
			a[i]=0;
			while(j<=m)
			{
				a[i]|=(1ll*read())<<(j-1);
				++j;
			}
			change(1,n,1,i);
		}
	}
	return 0;
}

最終總結

orzhyy

比賽的時候第六題沒有優化,結果沒有 A\ A
個人覺得這一堆題還是不錯的,不過我太菜了就是。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章