A. Ilya and a Colorful Walk
题意
给n个不同颜色的房子,求出两个不同颜色的房子的最远距离是多少。
关键词
贪心
思路
对于每一个房子,用来和第一个、最后一个房子比较颜色并求距离,用这个距离来更新最大距离就是答案。
代码
#include <bits/stdc++.h>
#define Debug 0
#define MAXN 300056
#define MAXM 200
#define MOD 1000000007
#define INF 0x3f3f3f3f
#define PI 3.1415926535
#define pb push_back
#define SYNC ios::sync_with_stdio(false);
//#define FILE_IN "I:\\acm-workspace\\CLion-Project\\in.txt"
typedef long long ll;
typedef std::pair<int, int> Pair;
using namespace std;
int arr[MAXN];
int pos[MAXN];
vector<Pair> vt;
int ans = 0;
int main ()
{
#ifdef FILE_IN
freopen(FILE_IN, "r", stdin);
#endif
SYNC
int n;
cin>>n;
memset(pos, -1,sizeof(pos));
for (int i = 0; i < n; ++i)
{
cin>>arr[i];
}
for (int i = 0; i < n; ++i)
{
if(arr[i]!=arr[0])
{
ans = max(ans, i);
}
if(arr[i] != arr[n-1])
{
ans = max(ans, n-1-i);
}
}
cout<<ans<<endl;
#ifdef FILE_IN
fclose(stdin);
#endif
return 0;
}
B. Alyona and a Narrow Fridge
题意
给一个宽为2,高为h冰箱,以及n个宽为1,高为ai的瓶子。
可以在冰箱中添加不占空间的夹板,问最多能放多少个瓶子。
关键词
贪心
思路
对瓶子高度降序排序,每次选取最近的两个,以两个中较高的一个的高度放置隔板,直到无法再放下任何瓶子或者瓶子放完。
代码
#include <bits/stdc++.h>
#define Debug 0
#define MAXN 300056
#define MAXM 200
#define MOD 1000000007
#define INF 0x3f3f3f3f
#define PI 3.1415926535
#define pb push_back
#define SYNC ios::sync_with_stdio(false);
//#define FILE_IN "I:\\acm-workspace\\CLion-Project\\in.txt"
typedef long long ll;
typedef std::pair<int, int> Pair;
using namespace std;
int ans = 0;
vector<int> pq;
int n,h;
int arr[MAXN];
int main ()
{
#ifdef FILE_IN
freopen(FILE_IN, "r", stdin);
#endif
// SYNC
cin>>n>>h;
int tag = 1;
for (int i = 0; i < n; ++i)
{
cin>>arr[i];
pq.pb(arr[i]);
sort(pq.begin(), pq.end(), greater<int>());
int hh = h;
for (int j = 0; j <= i; ++j)
{
int t1 = pq[j];
if(j<i)
{
t1 = max(t1, pq[j+1]);
++j;
}
if(hh<t1)
{
tag = 0;
break;
}else
hh -= t1;
}
if(!tag)
{
break;
} else
{
ans = i+1;
}
}
cout<<ans<<endl;
#ifdef FILE_IN
fclose(stdin);
#endif
return 0;
}
C. Ramesses and Corner Inversion
题意
给定两个n*m的矩阵A和B,问能否在矩阵A中,通过若干次变换一个子矩阵的四个角来得到B矩阵。
关键词
构造、贪心、奇偶
思路
计算每行每列中,有多少个不同的地方,一旦某行或某列出现奇数个不同的,就是No
,必须都是偶数才是Yes
。
显然,每次变换一个子矩阵的四个角,意味着四个角所在行或者列都会改变2个元素。
进行若干次变换,每行每列改变的元素个数必然也是偶数个。
所以,当某一行或者某一列出现奇数个不同的地方,就无法从矩阵A得到矩阵B。
代码
#include <bits/stdc++.h>
#define Debug 0
#define MAXN 556
#define MAXM 200
#define MOD 1000000007
#define INF 0x3f3f3f3f
#define PI 3.1415926535
#define pb push_back
#define SYNC ios::sync_with_stdio(false);
//#define FILE_IN "I:\\acm-workspace\\CLion-Project\\in.txt"
typedef long long ll;
typedef std::pair<int, int> Pair;
using namespace std;
bool ans = 1;
int n,m;
int a[MAXN][MAXN],b[MAXN][MAXN],df[MAXN][MAXN];
int r[MAXN] = {0}, c[MAXN] = {0};
int main ()
{
#ifdef FILE_IN
freopen(FILE_IN, "r", stdin);
#endif
// SYNC
cin>>n>>m;
for (int i = 0; i < n; ++i)
{
for (int j = 0; j < m; ++j)
{
cin>>a[i][j];
}
}
for (int i = 0; i < n; ++i)
{
for (int j = 0; j < m; ++j)
{
cin>>b[i][j];
df[i][j] = b[i][j] == a[i][j];
if(!df[i][j])
{
r[i]++;
c[j]++;
}
}
}
for (int i = 0; i < n; ++i)
{
if(r[i] %2== 1)
{
ans = 0;
break;
}
}
for (int i = 0; i < m; ++i)
{
if(c[i] %2== 1)
{
ans= 0;
break;
}
}
if(ans)
cout<<"Yes"<<endl;
else
cout<<"No"<<endl;
#ifdef FILE_IN
fclose(stdin);
#endif
return 0;
}
D. Frets On Fire
题意
给定n个序列的起始值。
设第i个序列的起始值为si,该序列之后的每一个数等于前一个数加1(即序列i可描述为:[si,si+1,si+2,……,∞])。
再给出q个询问,每个询问中包含两个整数l和r。
需要求出,在每个序列中,选取[l,r]这个区间中的值,求出现过的数字种类和,即:重复的数字只算一次。
关键词
排序、二分。
思路
对si排序,求差值,再排序,求和。二分查找输入区间长度所在的位置。
在这道题目中,每一个区间内具体选中了哪些数字其实并不重要。
首先对si排序,求出相邻两个si的差值。
对于每一段区间,去分析它在[l,r]这个查询中贡献的数字数量。记[l,r]的区间长度为w,w=r-l+1。对如下三种情况进行分析:
- 如果si+1-si>w,si所代表的区间最多只能贡献w个数。
- 否则如果w>si+1-si,si所代表的区间最多只能贡献si+1-si个数。超过的部分算作si+1所代表的区间所贡献的部分。
- 且,最后一个序列所贡献的数字数量为w,因为它后面没有了其他区间,可以假设其与后面一项差值为∞。
所以,对于区间长度w,可以对之前求的差值进行排序,使用二分快速求出有多少个区间满足第一种情况,多少个满足第二种情况。对第二种情况累加求和,加上第一种情况的数量*w即为答案
用第一个案例的第二个查询举例子:
按照这个思路去编程即可。
代码
#include <bits/stdc++.h>
#define Debug 0
#define MAXN 100005
#define MAXM 200
#define MAXK 10000010
#define MOD 6000
#define INF 0x3f3f3f3f
#define PI 3.1415926535
#define pb push_back
#define SYNC ios::sync_with_stdio(false);
//#define FILE_IN "I:\\acm-workspace\\CLion-Project\\in.txt"
typedef long long ll;
typedef std::pair<int, int> Pair;
using namespace std;
ll arr[MAXN];
ll num[MAXN]; // 区间差值
ll sum[MAXN] = {0}; // 差值求和
int main ()
{
#ifdef FILE_IN
freopen(FILE_IN, "r", stdin);
#endif
// SYNC
int n;
cin >> n;
for (int i = 0; i < n; ++i)
{
cin >> arr[i];
}
sort(arr, arr + n); // 对输入排序
for (int i = 0; i < n - 1; ++i)
{
num[i] = arr[i + 1] - arr[i];
}
sort(num, num + n - 1); // 对差值排序
for (int i = 0; i < n - 1; ++i)
{
// 对排序后的差值求和,方便后续直接取值
if (i)
sum[i] = sum[i - 1] + num[i];
else
sum[i] = num[i];
}
int q;
cin >> q;
for (int i = 0; i < q; ++i)
{
ll l, r;
cin >> l >> r;
ll t = r - l + 1;
// 找到分界的位置,在这个位置之前都是小于r-l+1的
int ind = upper_bound(num, num + n - 1, t) - num;
// 由于使用的upper_bound,所以分别处理分界位置ind为0
// 和ind为n-1(求差值后只剩n-1个数且从0开始,所以n-1为所有区间长度都小于r-l+1)的情况
ll ans = 0;
if (ind < n - 1)
{
if(ind)
{
// 通常情况
ind--;
ans = sum[ind] + (n - 1 - ind) * t;
} else
{
ans = n*t;
}
}
else
ans = t + sum[n-2];
cout << ans << endl;
}
#ifdef FILE_IN
fclose(stdin);
#endif
return 0;
}
E. Pavel and Triangles
题意
给定n种长度的棍子,接下来的n个整数,第i个整数表示长度为2i的棍子有多少个。
求出使用这些木棍最多能构成多少个三角形。
每个木棍都只能使用一次,且不可折断。
关键词
贪心
思路
对于当前长度的棍子,优先考虑使用两根当前长度的加一根较短的棍子去构成等腰三角形。无法构成等腰三角形时,再考虑使用三根当前长度的棍子去构成等边三角形。剩下的留给后面构造等腰三角形用。
代码
#include <bits/stdc++.h>
#define Debug 0
#define MAXN 300005
#define MAXM 200
#define MAXK 10000010
#define MOD 6000
#define INF 0x3f3f3f3f
#define PI 3.1415926535
#define pb push_back
#define SYNC ios::sync_with_stdio(false);
//#define FILE_IN "I:\\acm-workspace\\CLion-Project\\in.txt"
typedef long long ll;
typedef std::pair<int, int> Pair;
using namespace std;
int main ()
{
#ifdef FILE_IN
freopen(FILE_IN, "r", stdin);
#endif
// SYNC
int n;
cin >> n;
ll r = 0; // The remainder
ll ans = 0;
for (int i = 0; i < n; ++i)
{
ll t;
cin >> t;
ll t2 = min(t / 2, r);
t -= t2 * 2;
r -= t2;
ans += t2;
ans += t/3;
r += t%3;
}
cout << ans << endl;
#ifdef FILE_IN
fclose(stdin);
#endif
return 0;
}
G. Get Ready for the Battle
题意
给定n个士兵,可以划分到m个组中,其中有些组可以为空。
再给定m组个敌人,每组敌人生命值为输入的该组人数。
每一轮可以选择自己的士兵去攻击敌人,每个组在每一轮中只可以攻击一次,但可以多个组攻击同一组敌人。每一组的攻击可以造成相当于组中人数的伤害,敌人生命值为零时,这个组就算被击败了(下一轮就不能再攻击这个组了)。
并且自己的士兵分组都不会受到伤害(也就是只有我们打敌人)。
问最少需要多少轮才能将敌人全部击败。
需要输出自己士兵的分组情况和每一轮攻击的安排。
关键词
构造、排序
思路
对敌人的生命值累加求和,并且将前 i 项之和对n求余并保存在一个数组中。将对这个数组最后一项修改为n,然后排序后求差值。这个数组即为己方士兵的分组情况。按照这个分组,依照敌人输入的次序一轮一轮去杀敌即可。
-
最优解法:
-
假设和已知前提:用 hpi 表示第i个敌人的生命值,ki 表示第i个敌人在生命值低于n时的生命值(可能是攻击后的结果,也可能是初始值),
sum
表示敌人生命之和。我方士兵一轮攻击最高伤害为n
。 -
攻击策略:从第一个敌人开始(i=1),在hpi 高于
n
的时候,显然,这一轮攻击无法击败这个敌人,需要所有伤害都打到它身上;在生命值小于n
的时候,即生命值为ki的时候,最好的情况就是我方士兵存在一个伤害恰好为ki的组合,其他的士兵开始针对下一个敌人展开攻击,这样就不会在这个一轮攻击中可以击败的敌人身上浪费伤害,尽可能提高了攻击的效率。当然,在面对最后一个敌人的时候,就不需要考虑这么多了。 -
结论:在最优的攻击策略下,每一轮都能打满
n
的伤害,所以需要攻击的轮数就是对sum/n
向上取整。满足最优的攻击策略只需要对于前m-1组敌人中的每一个ki,在我方士兵的分组中都能找到一个满足的组合即可。 -
求解:
-
假设和已知前提:用 hpi 表示第i个敌人的生命值,ki 表示第i个敌人在生命值低于n时的生命值(可能是攻击后的结果,也可能是初始值),
-
补充解释:
-
累加求余
:求出上面定义的ki。因为是对累加求余,所以处理了在某一轮攻击中,按照上述的攻击策略将前面的敌人击败后,还攻击了第i个敌人使其生命值低于n的这种情况对ki影响。
可以更形象地理解为,配合后面一步的排序将超过一轮攻击的情况合并到一轮攻击中来。 -
排序
:让我们通过ki求出我们需要的分组方案。按照上面的策略,我们只需要知道一共有多少种ki需要我们去考虑,具体出现的位置并不会影响最终的答案,因为我们的策略就是只考虑如何能让我们的士兵分组组合出ki来。
对接下来下一步相减求差值
的理解:对于排序后的ki,i为1时,是最少一种敌人情况,直接按照这个分组即可;i>1时(可以拿i=2代入),说明前面的ki-1已经考虑过,只需要考虑从ki-1的组合增加一个小的分组使其得到ki即可,这个小的分组的人数就是ki-ki-1了。 -
末项为n
:显然对已排序序列的求差值后,其中各项之和一定等于最后一项。一轮攻击下来最多能造成n的伤害。这里将最后一项修改为n,就是为了保证在后面的步骤中,一轮下来最多有n点伤害。 -
不足补0
:一共有m(7)个分组,且题意说明了可以存在空的组,没有分配士兵的组就是0。
-
代码
#include <bits/stdc++.h>
#define Debug 0
#define MAXN 1000005
#define MAXM 200
#define MAXK 10000010
#define MOD 6000
#define INF 0x3f3f3f3f
#define PI 3.1415926535
#define pb push_back
#define SYNC ios::sync_with_stdio(false);
//#define FILE_IN "I:\\acm-workspace\\CLion-Project\\in.txt"
typedef long long ll;
typedef std::pair<int, int> Pair;
using namespace std;
int hp[MAXN] = {0};
int b[MAXN] = {0};
ll sum = 0;
int main ()
{
#ifdef FILE_IN
freopen(FILE_IN, "r", stdin);
#endif
// SYNC
int n, m;
scanf("%d %d", &n, &m);
for (int i = 1; i <= m; ++i)
{
scanf("%d", &hp[i]);
sum += hp[i];
b[i] = sum % n;
}
b[m] = n;
sort(b + 1, b + 1 + m);
for (int i = m; i >= 0; --i)
{
b[i] = b[i] - b[i - 1];
}
printf("%lld\n", (sum - 1) / n + 1);
for (int i = 1; i <= m; ++i)
{
printf("%d%c", b[i], (i==m?'\n':' '));
}
int cur = 1; // Evlampy's army group
for (int i = 1; i <= m; ++i)
{
// The enemy currently under attack
int t = hp[i];
while (t > 0)
{
t -= b[cur];
++cur;
printf("%d%c", i, (cur>m?'\n':' '));
if (cur > m)
{
cur = 1;
}
}
}
while (cur > 1 && cur <= m)
{
cout << 1 << (cur == m ? "\n" : " ");
cur++;
}
#ifdef FILE_IN
fclose(stdin);
#endif
return 0;
}
留坑
F. Niyaz and Small Degrees
题意
关键词
思路
代码
H. Triple
题意
关键词
思路
代码