No Time to Dry
题意:
给你2e5个数,和2e5组询问。
序列代表的是最后涂成的油漆,大的数只能涂在小的数上面,而小的数不能涂在大的数上面。每次询问一个区间,求最小的操作数使得涂成上面需要的序列。
思路:
线段树 + 单调栈
涂油漆必备套路,单调栈,能预处理处最初的涂色操作数。
这道题的核心在于 离线(想到了,可惜没写),这个数据量在线是不可能的。
先把处理出next数组,就能跳同一个数的下一个位置,
那么我们就把询问离线,按照l为第一关键字,r为第二关键字排序,当区间的左边界移动时,用next数组修改下一个数为1即可,区间答案就用线段树查询就好了。
int n,m;
struct node{
int l,r,id;
}z[200060];
int ans[200060];
int s[200060];
bool cmp(node a,node b){
if(a.l!=b.l)return a.l<b.l;
return a.r<b.r;
}
map<int,int>mp;
int nex[200060];
int tree[800070];
int sss[200060];
void build(int p,int l,int r){
if(l==r){
tree[p]=sss[l];
return;
}
int mid=l+r>>1;
build(2*p,l,mid);
build(2*p+1,mid+1,r);
tree[p]=tree[2*p]+tree[2*p+1];
}
void update(int p,int l,int r,int x,int y,int w){
if(l==r){
tree[p]=w;
return;
}
int mid=l+r>>1;
if(x<=mid)update(2*p,l,mid,x,y,w);
if(mid<y)update(2*p+1,mid+1,r,x,y,w);
tree[p]=tree[2*p]+tree[2*p+1];
}
int query(int p,int l,int r,int x,int y){
if(x<=l&&r<=y){
return tree[p];
}
int mid=l+r>>1,ans=0;
if(x<=mid)ans+=query(2*p,l,mid,x,y);
if(mid<y)ans+=query(2*p+1,mid+1,r,x,y);
return ans;
}
stack<int>ss;
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)s[i]=qread();
for(int i=1;i<=m;i++){
z[i].l=qread(),z[i].r=qread();
z[i].id=i;
}
sort(z+1,z+1+m,cmp);
for(int i=1;i<=n;i++){
if(mp[s[i]]==0)mp[s[i]]=i;
else {
nex[mp[s[i]]]=i;
mp[s[i]]=i;
}
nex[i]=n+1;
}
s[0]=-1;
ss.push(0);
for(int i=1;i<=n;i++)
{
while(!ss.empty()&&s[ss.top()]>s[i]){
ss.pop();
}
if(s[ss.top()]!=s[i]){
sss[i]=1;
}
ss.push(i);
}
build(1,1,n);
int l=1;
for(int i=1;i<=m;i++){
while(l<z[i].l){
if(nex[l]!=n+1)update(1,1,n,nex[l],nex[l],1);
l++;
}
ans[z[i].id]=query(1,1,n,z[i].l,z[i].r);
}
for(int i=1;i<=m;i++){
printf("%d\n",ans[i]);
}
return 0;
}
Just Green Enough
题意:
给你一个 N*N 的矩阵(N<=500),要求求出最小值刚好是100的矩阵的个数。
思路:
这道题N^4,可以很简单的解决,那么我们考虑优化到N³ ;
核心想法:将每个 大于等于100 的数看成1,将小于100的数看成0,计算出全1·矩阵数;
考虑化简问题,最小值恰好等于100的矩阵数==最小值大于等于100的矩阵数 - 最小值大于100的矩阵数。
然后 N² 处理出 next 数组,表示下一个 0 的位置,然后暴力枚举矩阵的左上角,用 next 数组得到边长,然后遍历之后的每行,边长取 min 得到长为此时的宽,加到答案里即可。
太秒啦~
int n,m;
int mp[505][505];
int nex[505][505];
int s[505][505];
void init1(){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(mp[i][j]>=100)s[i][j]=1;
else s[i][j]=0;
}
}
for(int i=1;i<=n;i++){
if(s[i][n]==1)nex[i][n]=n+1;
else nex[i][n]=n;
for(int j=n-1;j>=1;j--){
if(s[i][j]==1)nex[i][j]=nex[i][j+1];
else nex[i][j]=j;
}
}
}
void init2(){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(mp[i][j]>100)s[i][j]=1;
else s[i][j]=0;
}
}
for(int i=1;i<=n;i++){
if(s[i][n]==1)nex[i][n]=n+1;
else nex[i][n]=n;
for(int j=n-1;j>=1;j--){
if(s[i][j]==1)nex[i][j]=nex[i][j+1];
else nex[i][j]=j;
}
}
}
ll solve(){
ll res=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(s[i][j]==0)continue;
int t=nex[i][j];
res+=t-j;
for(int k=i+1;k<=n;k++){
t=min(t,nex[k][j]);
res+=t-j;
}
}
}
return res;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
mp[i][j]=qread();
}
}
ll ans=0;
init1();
ans=solve();
init2();
ans=ans-solve();
printf("%lld\n",ans);
return 0;
}