A Game
题意:给定一序列,1为陆地,0为水,在陆地可以免费移动,不能走到水中,序列左右两端都是陆地。有水的地方只能通过花费金币飞行通过,一格一个金币(不管是否是陆地还是水)且只能飞行一次,问最少花费为多少可以从左端到达右端。
题解:一开始没注意只能飞行一次,101101这种只能从开头1一次飞到右端1,总花费为5,其实只需要遍历一遍,记录最开始水的坐标x和最后的坐标y,答案就是y-x+2(如果有水的话)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int main()
{
int t; cin >> t;
while(t -- )
{
int n; cin >> n;
int a[110];
memset(a, 0x3f, sizeof a);
int ans = 0, idx = 1;
for(int i = 1; i <= n; i ++ ) cin >> a[i];
int l = 0;
int b = 0, e = 0;
for(int i = 1; i <= n; i ++ )
{
if(a[i] == 0) {
if(a[i - 1] == 1)
{
if(l == 0) b = i;
l ++;
}
if(a[i + 1] == 1)
{
e = i;
}
}
}
if(b == 0) cout << 0 << endl;
else cout << e - b + 2 << endl;
}
return 0;
}
B Game of Ball Passing
题意:给一个序列,第i个序列表示第i个球员踢球的次数,接球不算,问一共需要踢多少个球
题解:极端情况一个球将最大值队员之外的其余队员都给踢满,看看最大值队员还剩多少次,一次踢一个球给任意一个队员。以1 5 2为例,极端情况为2-》1-》2-》3-》2-》3-》2-》3,这种情况下2号球员还差1次,而其余球员踢的次数已经满足了,所以只能另换一只球,由2发球踢给其他人。
球的最小个数,找序列最大值,与其他剩余次数求差值再减个1(因为最大值的那个球员最后可以踢一次给任意一个球员)
#include <iostream>
using namespace std;
int a[100010];
int main()
{
int t; scanf("%d", &t);
while(t -- )
{
int n; scanf("%d", &n);
int mx = 0;
long long sum = 0;
bool st = true;
for(int i = 1; i <= n; i ++ ) {
scanf("%d", &a[i]);
if(a[i]) st = false;
mx = max(a[i], mx);
sum += a[i];
}
sum -= mx;
if(!st)//次数不全为0的情况下,至少也得踢一次球
printf("%d\n", 1 + max((long long)0, mx - sum - 1));
else printf("0\n");
}
return 0;
}
C Weird Sum
本来用vector<pair<int,int> >直接记录每个颜色所在的节点,直接进行三重循环判断,直接一手TLE。时间复杂度为O(c*n^2),c最差为1e5,二维矩阵内元素最多为1e10,每个元素可以有1e5个,即n为1e5。。。
由于记录二维坐标不能进行排序,dis=|xi - xj| + |yi - yj|发现x和y坐标可以分开进行求解,将颜色相同的分别存储,从小到大排序,若这个颜色的坐标只有一个,则不用计算,否则进行计算,以x坐标为例,按照暴力的想法,xi会被加i - 1次,也会被减v.size() - i次,注意xi的i从0开始,需要微调一下
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
vector<int> x[N], y[N];
int main()
{
int n, m; cin >> n >> m;
int mx = 0;
for(int i = 1; i <= n; i ++ )
for(int j = 1; j <= m; j ++ ){
int nu;
scanf("%d", &nu); mx = max(nu, mx);
x[nu].push_back(i);
y[nu].push_back(j);
}
long long ans = 0;
for(int i = 1; i <= mx; i ++ ){
if(x[i].size() == 1 || y[i].size() == 1) continue;
sort(x[i].begin(), x[i].end());
sort(y[i].begin(), y[i].end());
for(int j = 0; j < x[i].size(); j ++ ){
int t = x[i][j];
ans += (long long)j * t;
ans -= (long long)(x[i].size() - j - 1) * t;
}
for(int j = 0; j < y[i].size(); j ++ )
{
int t = y[i][j];
ans += (long long) j * t;
ans -= (long long)(y[i].size() - j - 1) * t;
}
}
cout << ans << endl;
return 0;
}
D Integral Array
题意:给定一序列a,明确ai小于c,若序列a中任意两值x,y(x > y),⌊x/y⌋也在序列a中,则输出yes,否则输出no
题解:由于给定x,y,就可以判断哪些数可从⌊x/y⌋和y中推出,即确定一个范围,使得在这个范围内的所有数除y都等于⌊x/y⌋,例如,给定x=38,y=7,则范围35~41内的所有数除y都为⌊x/y⌋。
即:
⌊
x
/
y
⌋
∗
y
<
=
x
⌊x/y⌋ * y <= x
⌊x/y⌋∗y<=x
⌊
x
/
y
⌋
∗
y
+
y
>
x
⌊x/y⌋ * y + y > x
⌊x/y⌋∗y+y>x
设
r
=
⌊
x
/
y
⌋
r = ⌊x/y⌋
r=⌊x/y⌋若r不在序列a中,y在序列a中,则x一定也不在序列a中,可通过前缀和判断该区间是否有值。
注意这种为调和级数复杂度问题,时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)
for (int i = 1; i <= lim; i ++)//lim为上届
{
for (int j = i; j * i <= lim; j ++)
}
除此之外,这个题目还卡住我的地方在于memset,我经常写memset(a, 0, sizeof a);将内容全部更新,时间复杂度为O(n),当t变得特别大时,memset就会超时(很离谱)所以这里写成memset(a,0,sizeof(int)*(c+5));只对需要的内容进行刷新
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int a[N];
int sum[N];
int st[N];
int main()
{
int t; scanf("%d", &t);
while(t -- )
{
int n, c; scanf("%d%d", &n, &c);
memset(st, 0, sizeof(bool) * (c+ 5));//注意memset的时间复杂度,如果全部更新则会超时
//!!!!!!!!!
memset(sum, 0, sizeof(int) * (c + 5));
memset(a, 0, sizeof a);
for(int i = 1; i <= n; i ++ )
{
cin >> a[i];
st[a[i]] = 1;
}
for(int i = 1; i <= c; i ++ )
sum[i] = sum[i - 1] + st[i];
bool s = true;
for(int i = 1; i <= c; i ++ )
{
if(!st[i]) continue;//y = i,选择y在序列中
for(int j = 1; j * i <= c; j ++ )
{
if(st[j]) continue;//r = j,选择r在不在序列中
int x = sum[min(c, (j + 1) * i - 1)] - sum[i * j - 1];
//需要判断(j + 1) * i - 1是否超过c,若超过则取c值
if(x > 0) {s = false; break;}
}
if(!s) break;
}
if(!s) puts("No");
else puts("Yes");
}
return 0;
}