Educational Codeforces Round 1
总结
涉及到计算几何—极角排序及简单线性DP
极角排序的确知识盲区。
VP的时候没出E题,很可惜, 不要被题目吓倒就好了。
尽力写就好了,反正赛后还要补题。
A. Tricky Sum
题意:
求1–n所有数字之和,仅有2的幂次方的数字为负数。
int main()
{
int T;
scanf("%d", &T);
while(T --)
{
ll n;
scanf("%lld", &n);
ll sum = n * (1ll + n) / 2;
ll temp = 1;
while(1)
{
if(temp <= n)
sum -= temp * 2ll;
else break;
temp *= 2ll;
}
printf("%lld\n", sum);
}
return 0;
}
B. Queries on a String
题意:
给定m个查询,每次查询给定l, r, k,将[l ,r]区间中的字符循环移动k次,最后一个移到首位,其他不变。求m次操纵后的字符串
思路:
观察范围,暴力即可
char last[maxn], ans[maxn];
int main()
{
scanf("%s", last + 1);
int len = strlen(last + 1);
for(int i = 1 ; i <= len ; i ++)
ans[i] = last[i];
int m;
scanf("%d", &m);
for(int i = 1 ; i <= m ; i ++)
{
int l, r, k;
scanf("%d %d %d", &l, &r, &k);
int temp = k % (r - l + 1);
if(temp == 0) continue;
int cur = l;
for(int j = r - temp + 1; j <= r ; j ++)
{
ans[cur ++] = last[j];
}
for(int j = l ; j <= r - temp ; j ++)
{
ans[cur ++] = last[j];
}
for(int j = 1 ; j <= len ; j ++)
last[j] = ans[j];
}
printf("%s\n", ans + 1);
return 0;
}
C. Nearest vectors
题意:
给定n个坐标,求出原点和n个坐标形成了若干向量之间,夹角最小的两条向量的编号
思路:
经典的极角排序。(知识盲区)
atan2(y, x):返回(x,y)和x轴正半轴的夹角,返回值[-PI / 2, PI / 2]。
注意此函数存在误差,取long double
考虑两个向量之前的夹角为[0,PI],要么是两角之间的差值,要么是其补角,注意最小角和最大角还要再次比较
struct note{
int id;
double x;
double y;
long double d;
bool operator < (const note &a) const
{
return d < a.d;
}
}point[maxn];
int main()
{
int n;
scanf("%d", &n);
for(int i = 1 ; i <= n ; i ++)
{
double x, y;
scanf("%lf %lf", &x, &y);
double d = atan2(y, x);
point[i] = {i,x, y, d};
}
sort(point + 1, point + n + 1);
long double minn = min(point[n].d - point[1].d, 2 * PI - (point[n].d - point[1].d));
int res1 = point[1].id, res2 = point[n].id;
for(int i = 1 ; i <= n ; i ++)
{
if(i + 1 <= n)
{
long double temp = point[i + 1].d - point[i].d;
temp = min(temp, 2 * PI - temp);
if(minn > temp)
{
minn = temp;
res1 = point[i].id, res2 = point[i + 1].id;
}
}
}
printf("%d %d\n", res1, res2);
return 0;
}
D. Igor In the Museum
题意:
给定n * m的矩阵, “.“表示空地,”“表示障碍,给定k个查询,每次输出当前左边周围有多少个””
思路:
观察数据范围,直接暴力铁TLE
可以发现同属于一个连通块内的点,其所对应的答案是一定的,可以直接bfs预处理所有的".",那么同属一个连通块中的点的ans都是一样的。时间复杂度O(n * m)
void bfs(int sta_x, int sta_y)
{
int cnt = 0;
queue <PII> alls;
alls.push({sta_x, sta_y}), book[sta_x][sta_y] = true;
vector <PII> cc;
cc.push_back({sta_x, sta_y});
while(!alls.empty())
{
auto t = alls.front();
alls.pop();
for(int i = 0 ; i < 4 ; i ++)
{
int temp_x = t.first + dx[i];
int temp_y = t.second + dy[i];
if(temp_x >= 1 && temp_x <= n && temp_y >= 1 && temp_y <= m)
{
if(mm[temp_x][temp_y] == '*')
{
cnt ++;
}
else
{
if(!book[temp_x][temp_y])
{
alls.push({temp_x, temp_y});
cc.push_back({temp_x, temp_y});
book[temp_x][temp_y] = true;
}
}
}
}
}
for(auto t : cc)
ans[t.first][t.second] = cnt;
}
int main()
{
scanf("%d %d %d", &n, &m ,&k);
for(int i = 1 ; i <= n ; i ++)
{
scanf("%s", mm[i] + 1);
}
for(int i = 1 ; i <= n ; i ++)
{
for(int j = 1 ; j <= m ; j ++)
{
if(mm[i][j] == '.' && !book[i][j])
{
bfs(i, j);
}
}
}
for(int i = 1 ; i <= k ; i ++)
{
int x, y;
scanf("%d %d", &x, &y);
printf("%d\n", ans[x][y]);
}
return 0;
}
E. Chocolate Bar
题意:
将一个n * m的矩阵切成一组总大小为k的方块,可横着切也可竖着切,每次切矩阵的花费为所切长度的平方,求最小的花费.
思路:
分析题意,可以发现,存在多组查询(4e4),且每组查询涉及到n、m及k,可以提前预处理出答案,然后O(1)查询。
尝试是否可以DP求解。
发现存在递推关系,那么可以DP
f[i][j][k]:将i * j的矩阵切成k块的所有合法集合
属性:min
状态转移:
考虑横切:假设t为形成的其中一个矩形的长度,那么i - t为另一个矩形的长度
f
[
i
]
[
j
]
[
k
]
=
m
i
n
(
f
[
i
]
[
j
]
[
k
]
,
f
[
t
]
[
j
]
[
t
t
]
+
f
[
i
−
t
]
[
j
]
[
k
−
t
t
]
+
j
∗
j
)
f[i][j][k] = min(f[i][j][k], f[t][j][tt] + f[i - t][j][k - tt] + j * j)
f[i][j][k]=min(f[i][j][k],f[t][j][tt]+f[i−t][j][k−tt]+j∗j)
竖切同理。
初始化:
所求为min,根据DP定义,起点状态为f[i][j][i * j] = f[i][j][0] = 0, 其他为INF。
int f[40][40][60];
int main()
{
for(int i = 1 ; i <= 35 ; i ++)
{
for(int j = 1 ; j <= 35 ; j ++)
{
for(int k = 1 ; k <= 50 ; k ++)
{
f[i][j][k] = INF;
}
f[i][j][i * j] = 0;
}
}
for(int i = 1 ; i <= 35 ; i ++) //
{
for(int j = 1 ; j <= 35 ; j ++)
{
for(int k = 1 ; k <= 50 ; k ++)
{
for(int t = 1 ; t <= i - 1 ; t ++) //横向切
{
for(int tt = 1 ; tt <= k; tt ++)
{
if(t * j >= tt && i - t > 0 && (i - t) * j >= k - tt)
f[i][j][k] = min(f[i][j][k],f[t][j][tt] + f[i - t][j][k - tt] + j * j);
}
}
for(int t = 1 ; t <= j - 1 ; t ++) //竖着 t <= 1
{
for(int tt = 1 ; tt <= k; tt ++)
{ // f[1][2][1] = f[1][1][1] + f[1][1][0] + i * i
if(i * t >= tt && j - t > 0 && i * (j - t) >= k - tt)
f[i][j][k] = min(f[i][j][k],f[i][t][tt] + f[i][j - t][k - tt] + i * i);
}
}
}
}
}
int T;
scanf("%d", &T);
while(T --)
{
int n, m, k;
scanf("%d %d %d", &n, &m, &k);
printf("%d\n", f[n][m][k]);
}