C题-Helping the Nature
思路
- 直接的思路:使得每棵相邻树的高度相同。做法只能是对每棵相邻的树,根据大小处理前缀或后缀之一,相同高度为两者的较小值,且这些处理序列一个也不能少。
- 差分的思路(转):
其中 d 1 = a [ 1 ] − a [ 0 ] d_1 = a[1] - a[0] d1=a[1]−a[0],其中 a [ 0 ] = 0 a[0]=0 a[0]=0
看到区间操作就想到前缀和和差分。属于常用套路
#include <bits/stdc++.h>
using namespace std;
#include<stack>
#define int long long
signed main()
{
int t;
cin >> t;
for(int i = 0;i<t;i++)
{
int n;
cin >> n;
int a[n];
for(int k = 0;k<n;k++) cin>>a[k];
int ans = 0;
int temp = a[n - 1];
for(int k = n - 1;k>=1;k--)
{
if(a[k] - a[k - 1] >= 0) //后缀相减使其等于前缀
{
ans += (a[k] - a[k - 1]);
temp -= (a[k] - a[k - 1]);
}
else
{
ans += (a[k - 1] - a[k]);
}
}
cout << ans + abs(temp) << endl;
}
system("pause");
}
D题-Helping The Nature
思路
- 必要条件减小搜索空间
- 找是否能满足DP
设 D P [ i ] DP[i] DP[i],处理到第i个lock,前i个lock全部开启,并装满前i个lock所需要的最短时间。guess:
- 若第i个lock能在前面i-1个lock装满的时间之前或刚好装满,则 D P [ i ] = D P [ i − 1 ] DP[i] = DP[i-1] DP[i]=DP[i−1]
- 否则,前i-1个lock溢出的水与第i个lock管道的水会相互混合。可看作是前i个管道向前i个lock注水。所需时间 ⌈ s u m ( v [ 1 : i ] ) i ⌉ \lceil \frac{sum(v[1:i])}{i}\rceil ⌈isum(v[1:i])⌉
所以
D
P
[
i
]
=
m
a
x
(
D
P
[
i
−
1
]
,
⌈
s
u
m
(
v
[
1
:
i
]
)
i
⌉
)
DP[i] = max(DP[i-1],\lceil \frac{sum(v[1:i])}{i}\rceil)
DP[i]=max(DP[i−1],⌈isum(v[1:i])⌉)
对每个query,必要条件是
t
q
u
e
r
y
>
=
d
p
[
n
]
t_{query} >= dp[n]
tquery>=dp[n].在这样的时间段内,若取q个管道,可保证有充足的时间将这前q个装满。
- 若开启前q个管道而在 d p [ n ] dp[n] dp[n]的时间内所有水库已经装满,则q一定满足条件。且一定有 t q u e r y ⋅ q ≥ d p [ n ] ⋅ q ≥ s u m ( v [ 1 : n ] ) t_{query} \cdot q \geq dp[n] \cdot q\geq sum(v[1:n]) tquery⋅q≥dp[n]⋅q≥sum(v[1:n])
- 否则可看作前q的管道同时注水,速率混合。因此需要保证在 t q u e r y t_{query} tquery的时间内,以q速率,能将水库注满,也有: t q u e r y ⋅ q ≥ s u m ( v [ 1 : n ] ) t_{query} \cdot q \geq sum(v[1:n]) tquery⋅q≥sum(v[1:n])
因此给定q,只需检查
t
q
u
e
r
y
⋅
q
≥
s
u
m
(
v
[
1
:
n
]
)
t_{query} \cdot q \geq sum(v[1:n])
tquery⋅q≥sum(v[1:n])是否满足即可。找到最小这样的q即可。
#include <bits/stdc++.h>
using namespace std;
#include<stack>
#define int long long
signed main()
{
int n;
cin >> n;
int v[n];
int prev[n + 1];
prev[0] = 0;
for(int i = 0;i<n;i++)
{
scanf("%ld",&v[i]);
prev[i + 1] = prev[i] + v[i];
}
int dp[n + 1];
dp[1] = v[0];
for(int i = 2;i<=n;i++) dp[i] = max(dp[i - 1],(long long)ceil((double)prev[i] / i));
int q;
cin >> q;
for(int i = 0;i<q;i++)
{
int t;
scanf("%ld",&t);
if(t < dp[n]) printf("-1\n");
else //找到第一个大于等于ceil(prev[n]/t)的prefixsum之和
{
t = (int)ceil((double)prev[n]/t);
printf("%ld\n",t);
}
}
system("pause");
}
E题-Serega the Pirate
- a=0的充要条件是除1外,每格点四周至少一个比其小的格点。称满足条件的格点为好格点,否则为坏格点
- 坏格点的常数上界: 交换1次最多改变10个格点的好坏性,因此坏格点多于10个一定至少需要2次交换
- 坏格点数小于等于10时,对每个坏格点及其四周格点,将其与mn个格点分别交换。可在 O ( 1 ) O(1) O(1)判断格局变换后的坏格点数。
代码老是T待查明原因:
#include<bits/stdc++.h>
using namespace std;
bool judge(int **a,int i,int j,int n,int m)
{
bool f1 = i - 1 >= 0 && a[i - 1][j] < a[i][j];
bool f2 = i + 1 < n && a[i + 1][j] < a[i][j];
bool f3 = j + 1 < m && a[i][j + 1] < a[i][j];
bool f4 = j - 1 >= 0 && a[i][j - 1] < a[i][j];
return !(f1 || f2 || f3 || f4 || a[i][j] == 1);
}
bool update(int **a,bool **is,int n,int m,int i1,int j1,int i2,int j2,int ori,vector<array<int,2>>& con)
{
//判断a数组的(i_1,j_1)和(i_2,j_2)交换后坏点数的变化情况
int t = a[i1][j1];
a[i1][j1] = a[i2][j2];
a[i2][j2] = t;
set<pair<int,int>> c;
c.insert(make_pair(i1,j1)); c.insert(make_pair(i2,j2));
for(auto& p : con)
{
if(i1 + p[0] >=0 && i1 + p[0]<n && j1 + p[1]>=0 && j1 + p[1]<m) c.insert(make_pair(i1+p[0],j1+p[1]));
if(i2 + p[0] >=0 && i2 + p[0]<n && j2 + p[1]>=0 && j2 + p[1]<m) c.insert(make_pair(i2+p[0],j2+p[1]));
}
for(auto&p : c)
{
int x = p.first; int y = p.second;
if(is[x][y]==true && judge(a,x,y,n,m)==false) ori--;
if(is[x][y]==false && judge(a,x,y,n,m) == true) ori++;
}
t = a[i1][j1];
a[i1][j1] = a[i2][j2];
a[i2][j2] = t;
return ori == 0;
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
int **a = new int *[n];
for(int i = 0;i<n;i++) a[i] = new int[m];
bool **is = new bool* [n];
for(int i = 0;i<n;i++) is[i] = new bool[m];
for(int i = 0;i<n;i++) for(int j = 0;j<m;j++) {is[i][j]=false;scanf("%d",&a[i][j]);}
int v = 0;
vector<array<int,2>> b;
vector<array<int,2>> con = {{1,0},{-1,0},{0,1},{0,-1}};
for(int i = 0;i<n;i++) for(int j = 0;j<m;j++)
{
bool f1 = false,f2 = false,f3 = false,f4 = false;
f1 = i - 1 >= 0 && a[i - 1][j] < a[i][j];
f2 = i + 1 < n && a[i + 1][j] < a[i][j];
f3 = j + 1 < m && a[i][j + 1] < a[i][j];
f4 = j - 1 >= 0 && a[i][j - 1] < a[i][j];
if(f1 || f2 || f3 || f4 || a[i][j] == 1) {is[i][j]=false;} else {b.push_back({i,j});is[i][j]=true;};
}
v = b.size();
if(v == 0) printf("0\n");
else if(v >= 11) printf("2\n");
else{ //v<=10,是否能两次交换成功
int cont = 0;
set<pair<int,int>> f;
for(auto& p : b)
{
int x = p[0]; int y = p[1];
for(int i = 0;i<n;i++) for(int j = 0;j<m;j++)
{
if(x == i && j == y) continue;
else if(update(a,is,n,m,i,j,x,y,v,con))
{
f.insert(make_pair(a[i][j],a[x][y]));
f.insert(make_pair(a[x][y],a[i][j]));
}
}
for(auto& q:con)
{
int x = p[0] + q[0]; int y = p[1] + q[1];
if(x >= 0 && x < n && y >= 0 && y < m)
{
for(int i = 0;i<n;i++) for(int j = 0;j<m;j++)
{
if(x == i && j == y) continue;
else if(update(a,is,n,m,i,j,x,y,v,con))
{
f.insert(make_pair(a[i][j],a[x][y]));
f.insert(make_pair(a[x][y],a[i][j]));
}
}
}
}
}
if(f.size() > 0) printf("1 %d\n",f.size()/2);
else printf("2\n");
}
system("pause");
return 0;
}
可能原因:update中set频繁使用。
改变方式:交换后检查原先所有坏点(最多10个)和交换后可能改变好坏的点(最多10个)。不会怎么引入常数。
#include<bits/stdc++.h>
using namespace std;
inline bool judge(int **a,int i,int j,int n,int m)
{
if(i < 0 || i >= n || j <0 || j >= m) return false;
if(a[i][j]==1) return false;
bool f1 = i - 1 >= 0 && a[i - 1][j] < a[i][j]; if(f1) return false;
bool f2 = i + 1 < n && a[i + 1][j] < a[i][j]; if(f2) return false;
bool f3 = j + 1 < m && a[i][j + 1] < a[i][j]; if(f3) return false;
bool f4 = j - 1 >= 0 && a[i][j - 1] < a[i][j]; if(f4) return false;
return true;
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
int **a = new int *[n];
for(int i = 0;i<n;i++) a[i] = new int[m];
for(int i = 0;i<n;i++) for(int j = 0;j<m;j++) {scanf("%d",&a[i][j]);}
int v = 0;
vector<array<int,2>> b;
vector<array<int,2>> con = {{1,0},{-1,0},{0,1},{0,-1},{0,0}};
for(int i = 0;i<n;i++) for(int j = 0;j<m;j++)
{
bool f1 = false,f2 = false,f3 = false,f4 = false;
f1 = i - 1 >= 0 && a[i - 1][j] < a[i][j];
f2 = i + 1 < n && a[i + 1][j] < a[i][j];
f3 = j + 1 < m && a[i][j + 1] < a[i][j];
f4 = j - 1 >= 0 && a[i][j - 1] < a[i][j];
if(f1 || f2 || f3 || f4 || a[i][j] == 1) {} else {b.push_back({i,j});}
}
v = b.size();
if(v == 0) printf("0\n");
else if(v >= 11) printf("2\n");
else{ //v<=10,是否能两次交换成功
int cont = 0;
set<pair<int,int>> f;
for(auto& p : b)
{
for(auto& q:con)
{
int x = p[0] + q[0]; int y = p[1] + q[1];
if(x >= 0 && x < n && y >= 0 && y < m)
{
for(int i = 0;i<n;i++)
for(int j = 0;j<m;j++)
{
bool flag = true;
swap(a[i][j],a[x][y]);
// if(min(a[i][j],a[x][y]) == 2 && max(a[i][j],a[x][y]) == 4)
// printf("h");
//检查原先所有坏点
for(auto & o:b) if(judge(a,o[0],o[1],n,m)) {flag = false;}
//检查可能改变好坏性的点
for(auto & o:con)
{
int ix = o[0] + i; int iy = o[1] + j;
if(judge(a,ix,iy,n,m)) {flag = false;}
ix = o[0] + x; iy = o[1] + y;
if(judge(a,ix,iy,n,m)) {flag = false;}
}
if(flag)
{
f.insert({min(a[i][j],a[x][y]),max(a[i][j],a[x][y])});
}
swap(a[i][j],a[x][y]);
}
}
}
}
//for(auto& p:f) cout << p.first << " " << p.second << endl;
if(f.size() > 0) printf("1 %d\n",f.size());
else printf("2\n");
}
system("pause");
return 0;
}