2018-hyy的练习赛总结

hyy的练习赛总结

  • 题目为hyy大佬原创。

  • 以下代码默认开   O 2 \ O2  O2

hyy有鱼系列(序章)

2018-hyy的练习赛总结_第1张图片

题解

显然,这是一个图论最短路的题目,建图之后   d i j s t r a \ dijstra  dijstra即可解决。

#include
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)

2018-hyy的练习赛总结_第2张图片

题解

覆盖整个区间,问最少使用数,这显然是可以用   d p \ dp  dp来写的。

我们设   t o i \ to_{i}  toi,表示以   i \ i  i为右端点的所有区间中,最小的左端点;设   f i \ f_{i}  fi表示   [ 1 , i ] \ [1,i]  [1,i]都被覆盖的最小使用数。我们可以得到以下方程:

f i = ( min ⁡ j = t o i − 1 i − 1 f j ) + 1 f_{i}= (\min_{j=to_{i}-1}^{i-1} f_{j}) + 1 fi=(j=toi1mini1fj)+1

可以证明这是正确的。

唯一的问题是这么做的复杂度是   O ( n 2 ) \ O(n^{2})  O(n2)的,不过很多数据结构都支持求区间求最小值,这里我使用的是线段树。

#include
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]  [l,r]都建一条边   l − 1 → r \ l-1 \rightarrow r  l1r,但是这样的话,怎么允许区间的覆盖呢?很简单,每个节点   i \ i  i都向   i − 1 \ i-1  i1建一条边即可。

#include
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)

2018-hyy的练习赛总结_第3张图片

题解

显然是一个五维的完全背包,也许有大佬可以使用状态压缩,但实际上裸的背包也可以过这道题。

#include
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)

2018-hyy的练习赛总结_第4张图片

题解

一道还算不错的   d p \ dp  dp题。

如果不看保质期,这道题就是个完全背包。因为每天吃的食物是互不影响的,所以只需要每天取最小的可以让小鱼吃饱的钱就行了。

而加上保质期,这个问题就变成了了如何将完全背包中的某个物品删除。这个问题无疑是非常困难的。所以不如倒着来,从最后一天开始,一个个将物品加入完全背包。

#include
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)

2018-hyy的练习赛总结_第5张图片

题解

由于   T ≤ 1000 \ T \leq 1000  T1000,所以每一行建一棵线段树就可以了。

如果将   T \ T  T变为   50000 \ 50000  50000,二维线段树可以解决一切问题。

#include
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)

2018-hyy的练习赛总结_第6张图片

题解

乍一看很蒙,但是我们细细想一想就不是很蒙了。

  c = 1 \ c=1  c=1

我们先对一个二进制数考虑:

A = 10101 x x x x x x , A < 10101100000 A=10101xxxxxx,A < 10101100000 A=10101xxxxxx,A<10101100000

  x \ x  x是不确定的数字,有   n u m \ num  num位,已经确定的   1 \ 1  1   s u m \ sum  sum个,从左往右第一个   x \ x  x恒为   0 \ 0  0
因为要计算所有的1的总和,我们先考虑后面未知的部分。如果设某一位为   1 \ 1  1,其他的部分有   2 n u m − 2 \ 2^{num-2}  2num2种可能,所以这一部分的总和为   ( n u m − 1 ) ⋅ 2 n u m − 2 \ (num-1) \cdot 2^{num-2}  (num1)2num2;接着考虑前面的部分。未知的部分可能性有 2 n u m − 1 2^{num-1} 2num1种,所以是   s u m ⋅ 2 n u m − 1 \ sum \cdot 2^{num-1}  sum2num1

现在得出结论,设   n \ n  n的二进制写法中   1 \ 1  1的写法为   n = s t o t s t o t − 1 s t o t − 2 ⋯ s 3 s 2 s 1 , s i ∈ { 0 , 1 } \ n=s_{tot}s_{tot-1}s_{tot-2} \cdots s_{3}s_{2}s_{1},s_{i} \in \{ 0,1 \}  n=stotstot1stot2s3s2s1,si{ 0,1}

答案为:

