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
个人觉得这一堆题还是不错的,不过我太菜了就是。

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