雅礼集训2017day1乱写

t1

区间加,区间除法(下取整),区间最小值,区间求和

这很显然要用线段树了叭

其他操作都是线段树常见操作了,只有除法需要动动脑子

然鹅我们知道一个区间内所有数字应该都只会被除以(log)次(毕竟每次最少减少为(2)倍),所以也许我们可以想一个暴力一点的方法(雾)

考虑一段区间,例如([4,4,4,5,5,3]),这段区间除以三,那么结果会是([1,1,1,1,1,1]),相当于区间赋值操作!

还有另一种可能,例如([2,2,3,3]),这段区间除以三,结果是([0,0,1,1]),相当于区间每个数字(-2)

我们可以把除法操作拆分成这两种操作,对于满足上面两种条件的区间进行操作,复杂度是玄学(反正不会很大)

为了判断上述操作,还要维护一个区间最大值

吐槽(loj)(linux)环境不给开(O2)一度认为自己写假了

#include<bits/stdc++.h>
using namespace std;
namespace red{
#define int long long
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define mid ((l+r)>>1)
#define y1 qwq 
	inline int read()
	{
		int x=0;char ch,f=1;
		for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
		if(ch=='-') f=0,ch=getchar();
		while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
		return f?x:-x;
	}
	const int N=1e5+10,inf=0x3f3f3f3f;
	int n,m,ret;
	int a[N];
	int ans[N<<2],minn[N<<2],maxn[N<<2],tag1[N<<2],tag2[N<<2];
	inline void pushup(int p)
	{
		ans[p]=ans[ls(p)]+ans[rs(p)];
		minn[p]=min(minn[ls(p)],minn[rs(p)]);
		maxn[p]=max(maxn[ls(p)],maxn[rs(p)]);
	}
	inline void build(int l,int r,int p)
	{
		tag1[p]=inf;
		if(l==r)
		{
			ans[p]=minn[p]=maxn[p]=a[l];
			return;
		}
		build(l,mid,ls(p));build(mid+1,r,rs(p));
		pushup(p);
	}
	inline void get1(int l,int r,int p,int k)
	{
		ans[p]=k*(r-l+1);
		minn[p]=maxn[p]=tag1[p]=k;
		tag2[p]=0;
	}
	inline void get2(int l,int r,int p,int k)
	{
		ans[p]+=k*(r-l+1);
		minn[p]+=k,maxn[p]+=k;
		tag2[p]+=k;
	}
	inline void pushdown(int l,int r,int p)
	{
		if(tag1[p]^inf)
		{
			get1(l,mid,ls(p),tag1[p]);
			get1(mid+1,r,rs(p),tag1[p]);
			tag1[p]=inf;
		}
		if(tag2[p])
		{
			get2(l,mid,ls(p),tag2[p]);
			get2(mid+1,r,rs(p),tag2[p]);
			tag2[p]=0;
		}
	}
	inline void update1(int tl,int tr,int l,int r,int p,int k)
	{
		if(tl<=l&&r<=tr) return get2(l,r,p,k);
		pushdown(l,r,p);
		if(tl<=mid) update1(tl,tr,l,mid,ls(p),k);
		if(tr>mid) update1(tl,tr,mid+1,r,rs(p),k);
		pushup(p);
	}
	inline void update2(int tl,int tr,int l,int r,int p,int d)
	{
		if(tl<=l&&r<=tr)
		{
			int t1=(int)floor((double)minn[p]/(double)d);
			int t2=(int)floor((double)maxn[p]/(double)d);
			if(t1==t2) return get1(l,r,p,t1);
			if(minn[p]-t1==maxn[p]-t2) return get2(l,r,p,t1-minn[p]);
		}
		pushdown(l,r,p);
		if(tl<=mid) update2(tl,tr,l,mid,ls(p),d);
		if(tr>mid) update2(tl,tr,mid+1,r,rs(p),d);
		pushup(p);
	}
	inline void query1(int tl,int tr,int l,int r,int p,int &ret)
	{
		if(tl<=l&&r<=tr) return(void)(ret=min(ret,minn[p]));
		pushdown(l,r,p);
		if(tl<=mid) query1(tl,tr,l,mid,ls(p),ret);
		if(tr>mid) query1(tl,tr,mid+1,r,rs(p),ret);
	}
	inline void query2(int tl,int tr,int l,int r,int p,int &ret)
	{
		if(tl<=l&&r<=tr) return(void)(ret+=ans[p]);
		pushdown(l,r,p);
		if(tl<=mid) query2(tl,tr,l,mid,ls(p),ret);
		if(tr>mid) query2(tl,tr,mid+1,r,rs(p),ret);
	}
	inline void main()
	{
		n=read(),m=read();
		for(int i=1;i<=n;++i) a[i]=read();
		build(1,n,1);
		for(int opt,x,y,i=1;i<=m;++i)
		{
			opt=read(),x=read()+1,y=read()+1;
			if(opt==1) update1(x,y,1,n,1,read());
			if(opt==2) update2(x,y,1,n,1,read());
			if(opt==3) {ret=inf;query1(x,y,1,n,1,ret);printf("%lld
",ret);}
			if(opt==4) {ret=0;query2(x,y,1,n,1,ret);printf("%lld
",ret);}
		}
	}
}
signed main()
{
	red::main();
	return 0;
}

t2

比较水的一道构造。。

先确定方案:先把一整行变成黑色,然后拿一整行去涂每一列

无解很好判断,如果一开始就没有任何黑色无解,反之一定有解

我们枚举每一行作为我们选的要变全黑的一整行

这里要分类讨论

对于第(i)行,若第(i)列有黑色,则代价为第(i)行白色数量

若第(i)列没有黑色,就要从其他列借一个,代价为第(i)行白色数量(+1)

最后加上不是全黑的列数

#include<bits/stdc++.h>
using namespace std;
namespace red{
#define int long long
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define mid ((l+r)>>1)
#define y1 qwq 
	inline int read()
	{
		int x=0;char ch,f=1;
		for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
		if(ch=='-') f=0,ch=getchar();
		while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
		return f?x:-x;
	}
	const int N=1010,inf=0x3f3f3f3f;
	int n,m,ret,tmp;
	bool flag;
	char s[N][N];
	int s1[N],s2[N];
	inline void main()
	{
		n=read();ret=inf;
		for(int i=1;i<=n;++i) scanf("%s",s[i]+1);
		for(int i=1;i<=n;++i)
		{
			for(int j=1;j<=n;++j)
			{
				if(s[i][j]=='#') ++s1[i],++s2[j],flag=1;
			}
		}
		if(!flag) return(void)puts("-1");
		for(int i=1;i<=n;++i) tmp+=(s2[i]==n);
		for(int i=1;i<=n;++i)
		{
			if(s2[i]) ret=min(ret,n+n-s1[i]-tmp);
			else ret=min(ret,n+n-s1[i]+1);
		}
		printf("%lld
",ret);
	}
}
signed main()
{
	red::main();
	return 0;
}

t3

硬核题目,根据题意得到(kq<=m),那么我们不知道想到根号分治

先对原字符串建出(SAM)(这种东西一看就要(SAM)吧)

对于(kle sqrt{m})的情况,

考虑枚举(w)的所有子串,算出在(SAM)上的贡献,把每个询问放入(vector pos[l][r])中,用二分寻找([a,b])内的数量

具体操作:对于每个位置(i),找到(w[l,i])(SAM)上对应的节点(这里(l)(s)中存在子串的最左侧位置)

然后枚举(len)的长度,同时对应的节点不断向上跳

复杂度(O(k^2*q*logn)=O(msqrt{m}logn))

对于(kge sqrt{m}),则(qle sqrt{m})

把所有询问((l,r))的长度和编号放入右端点对应的(vector)

然后用上面的枚举节点的方法,不过把枚举长度变成枚举(r)位置上存储的询问,然后用倍增向上跳到对应长度所代表的子串

复杂度(O(q*m*logn)=O(msqrt{m}logn))

#include<bits/stdc++.h>
using namespace std;
namespace red{
#define int long long
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define mid ((l+r)>>1)
#define y1 qwq 
	inline int read()
	{
		int x=0;char ch,f=1;
		for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
		if(ch=='-') f=0,ch=getchar();
		while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
		return f?x:-x;
	}
	const int N=2e5+10,inf=0x3f3f3f3f;
	int n,m,k,q,tot=1,pre=1,lg,rt=1;
	char s[N];
	int fa[N],len[N],endpos[N],son[N][26],f[N][20];
	vector<int> eg[N];
	inline void insert(int c)
	{
		int p=pre,np=pre=++tot;endpos[tot]=1;
		len[np]=len[p]+1;
		for(;p&&!son[p][c];p=fa[p]) son[p][c]=np;
		if(!p) fa[np]=rt;
		else
		{
			int q=son[p][c];
			if(len[q]==len[p]+1) fa[np]=q;
			else
			{
				int nq=++tot;
				for(int i=0;i<26;++i) son[nq][i]=son[q][i];
				len[nq]=len[p]+1;
				fa[nq]=fa[q];
				fa[q]=fa[np]=nq;
				for(;p&&son[p][c]==q;p=fa[p]) son[p][c]=nq;
			}
		}
	}
	inline void sakura(int now)
	{
		for(int i=1;i<=19;++i) f[now][i]=f[f[now][i-1]][i-1];
		for(auto t:eg[now])
		{
			f[t][0]=now;
			sakura(t);
			endpos[now]+=endpos[t];
		}	
	}
	namespace solve1
	{
		vector<int> pos[350][350];
		inline void main()
		{
			for(int l,r,i=0;i<m;++i)
			{
				l=read(),r=read();
				pos[l][r].push_back(i);
			}
			while(q--)
			{
				scanf("%s",s);
				int a=read(),b=read();
				int cur=rt,curlen=0;
				int ans=0;
				for(int i=0;i<k;++i)
				{
					while(cur!=rt&&!son[cur][s[i]-'a'])
						cur=fa[cur],curlen=min(curlen,len[cur]);
					if(son[cur][s[i]-'a'])
					{
						cur=son[cur][s[i]-'a']; 
						++curlen;
						int now=cur;
						for(int plen=curlen;plen;--plen)
						{
							while(len[fa[now]]>=plen) now=fa[now];
							auto& q=pos[i-plen+1][i];
							auto l=lower_bound(q.begin(),q.end(),a);
							auto r=upper_bound(q.begin(),q.end(),b);
							int cnt=r-l;
							ans+=cnt*endpos[now];
						}
					}
				}
				printf("%lld
",ans);
			}
		}
	}
	namespace solve2
	{
		typedef pair<int,int> p;
		vector<p> qlen[N];
		inline void main()
		{
			for(int l,r,i=0;i<m;++i)
			{
				l=read(),r=read();
				qlen[r].push_back(p(r-l+1,i));
			}
			while(q--)
			{
				scanf("%s",s);
				int a=read(),b=read();
				int cur=rt,curlen=0;
				int ans=0;
				for(int i=0;i<k;++i)
				{
					while(cur!=rt&&!son[cur][s[i]-'a'])
						cur=fa[cur],curlen=min(curlen,len[cur]);
					if(son[cur][s[i]-'a'])
					{
						cur=son[cur][s[i]-'a'];
						++curlen;
						for(auto q:qlen[i])
						{
							if(q.second<a||q.second>b) continue;
							if(curlen<q.first) continue;
							int now=cur;
							for(int j=19;~j;--j)
								if(len[f[now][j]]>=q.first) now=f[now][j];
							ans+=endpos[now];
						}
					}
				}
				printf("%lld
",ans);
			}
		}
	}
	inline void main()
	{
		n=read(),m=read(),q=read(),k=read();
		scanf("%s",s);
		for(int i=0;i<n;++i) insert(s[i]-'a');
		for(int i=2;i<=tot;++i) eg[fa[i]].push_back(i);
		sakura(rt);
		if(k<=333) solve1::main();
		else solve2::main();
	}
}
signed main()
{
	red::main();
	return 0;
}
原文地址:https://www.cnblogs.com/knife-rose/p/12735197.html