2023 ICPC 南京

10.5
想要袋鼠。
赛时5题
深刻感觉到代码能力瓶颈。

I

签到

C

也是签到,需要枚举的次数很少。

F

似乎是签到但是队友debug卡了一百年,晚点补一下看看

G

xixike秒的

L

思路就是贪心。
我写了两遍错的,xixike重构了一下把能合并的都合并了就过了。

A

比较显然的是连通块里面的袋鼠都胜负状态都相同,证明有点难但是可证。不过这样想是做不了的。
因为是连通块所以考虑建图来做。
实现上首先把可行的ij和xy连边,然后可以把能相互转移的状态放到一起去,对于必胜态就放到队列里,最后从必胜态开始遍历转移,求出所有袋鼠的胜负关系。
然后暴力枚举看该袋鼠是否能胜过其他所有的袋鼠。
刚好能卡过。。但是我这个跑了1000ms题解只跑200ms。。。但是个人觉得这个还是好理解的。。

#include<bits/stdc++.h>
using namespace std;
#define CI const int&
const int maxn=5e3+10;

queue<int>q;
string a[maxn];
int n,m,dx[4]={0,1,0,-1},dy[4]={1,0,-1,0};
bool vis[maxn*maxn];
vector<int>vt[maxn*maxn];


inline int encode(CI a,CI b,CI c,CI d)
{
	return
        a * m * n * m +
        b * n * m +
        c * m +
        d;
}
// void decode(int code,int &x,int &y,int &i,int &j){
//     x=code/((n+1)*(n+1)*(m+1));
//     y=code/((n+1)*(m+1))%(m+1);
//     i=code/(n+1)%(n+1);
//     j=code%(m+1);
// }
void link(int i,int j,int x,int y){
    for(int k=0;k<4;k++){
        int i2=i+dx[k],j2=j+dy[k];
        if(a[i2][j2]=='O'||i2<1||j2<1||i2>n||j2>m)continue;
        int x1=x+dx[k],y1=y+dy[k];
        if(a[x1][y1]=='O'||x1<1||y1<1||x1>n||y1>m){
            q.push(encode(i,j,x,y));vis[encode(i,j,x,y)]=1;
        }
        else{
            vt[encode(i2,j2,x1,y1)].push_back(encode(i,j,x,y));
        }
    }
}
void solve(){
    cin>>n>>m;
    for(int i=1;i<=n;i++)cin>>a[i],a[i]='1'+a[i];
    //join
    for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)if(a[i][j]=='.')
        for(int x=1;x<=n;x++)for(int y=1;y<=m;y++)
            if(x!=i||y!=j)if(a[x][y]=='.')link(i,j,x,y);
    //bfs
    while(!q.empty()){
        int nw=q.front();q.pop();
        for(auto to:vt[nw])
            if(!vis[to])
                vis[to]=1,q.push(to);
    }
    //cnt ans
    int ans=0;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)if(a[i][j]=='.'){
        int f=1;
        for(int x=1;x<=n&&f;x++)for(int y=1;y<=m&&f;y++)
        if(x!=i||y!=j)if(a[x][y]=='.')if(vis[encode(i,j,x,y)]==0)f=0;
        ans+=f;
    }
    cout<<ans<<endl;
    //clear
    for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)if(a[i][j]=='.')
        for(int x=1;x<=n;x++)for(int y=1;y<=m;y++)
            if(x!=i||y!=j)if(a[x][y]=='.')vt[encode(i,j,x,y)].clear(),vis[encode(i,j,x,y)]=0;
}
signed main(){
    ios::sync_with_stdio(false);
    int tt;cin>>tt;while(tt--)solve();
}

M

我竟然能单人开出中档题思路,线段树真是没白写。
首先注意到有贡献的位置会形成一个单峰数列,那么修改的时候其实改变的贡献只有相邻的位置,所以不需要修改太多。可以预处理贡献然后线段树需要支持求出左边第一个大于的和右边第一个大于的,以及查询区间贡献这三个功能,就能做。
但是这个二分不能是log方必须是单log就有点麻烦。
线段树可以维护但是很难写,我不会。题解支持了删除操作,我也不会写。

posted @ 2024-10-08 19:45  lyrrr  阅读(19)  评论(0编辑  收藏  举报