比赛地址:https://ac.nowcoder.com/acm/contest/60063

先说好消息,搞出模拟了。
再说坏消息,就A了三个.......DE真的很巧妙,尤其D

D:大意是给出给出n*m的矩阵,从11走到nm。求最小花费时间。
两类操作,向上下左右四个反向走,如果与当前点标记不同。
更改下个要走的点,标记使其相反。

个人做法:诈骗DP。我的做法是f[i][0]表示当前点没有被更改时,f[i][1]表示当前点被更改时。然而不可以。过了84

正解:曼哈顿距离判断和1,1的距离,可以得出走了几步到这个点。然后根据奇偶性可以判断是否需要更改才可以到达,因此可以得到边权。
优先队列存储点位,dis[n][m]表示到1,1的距离。djstla搞一下即可。

code:

void solve()
{
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        cin>>c[i]+1;
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++)
            dis[i][j] = INF;
    }
    priority_queue<pair<int,pair<int,int>>> p;
    dis[1][1] = 0;
    p.push({0,{1,1}});
    while(!p.empty()){
        auto t = p.top().se;
        p.pop();
        if(sign[t.fi][t.se])
            continue;
        sign[t.fi][t.se]=1;
        for(int k=0;k<4;k++){
            int nx = t.fi+fx[k],ny = t.se+fy[k];
            int w;
            if(nx>=1&&nx<=n&&ny>=1&&ny<=m){
                if((nx+ny)&1){
                    if(c[nx][ny]==c[1][1]){
                        w = 2;
                    }else w = 1;
                }else{
                    if(c[nx][ny]==c[1][1]){
                        w = 1;
                    }else w = 2;
                }
            }
            if(dis[nx][ny]>dis[t.fi][t.se]+w){
                dis[nx][ny] = dis[t.fi][t.se]+w;
                p.push({-dis[nx][ny],{nx,ny}});
            }
        }
    }
    cout<<dis[n][m]<<endl;
}

E:大意是给出n和m,求构造数量,每个位上的数仅为m以内。求满足前缀是当前i的倍数的数量。

解法:考虑DP。f[i][j]表示前i个数,总和为i*j的数量。可以转移的是可以到达的i+1的量。直接拿总量去除以i+1,然后开始枚举到m。
复杂度是调和级数,这里没看出来,很惭愧....
剩下直接累加即可,难点是转移从哪里来优化?

code:

void solve()
{
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		f[1][i] = 1;
	}
	//f[i][j]表示前i个数,总和为i*j的数量。需要转到可以影响的k位置。从最小影响开始遍历,复杂度是调和级数。
	for(int i=2;i<=n;i++){
		for(int j=1;j<=m;j++){
			ll st = (i-1)*j/i+1;
			ll ed = m;
			for(int k=st;k<=m;k++){
				if(i*k-(i-1)*j>m) break;
				f[i][k]+=f[i-1][j];
				f[i][k]%=mod7;
			}
		}
	}
	ll ans = 0;
	for(int i=1;i<=m;i++){
		ans+=f[n][i];
		ans%=mod7;
	}
	cout<<ans<<endl;
}