区间加,区间除法(下取整),区间最小值,区间求和
这很显然要用线段树了叭
其他操作都是线段树常见操作了,只有除法需要动动脑子
然鹅我们知道一个区间内所有数字应该都只会被除以(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;
}
比较水的一道构造。。
先确定方案:先把一整行变成黑色,然后拿一整行去涂每一列
无解很好判断,如果一开始就没有任何黑色无解,反之一定有解
我们枚举每一行作为我们选的要变全黑的一整行
这里要分类讨论
对于第(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;
}
硬核题目,根据题意得到(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;
}