( ∑ i = 1 t o t s i ) + ∑ i = 1 t o t { s i ⋅ [ ( ∑ j = i + 1 t o t s j ) 2 i − 1 + ( i − 1 ) 2 i − 2 ] } (\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}] \} (i=1totsi)+i=1tot{ si[(j=i+1totsj)2i1+(i1)2i2]}

  c = 2 \ c=2  c=2

和刚才的考虑方法类似,但是只用考虑后面未知的部分

A = 10101 x x x x x x , A < 10101100000 A=10101xxxxxx,A < 10101100000 A=10101xxxxxx,A<10101100000

  x \ x  x是不确定的数字,有   n u m \ num  num位,已经确定的   1 \ 1  1   s u m \ sum  sum个,从左往右第一个   x \ x  x恒为   0 \ 0  0

当有   j \ j  j个不确定的数字为   1 \ 1  1时,所有的情况有   ( s u m − 1 j ) \ \binom{sum-1}{j}  (jsum1)种,显然这一部分的结果为   ( j + s u m ) ( s u m − 1 j ) \ (j+sum)^{\binom{sum-1}{j}}  (j+sum)(jsum1),即可得出结论。

  n \ n  n的二进制写法中   1 \ 1  1的写法为   n = s t o t s t o t − 1 s t o t − 2 ⋯ s 3 s 2 s 1 , s i ∈ { 0 , 1 } \ n=s_{tot}s_{tot-1}s_{tot-2} \cdots s_{3}s_{2}s_{1},s_{i} \in \{ 0,1 \}  n=stotstot1stot2s3s2s1,si{ 0,1}

答案为:

( ∑ i = 1 t o t s i ) ⋅ ∏ i = 1 t o t [ s i ⋅ ∏ j = 0 j < i ( j + s u m ) ( s u m − 1 j ) ] (\sum_{i=1}^{tot}s_{i}) \cdot \prod_{i=1}^{tot} [ s_{i} \cdot \prod_{j=0}^{j<i} (j+sum)^{\binom{sum-1}{j}} ] (i=1totsi)i=1tot[sij=0j<i(j+sum)(jsum1)]

通过一些优化可以减少一些复杂度。

#include
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)

2018-hyy的练习赛总结_第7张图片

题解

展开这个式子,就会得到这样一个形式的东西:

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

是一个等差数列与一个幂的和,这两个都有   O ( log ⁡ n ) \ O( \log n)  O(logn)的做法。

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

s u m p , c = { 1 , c = = 0 p + 1 c = = 1 ( 1 + p c 2 ) s u m p , c 2 − 1 + p c o t h e r sum_{p,c}= \begin{array}{rcl} \begin{cases} 1, & & c==0\\ p + 1 & & c==1\\ (1+p^{\frac{c}{2}})sum_{p,\frac{c}{2}-1}+p^{c} & & other \end{cases} \end{array} sump,c=1,p+1(1+p2c)sump,2c1+pcc==0c==1other

证明略

#include 
#include 
#include 
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)

2018-hyy的练习赛总结_第8张图片

题解

  S T L \ STL  STL   O 2 \ O2  O2下巨快。
  l c p \ lcp  lcp   k m p \ kmp  kmp啊!

#include
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)

2018-hyy的练习赛总结_第9张图片

题解

先考虑   m = 1 \ m=1  m=1的情况,   a i \ a_{i}  ai表示第   i \ i  i个数字。这时矩阵变成区间。

首先:

  •   [ l , l ] \ [l,l]  [l,l]一定是波澜区间。
  •   [ l , r ] , l < r \ [l,r],l < r  [l,r],l<r是波澜区间的要求是任意   x , l ≤ x < r \ x,l \leq x < r  x,lx<r   [ l , x ] \ [l,x]  [l,x]是波澜区间,   [ x + 1 , r ] \ [x+1,r]  [x+1,r]是波澜区间,且   a x = ≠ a x + 1 \ a_{x}= \neq a_{x+1}  ax≠=ax+1

正确性是显然的。这两条使我们可以用线段树维护。

  m > 1 \ m>1  m>1时呢?容易想到建   m \ m  m棵线段树,但是这样复杂度是过不去的 (我卡过9九点)。实际上状态压缩,用二进制表示就可以了。

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

你可能感兴趣的:(总结,模拟赛)