题意:给你一个正整数 N 和一个长度为 N 的正整数序列 A=(A1,A2,…,AN)。请判断 A 是否严格递增
思路:模拟即可
// Code Start Here
int n;
cin >> n;
vector<int> a(n);
bool f = true;
for(auto &it : a)cin >> it;
for(int i = 0;i<n-1;i++){
if(a[i] >= a[i+1])f = false;
}
if(f)cout <<"Yes\n";
else cout <<"No\n";
B:
题意:
给你一个正整数 N 。
考虑一个 N×N 网格。让 (i,j)表示从上往下第 i 行,从左往上第 j 列的单元格。最初,没有单元格被着色。
然后,依次对 i=1,2,…,N进行以下操作:
- 让 j=N+1−i
- 如果是 i≤j,则在左上角单元格为 (i,i)、右下角单元格为 (j,j) 的矩形区域填充黑色(如果 i为奇数),如果 i 为偶数,则填充白色。如果某些单元格已经着色,则覆盖它们的颜色。
- 如果是 i>j,则不做任何操作。
经过所有这些操作后,可以证明没有未着色的单元格。确定每个单元格的最终颜色。
思路:N < 50 模拟即可
// Code Start Here
int n;
cin >> n;
vector<vector<char>> g(n+1,vector<char>(n+1));
for(int i = 1;i<=n;i++){
int j = n + 1 - i;
if(i <= j){
for(int now_i = i;now_i<=j;now_i++){
for(int now_j = i;now_j <=j;now_j++){
if(i & 1)g[now_i][now_j] = '#';
else g[now_i][now_j] = '.';
}
}
}
}
for(int i = 1;i<=n;i++){
for(int j = 1;j<=n;j++){
cout << g[i][j];
}
cout << endl;
}
C
题意:
给你一个正整数 N 和一个长度为 N 的整数序列 A=(A1,A2,…,AN) 。
请判断 A 是否存在一个非空(连续)的子数组,它有一个重复值,多次出现在 A 中。如果存在这样的子数组,求最短的子数组的长度。
思路:由题意有,对于一个ai,找下一个理他最近的ai,注意到N < 1e5所以双重遍历不理想,思考把相同的元素融入到一个vector里面,每次看vector里面两个相邻的元素即可,转化到On左右
// Code Start Here
int n;
cin >> n;
map<int,vector<int>> g;
vector<int> a(n+1);
for(int i = 1;i<=n;i++)cin >> a[i];
for(int i = 1;i<=n;i++){
g[a[i]].push_back(i);
}
int ans = INT_MAX;
for(auto [val,now] : g){
for(int i = 0;i<(int)(now.size()) - 1;i++){
ans = min(now[i+1] - now[i] , ans);
}
}
if(ans == INT_MAX)cout <<"-1\n";
else cout << ans + 1 <<endl;
题意:
有 N 只标有 1,2,…,N 的鸽子和 N 个标有 1,2,…,N 的鸽巢。
最初,鸽子 i(1≤i≤N) 在巢 ii 中。
您将对这些鸽子执行 Q 操作。
操作有三种:
- 类型 1 :给你整数 a 和 b 。 (1≤a≤N,1≤b≤N).将鸽子 a 从当前巢中取出,移到巢 b 中。
- 输入 2 :给你整数 a 和 b 。 (1≤a<b≤N) .将巢 a 中的所有鸽子移动到巢 b ,并将巢 b 中的所有鸽子移动到巢 a 。这两次移动同时进行。
- 输入 3 :给你一个整数 a 。 (1≤a≤N) .鸽子 a 报告它当前所在鸽巢的标签。
按照给出的操作顺序打印每个类型 3 操作的结果。
思路:简单来想肯定能想到对于每个巢穴开一个set模拟,然后存储一下每个鸽子的巢穴pos,然后根据题意模拟一下即可
但是这样显然有一个问题,在对于2操作进行模拟的时候,要把两个巢穴的鸽子全部清空,然后互相copy,当N过大时,两个互相copy的过程能达到On^2,显然超时
// TLE version
// Code Start Here
int n , q;
cin >> n >> q;
vector<int> pos(n+1,-1);
for(int i = 1;i<=n;i++)pos[i] = i;
vector<set<int>> g(n+1);
for(int i = 1;i<=n;i++)g[i].insert(i);
while(q--){
int op;
cin >> op;
if(op == 1){
int a , b;
cin >> a >> b;
g[pos[a]].erase(a);
pos[a] = b;
g[pos[a]].insert(a);
}
else if(op == 2){
int a , b;
cin >> a >> b;
vector<int> now_a;
vector<int> now_b;
for(auto it = g[a].begin() ; it != g[a].end() ; it++){
now_a.push_back(*it);
}
for(auto it = g[b].begin() ; it != g[b].end() ; it++){
now_b.push_back(*it);
}
g[a].clear();
g[b].clear();
for(auto i : now_a){
pos[i] = b;
g[pos[i]].insert(i);
}
for(auto i : now_b){
pos[i] = a;
g[pos[i]].insert(i);
}
}
else{
int a;
cin >> a;
cout << pos[a] << endl;
}
}
考虑一下优化方式,不涉及的巢穴中去鸽子,2操作的实质就是把两个巢穴的标签编号改变一下,所以开一个数组记录一下当前巢穴的“真”编号,然后模拟即可
// Code Start Here
int n , q;
cin >> n >> q;
vector<int> pos(n+1,-1);
vector<int> nest(n+1,-1);
vector<int> head(n+1,-1);
for(int i = 1;i<=n;i++){
pos[i] = i;
head[i] = i;
nest[head[i]] = i;
}
while(q--){
int op;
cin >> op;
if(op == 1){
int a , b;
cin >> a >> b;
pos[a] = head[b];
}
else if(op == 2){
int a , b;
cin >> a >> b;
int temp = nest[head[a]];
nest[head[a]] = nest[head[b]];
nest[head[b]] = temp;
temp = head[a];
head[a] = head[b];
head[b] = temp;
}
else{
int a;
cin >> a;
cout << nest[pos[a]] << endl;
}
}
题意:在一个有向图中,我们从节点1出发,要通过两种操作到达节点N。操作一是沿着当前的有向边移动,消耗1的代价。操作二是翻转所有边的方向,消耗X的代价。我们的目标是找到到达节点N的最小总代价
思路:
我们发现对于每个状态有两种可能:
1.沿着原始边移动
2.沿着反转边移动
记录一下两种情况下的最小值,然后跑一遍dijk即可
// Code Start Here
int n , m , x;
cin >> n >> m >> x;
vector<vector<int>> ore(n+1);
vector<vector<int>> inv(n+1);
vector<vector<int>> dis(n+1,vector<int>(2,LLONG_MAX));
for(int i = 1;i<=m;i++){
int u , v;
cin >> u >> v;
ore[u].push_back(v);
inv[v].push_back(u);
}
dis[1][0] = 0;
priority_queue<pair<int,pair<int,int>>,vector<pair<int,pair<int,int>>> ,greater<pair<int,pair<int,int>>>> pq;
pq.push({0,{1,0}});
while(!pq.empty()){
auto cur = pq.top();
pq.pop();
int now_val = cur.first;
int u = cur.second.first;
int op = cur.second.second;
if(u == n){
cout << now_val << endl;
return 0;
}
if(op == 0){
for(int v : ore[u]){
if(dis[v][op] > now_val + 1){
dis[v][op] = now_val + 1;
pq.push({dis[v][op],{v , 0}});
}
}
}
else{
for(int v : inv[u]){
if(dis[v][op] > now_val + 1){
dis[v][op] = now_val + 1;
pq.push({dis[v][op],{v,1}});
}
}
}
if(dis[u][1-op] > now_val + x){
dis[u][1-op] = now_val + x;
pq.push({dis[u][1-op],{u,1-op}});
}
}
题意:
高桥有 2N 颗牙齿:上牙有 N 颗,下牙有 N 颗。
左起第 i 颗上牙( 1≤i≤N)的长度是 Ui ,左起第 i 颗下牙( 1≤i≤N )的长度是 Di 。
如果同时满足以下两个条件,那么他的牙齿就可以说是 "合得很好":
- 存在一个整数 H ,使得每个整数 i 的 Ui+Di=H 与 1≤i≤N .
- 每个整数 i 都有 ∣Ui−Ui+1∣≤X 与 1≤i<N 。
他可以多次进行下面的运算:
- 支付 1 日元使用磨齿机,选择一个长度为正数的牙齿,并将其长度减少 1 。
不得使用其他方法改变牙齿的长度。
求为了使牙齿整齐排列,他至少需要支付的总金额。
思路:注意到H 必须满足 Ui+Di=H对于所有 i,所以 H 不能超过任何一对牙齿的总长度,所以需要确定最大可能的H。然后记录一下后续牙齿与U_i-1的范围计算一下新的范围,最后计算一下最小成本。
// Code Start Here
int n , x;
cin >> n >> x;
vector<int> u(n),d(n),s(n);
int sum = 0;
for(int i = 0;i<n;i++){
cin >> u[i] >> d[i];
s[i] = u[i] + d[i];
sum += s[i];
}
int max_val = *min_element(s.begin(),s.end());
auto check = [&](int h)->bool{
int low = max(0 * 1LL , h - d[0]);
int high = min(h , u[0]);
if( low > high)return false;
for(int i = 1;i<n;i++){
int l = max(0 * 1LL , h - d[i]);
int r = min(h , u[i]);
if(l > r)return false;
int nl = max(l , low - x);
int nr = min(r , high + x);
if(nl > nr)return false;
low = nl;
high = nr;
}
return true;
};
int l = 0 , r = max_val;
int ans = 0;
while(l <= r){
int mid = ( l + r ) / 2;
if(check(mid)){
ans = mid;
l = mid + 1;
}
else r = mid - 1;
}
cout << sum - ans * n << endl;