Algoritmos para Grillas y Secuencias en Programación Competitiva

Problema 1: Grilla

La solución inicial de 60 puntos utiliza un enfoque ineficiente O(n⁴). La versión óptima mantiene componentes conectados mediante una estructura union-find. Al desplazar una ventana de m×m, solo se actualizan las dos columnas afectadas. Se calcula el área máxima sumando el tamaño de los componentes adyacentes a la ventana.

#include<iostream>
#include<vector>
#include<algorithm>
#define ll long long
using namespace std;
const int L=505;
int gridSize,windowSize,maxConnected,tempSum,total,grid[L][L],parent[L*L],compSize[L*L];
int dx[4]={0,0,1,-1},dy[4]={1,-1,0,0};
vector<int> buffer;

int getIndex(int x,int y){return (x-1)*gridSize+y;}
int findRoot(int x){
    if(parent[x]!=x) parent[x]=findRoot(parent[x]);
    return parent[x];
}

void initWindow(int row,int col){
    tempSum=0; buffer.clear();
    for(int i=row;i<row+windowSize;i++)
        for(int j=col;j<col+windowSize;j++)
            if(grid[i][j]) compSize[findRoot(getIndex(i,j))]--;
    
    for(int i=1;i<=windowSize;i++){
        if(row>1&&grid[row-1][i]) buffer.push_back(findRoot(getIndex(row-1,i)));
        if(row+windowSize<=gridSize&&grid[row+windowSize][i]) buffer.push_back(findRoot(getIndex(row+windowSize,i)));
        if(grid[row+i-1][windowSize+1]) buffer.push_back(findRoot(getIndex(row+i-1,windowSize+1)));
    }
    sort(buffer.begin(),buffer.end());
    buffer.erase(unique(buffer.begin(),buffer.end()),buffer.end());
    for(auto& idx:buffer) tempSum+=compSize[idx];
    maxConnected=max(maxConnected,windowSize*windowSize+tempSum);
}

void slideWindow(int row,int col){
    for(int i=1;i<=windowSize;i++){
        if(grid[row+i-1][col-1]) compSize[findRoot(getIndex(row+i-1,col-1))]++;
        if(grid[row+i-1][col+windowSize-1]) compSize[findRoot(getIndex(row+i-1,col+windowSize-1))]--;
    }
    buffer.clear(); tempSum=0;
    for(int i=1;i<=windowSize;i++){
        if(row>1&&grid[row-1][col+i-1]) buffer.push_back(findRoot(getIndex(row-1,col+i-1)));
        if(col>1&&grid[row+i-1][col-1]) buffer.push_back(findRoot(getIndex(row+i-1,col-1)));
        if(row+windowSize<=gridSize&&grid[row+windowSize][col+i-1]) buffer.push_back(findRoot(getIndex(row+windowSize,col+i-1)));
        if(col+windowSize<=gridSize&&grid[row+i-1][col+windowSize]) buffer.push_back(findRoot(getIndex(row+i-1,col+windowSize)));
    }
    sort(buffer.begin(),buffer.end());
    buffer.erase(unique(buffer.begin(),buffer.end()),buffer.end());
    for(auto& idx:buffer) tempSum+=compSize[idx];
    maxConnected=max(maxConnected,windowSize*windowSize+tempSum);
}

void restoreWindow(int row,int col){
    for(int i=row;i<row+windowSize;i++)
        for(int j=col;j<col+windowSize;j++)
            if(grid[i][j]) compSize[findRoot(getIndex(i,j))]++;
}

int main(){
    ios::sync_with_stdio(0);
    cin>>gridSize>>windowSize;
    if(windowSize>=gridSize){cout<<gridSize*gridSize;return 0;}
    for(int i=1;i<=gridSize;i++){
        string s; cin>>s;
        for(int j=1;j<=gridSize;j++){
            compSize[++total]=grid[i][j]=(s[j-1]=='.');
            parent[total]=total;
        }
    }
    for(int i=1;i<=gridSize;i++)
        for(int j=1;j<=gridSize;j++)
            if(grid[i][j])
                for(int k=0;k<4;k++){
                    int nx=i+dx[k],ny=j+dy[k];
                    if(nx<1||nx>gridSize||ny<1||ny>gridSize||!grid[nx][ny]) continue;
                    int u=findRoot(getIndex(i,j)),v=findRoot(getIndex(nx,ny));
                    if(u==v) continue;
                    compSize[v]+=compSize[u];
                    parent[u]=v;
                }
    for(int i=1;i<=gridSize-windowSize+1;i++){
        initWindow(i,1);
        for(int j=2;j<=gridSize-windowSize+1;j++) slideWindow(i,j);
        restoreWindow(i,gridSize-windowSize+1);
    }
    cout<<maxConnected;
}

Problema 2: Secuencia

Se resuelve con programación dinámica optimizada mediante CDQ. La transición de j a i requiere que i>j, s[i]>s[j] y s[i]-i≤s[j]-j. Se utiliza un árbol de Fenwick para manejar la tercera condición después de comprimir coordenadas.

#include<iostream>
#include<algorithm>
#include<vector>
#define ll long long
using namespace std;
const int N=5e5+10;
int n,ans,cnt,dp[N],temp[N];
struct Element{int id,val,comp;};
Element arr[N];

struct FenwickTree{
    int tree[N];
    int lowbit(int x){return x&-x;}
    void reset(int pos){for(int i=pos;i;i-=lowbit(i))tree[i]=0;}
    void update(int pos,int val){for(int i=pos;i;i-=lowbit(i))tree[i]=max(tree[i],val);}
    int query(int pos){int res=0;for(int i=pos;i<=cnt;i+=lowbit(i))res=max(res,tree[i]);return res;}
}BIT;

void CDQ(int l,int r){
    if(l==r){dp[l]=max(dp[l],(arr[l].val<=n)*1LL);return;}
    int mid=(l+r)>>1;
    CDQ(l,mid);
    sort(arr+l,arr+mid+1,[](Element a,Element b){
        return a.val==b.val?a.comp<b.comp:a.val<b.val;
    });
    sort(arr+mid+1,arr+r+1,[](Element a,Element b){
        return a.val==b.val?a.comp<b.comp:a.val<b.val;
    });
    int i=l;
    for(int j=mid+1;j<=r;j++){
        while(i<=mid&&arr[i].val<=arr[j].val){
            if(arr[i].val<=arr[i].id) BIT.update(arr[i].comp,dp[arr[i].id]);
            i++;
        }
        if(arr[j].val>arr[l].val&&i>l) 
            dp[arr[j].id]=max(dp[arr[j].id],BIT.query(arr[j].comp)+1);
    }
    for(int k=l;k<i;k++) 
        if(arr[k].val<=arr[k].id) BIT.reset(arr[k].comp);
    sort(arr+mid+1,arr+r+1,[](Element a,Element b){return a.id<b.id;});
    CDQ(mid+1,r);
}

int main(){
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>arr[i].val;
        arr[i].id=i;
        temp[i]=arr[i].val-i;
    }
    sort(temp+1,temp+n+1);
    cnt=unique(temp+1,temp+n+1)-temp-1;
    for(int i=1;i<=n;i++) 
        arr[i].comp=lower_bound(temp+1,temp+cnt+1,arr[i].val-i)-temp;
    CDQ(1,n);
    for(int i=1;i<=n;i++) ans=max(ans,dp[i]);
    cout<<ans;
}

Etiquetas: grid-graph disjoint-set cdq binary-indexed-tree dynamic-programming

Publicado el 6-28 20:57