A
思路
不难想到,答案只能为0,1,2…或者直接-1
Code
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 1e2 + 10;
char G[N][N];
void solve(){
int r,c,x,y;
cin >> r >> c >> x >> y;
bool f = 0;
for(int i=1;i<=r;++i)
for(int j=1;j<=c;++j){
cin >> G[i][j];
if(G[i][j] == 'B') f = 1;
}
if(!f){
cout << -1 << '\n';
return;
}
if(G[x][y] == 'B'){
cout << 0 << '\n';
return;
}
//看行、列是否有黑色
int ans = 2;
for(int j=1;j<=c;++j)
if(G[x][j] == 'B') ans = 1;
for(int i=1;i<=r;++i)
if(G[i][y] == 'B') ans = 1;
cout << ans << '\n';
}
signed main(){
std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
int t;
cin >> t;
while(t--)
solve();
return 0;
}
B
思路
根据题目的意思,躲避者会会尽可能远离追求者,那么躲避者肯定只会坐在角落里;
躲避者涂色的时候,必然是从中间开始涂,期望把追求者怼到某个角落,这样使得最远;
但是直接这样模拟不好写,我们可以枚举每个点到角落的最远距离,然后排个序输出即可;
这样的结果跟上面从中间开始涂是相同的;
Code
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
void solve(){
int n,m;
cin >> n >> m;
vector<int> ve;
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
ve.push_back(max(i-1,n-i) + max(j-1,m-j));
sort(ve.begin(),ve.end());
for(int i=0;i<ve.size();++i)
cout << ve[i] << ' ';
cout << '\n';
}
signed main(){
std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
int t;
cin >> t;
while(t--)
solve();
return 0;
}
C
思路
首先,质数除了2以外都是奇数;
题意要我们给边赋权重,任意一个边或两个边权相加都要是质数;
那么必然一个是2,一个是其他质数;
因为奇数加奇数是偶数的,只有偶数加奇数才是奇数;
因此,任意一个点的度数不能超过2;
因为一旦超过2了,那么必然会出现相加后得到偶数,且这个偶数不可能是2;
有了这个结论,我们就可以发现,合法的树必然是一条链;
因为度数的取值只可能是 1 1 1和 2 2 2;
一条链的话就很容易满足题目的要求了,我们一个给 2 2 2,一个给 3 3 3(给其他也行,比如5)交替的进行即可;
Code
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#include <map>
#include <utility>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
int degree[N];
typedef pair<int,int> pii;
map<pii,int> mp;
vector<int> G[N];
void dfs(int u,int fa,bool f){
//肯定是一条链
for(auto to : G[u]){
if(to == fa) continue;
if(f) mp[{u,to}] = mp[{to,u}] = 3;
else mp[{u,to}] = mp[{to,u}] = 2;
dfs(to,u,!f);
}
}
void solve(){
mp.clear();
memset(degree,0,sizeof degree);
int n;
cin >> n;
for(int i=1;i<=n;++i) G[i].clear();
vector<pii> edges;
for(int i=1;i<n;++i){
int u,v;
cin >> u >> v;
edges.push_back({u,v});
++degree[u],++degree[v];
G[u].push_back(v);
G[v].push_back(u);
}
int start = 1;
for(int i=1;i<=n;++i){
if(degree[i] > 2){
cout << -1 << '\n';
return;
}
if(degree[i] == 1) start = i;
}
dfs(start,-1,0);
for(auto it : edges){
cout << mp[it] << ' ';
}
cout << '\n';
}
signed main(){
std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
int t;
cin >> t;
while(t--)
solve();
return 0;
}
D
思路
也就是说,因为gcd
以后,值是不增加的,要么维持不变,要么变小;
因此直接将所有的在集合中的倍数来gcd
,如果能得到当前的数,那么当前的数就是可以的;
然后又因为1e6
的范围,很自然就想到了调和级数的nlogn
;
Code
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 1e6 + 10;
bool vis[N];
int gcd(int a,int b){
if(!b) return a;
return gcd(b,a%b);
}
void solve(){
int n;
cin >> n;
for(int i=1;i<=n;++i){
int x;
cin >> x;
vis[x] = 1;
}
int ans = 0;
for(int i=1;i<=1e6;++i){
if(vis[i]) continue;
int t = 0;
for(int j=2*i;j<=1e6;j+=i){
if(vis[j]){
t = gcd(j,t);
}
}
if(t == i) ++ans;
}
cout << ans << '\n';
}
signed main(){
std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
solve();
return 0;
}
E
题面
思路
我们定义,梯子连接的点 ( a , b ) 和 ( c , d ) (a,b)和(c,d) (a,b)和(c,d)为关键点,其他点为普通点;
f ( i , j ) f(i,j) f(i,j)表示从 ( 1 , 1 ) (1,1) (1,1)到 ( i , j ) (i,j) (i,j)的最小代价;
不难发现,普通点的代价是固定的,只有关键点的代价才需要我们 d p dp dp状态转移;
假设当前在 ( i , j ) (i,j) (i,j),这个点可能被左边的 ( i , y 1 ) (i,y_1) (i,y1)、右边的 ( i , y 2 ) (i,y_2) (i,y2)以及楼梯连接的 ( a , b ) (a,b) (a,b)更新;
也就是说,我们这个点会被当前层的点更新以及楼梯连接的点更新;
被楼梯连接的转移方程很容易写出;
f[c][d] = min(f[c][d],f[i][j] - h);
被同层的点更新,如果我们直接考虑 O ( n 2 ) O(n^2) O(n2)枚举肯定会超时的;
因为只能左右移动,因此我们可以考虑用前缀最小值和后缀最小值,这样是 O ( n ) O(n) O(n)的;
对于无解判断需要注意,给 f ( n , m ) f(n,m) f(n,m)赋INF可能会变小;
因此有if(f[n][m] >= INF/2) cout << "NO ESCAPE\n";
Code
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <map>
#include <vector>
using namespace std;
typedef long long ll;
#define int ll
const int N = 1e6 + 10;
const int INF = 1e18;
int x[N];
map<int,vector<array<int,3>> > ladder[N];
map<int,int> f[N];//f(i,j)表示从(1,1)到(i,j)的最小花费
void solve(){
int n,m,k;
cin >> n >> m >> k;
for(int i=1;i<=n;++i) ladder[i].clear(),f[i].clear();
for(int i=1;i<=n;++i) cin >> x[i];
for(int i=1;i<=k;++i){
int a,b,c,d,h;
cin >> a >> b >> c >> d >> h;
//<a,b>可能有多个梯子,比如<a,b> -> <c1,d1> , <a,b> -> <c2,d2>
ladder[a][b].push_back({c,d,h});
}
//全部初始化为INF
for(int i=1;i<=n;++i)
for(auto x : ladder[i]){
auto j = x.first;
auto ve = x.second;
f[i][j] = INF;
for(auto y : ve){
auto c = y[0];
auto d = y[1];
f[c][d] = INF;
}
}
f[1][1] = 0;
f[n][m] = INF;
for(int i=1;i<=n;++i){
//先处理当前层的关键点
int mn = INF,last = 1;
//从左往右更新前缀最小值
for(auto it : f[i]){
auto j = it.first;
//前缀最小要么从当前点取 要么从左边取
f[i][j] = mn = min(f[i][j],mn + (j - last) * x[i]);
last = j;
}
mn = INF,last = m;
//从右往左更新后缀最小值
for(auto it = f[i].rbegin();it != f[i].rend();++it){
//后缀最小要么从当前点取 要么从右边取
int j = it->first;
f[i][j] = mn = min(f[i][j],mn + (last - j) * x[i]);
last = j;
}
//再更新从当前层的关键点爬楼梯上去的上面一层关键点
for(auto x : ladder[i]){
auto j = x.first;
auto vec = x.second;
for(auto x : vec){
auto c = x[0];
auto d = x[1];
auto h = x[2];
f[c][d] = min(f[c][d],f[i][j] - h);
}
}
}
if(f[n][m] >= INF/2) cout << "NO ESCAPE\n";
else cout << f[n][m] << '\n';
}
signed main(){
std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
int t;
cin >> t;
while(t--)
solve();
return 0;
}