总结
本场涉及到两圆相交面积,启发式合并,二分图最小边颜色。
C题贪心挺有意思。启发式合并,优美的暴力yyds,二分图最小边颜色,匈牙利最大匹配的改版。
A. Extract Numbers
题意:
题意比较难懂的小模拟,给定一个字符串,单词用’‘,’’ ‘’;"隔开,如果全为不含前导0的数字放入一一行,否则放入第二行
char str[maxn];vector <string> ans1, ans2;
void check(int l, int r)
{
bool flog = true;
string temp;
for(int i = l + 1 ; i <= r - 1 ; i ++)
{
temp += str[i];
if(str[i] >= 48 && str[i] <= 57) continue;
else
{
flog = false;
}
}
if(flog) //全是数字
{
if(str[l + 1] != '0')
ans1.push_back(temp);
else // 0
{
if(r - l - 1 > 1)
ans2.push_back(temp); // r - l < 2 r - l
else ans1.push_back(temp);
}
}
else
{
ans2.push_back(temp);
}
}
int main()//数字 / 字母
{
scanf("%s", str + 1);
int len = strlen(str + 1);
vector <int> alls;
alls.push_back(0);
for(int i = 1 ; i <= len ; i ++)
{
if(str[i] == ',' || str[i] == ';')
alls.push_back(i);
}
alls.push_back(len + 1);
for(int i = 0 ; i < alls.size() ; i ++)
{
if(i + 1 < alls.size())
{
if(alls[i + 1] - alls[i] == 1)
ans2.push_back("*"); //
else
{
check(alls[i], alls[i + 1]);
}
}
}
if(!ans1.empty())
{
cout << '"';
for(int i = 0 ; i < ans1.size() ; i ++)
{
cout << ans1[i];
if(i != int(ans1.size()) - 1)
cout << ",";
}
cout << '"';
cout << "\n";
}
else cout << "-" << "\n";
if(!ans2.empty())
{
cout << '"';
for(int i = 0 ; i < ans2.size() ; i ++)
{
if(ans2[i] != "*")
cout << ans2[i];
if(i != int(ans2.size()) - 1)
cout << ",";
}
cout << '"';
cout << "\n";
}
else cout << "-" << "\n";
return 0;
}
B. Queries about less or equal elements
题意:
给定一个数组A,一个数组B,对于B中每一个数B[i],输出A数组中<=B[i]的个数。
思路:
经典二分
int main()
{
int n ,m;
scanf("%d %d", &n, &m);
for(int i = 1 ; i <= n ; i ++)
scanf("%lld", &a[i]);
for(int i = 1 ; i <= m ; i ++)
scanf("%lld", &b[i]);
sort(a + 1, a + n +1);
for(int i = 1 ; i <= m ; i ++)
{
int l = 1, r = n;
while(l < r)
{
int mid = (l + r + 1) >> 1;
if(a[mid] <= b[i]) l = mid;
else r = mid - 1;
}
if(a[l] <= b[i])
{
printf("%d ", l);
}
else printf("0 ");
}
return 0;
}
C. Make Palindrome
题意:
给定一个字符串,每次操纵可把一个字符变成另一个字符,之后可随意重排字符的顺序,求使得此字符串代价最小且字典序最小的方案。
思路:
观察数据范围2e5,那么解法应为O(n) O(nlogn),且可随便重排字符的顺序。
假设给定的字符串长度为偶数,说明该串中存在偶数个出现奇数次的字符,要是其变成回文串,取其中任意两个,花费一个代价,即可把两个出现奇数次的字符变成偶数次,代价是固定的,为使得字典序最小,那么贪心使得字典序大的字符转化到字典序小的字符上即可。
同理,假设为奇数,说明该串中存在奇数个出现奇数次的字符,且该串的中间位置要至少放一个出现次数为奇数的字符,假设把其单独拿出来,那么此时字符次数的情况和偶数串一样。代价同样固定,要使得字典序最小,应贪心的取出现次数为奇数次的中间那个字符,因为此时只有中间那个字符还是奇数次,那么取它作为中间,就可将其转化成偶数次。
int len, book[30];
int main()
{
scanf("%s", str + 1);
len = strlen(str + 1);
for(int i = 1 ; i <= len ; i ++)
{
book[str[i] - 'a'] ++;
}
char ch;
if(len % 2)
{
vector <int> alls;
for(int i = 0 ; i <= 25 ; i ++)
{
if(book[i] % 2)
{
alls.push_back(i);
}
}
ch = char(alls[int(alls.size()) / 2] + 'a');
book[ch - 'a'] --;
} //偶数个奇数
string ans;
for(int i = 1 ; i <= len ; i ++)
{
for(int j = 0 ; j <= 25 ; j ++)
{
if(book[j] > 0 && book[j] % 2 == 0)
{
ans += char(j + 'a');
book[j] -= 2;
break;
}
else if(book[j] > 0 && book[j] % 2)
{
ans += char(j + 'a');
book[j] -= 1;
for(int k = 25 ; k >= 0 ; k --)
{
if(book[k] > 0 && book[k] % 2)
{
book[k] --;
break;
}
}
break;
}
}
}
string temp = ans;
reverse(temp.begin(), temp.end());
if(len % 2)
{
ans += ch;
ans += temp;
}
else
{
ans += temp;
}
cout << ans << "\n";
return 0;
}
D. Area of Two Circles’ Intersection
题意:
给定两个圆,求两圆的面积并
#include<cstdio>
#include<cstring>
#include<iostream>
#include<vector>
#include<algorithm>
#include<cmath>
#include<iomanip>
using namespace std;
const long double PI=acos(-1.0);
struct Round
{
long double x,y;
long double r;
long double K(long double x)
{
return x*x;
}
long double Dis(Round a,Round b)
{
return sqrt(K(a.x-b.x)+K(a.y-b.y));
}
long double Intersection_area(Round a,Round b)
{
long double dis=Dis(a,b);
if(a.r==0||b.r==0||dis>=a.r+b.r)return 0;
else if(dis<=fabs(a.r-b.r))return PI*K(min(a.r,b.r));
else
{
long double angA=2*acos( (K(a.r)+K(dis)-K(b.r))/(2*a.r*dis) );
long double angB=2*acos( (K(b.r)+K(dis)-K(a.r))/(2*b.r*dis) );
long double areaA=K(a.r)*(angA-sin(angA))/2;
long double areaB=K(b.r)*(angB-sin(angB))/2;
return areaA+areaB;
}
}
}a,b;
int main()
{
cin>>a.x>>a.y>>a.r>>b.x>>b.y>>b.r;
cout<<setprecision(25)<<a.Intersection_area(a,b)<<endl;
return 0;
}
E. Lomsat gelral
题意:
给定一棵树,根节点为1号点,每个节点都有一个颜色,求以1–n号节点的子树中出现次数最多的颜色之和。
思路:
最开始考虑树形DP。
发现无法满足O(1)递推。需要一直维护两个集合,然后暴力合并。
正解:树上启发式合并(dsu on tree)
树上启发式合并,应用于子树查询问题,巧妙利用轻重链的性质。每次都将同层节点的轻儿子向重儿子上合并。实际上就相当于暴力合并,然后巧妙利用一些性质。时间复杂度O(nlogn)。每次都先搜索当前点的轻儿子,最后搜索重儿子,在暴力统计轻儿子所在子树的贡献时,要及时将全局cnt[]数组清空。
code:
ll ans[maxn];
int h[maxn], ne[maxn * 2], e[maxn * 2], idx, siz[maxn], son[maxn], col[maxn], cnt[maxn], big;
ll maxx, sum;
void add(int u, int v)
{
e[idx] = v;
ne[idx] = h[u];
h[u] = idx ++;
}
void dfs0(int sta, int fa)
{
siz[sta] = 1;
for(int i = h[sta] ; i != -1 ; i = ne[i])
{
int soon = e[i];
if(soon == fa) continue;
dfs0(soon, sta);
if(siz[son[sta]] < siz[soon]) //找重儿子
{
son[sta] = soon;
}
siz[sta] += siz[soon];
}
}
void count(int sta, int fa, int w)
{
cnt[col[sta]] += w;
if(cnt[col[sta]] > maxx)
{
maxx = cnt[col[sta]];
sum = col[sta];
}
else if(cnt[col[sta]] == maxx)
{
sum += col[sta];
}
for(int i = h[sta] ; i != -1 ; i = ne[i])
{
int soon = e[i];
if(soon == fa || soon == big) continue;
count(soon, sta, w);
}
}
void dfs1(int sta, int fa, bool keep)
{
for(int i = h[sta] ; i != -1 ; i = ne[i])
{
int soon = e[i]; //儿子节点
if(soon == fa || soon == son[sta]) continue;
dfs1(soon, sta, false); //先取搜索轻儿子
}
if(son[sta]) //若有重儿子
{
dfs1(son[sta], sta, true);//搜索重儿子
big = son[sta];
}
count(sta, fa, 1); //暴力计算
ans[sta] = sum;
big = 0;
if(!keep)
{
count(sta, fa, -1);
maxx = 0;
sum = 0;
}
}
int main()
{
int n;
scanf("%d", &n);
for(int i = 1 ; i <= n ; i ++)
scanf("%d", &col[i]);
memset(h, -1, sizeof(h)), idx = 0;
for(int i = 1 ; i < n ; i ++)
{
int u, v;
scanf("%d %d", &u, &v);
add(u, v), add(v, u);
}
dfs0(1, -1), dfs1(1, -1, false);
for(int i = 1 ; i <= n ; i ++)
printf("%lld ", ans[i]);
return 0;
}
F. Edge coloring of bipartite graph
题意:
给定一个二分图,左半部分为a个点,右半部分为b个点。给定m条边,使得有公共点的边颜色不同。求满足条件的颜色的最小颜色数量和m条边分别染成颜色的方案。
思路:
考虑二分图性质。不含奇数环。
那么满足条件的最小颜色的数量为点的最大度数。
考虑如何构造方案。数据范围a <= 1000, b <= 1000, m <= 1e5。
考虑二分图匈牙利最大匹配思想。
对于a图中每个点找到b图所连边的点。
如果b图中与之对应的点不存在匹配对象 / b图已经存在匹配对象且此时与之对应的a图的匹配对象还有别的点可以匹配(说明此时这样匹配,还是合法方案,且匹配数会增加), 那么更改此b图的匹配对象。(本质上就是暴力,如果更改还是合法的,那么此时匹配数量就 + 1)
考虑此题。
u – v,先可以找出此时u所对应的最小颜色col_u, v所对应的最小颜色col_v,先强行把v点颜色改成col_u,如果发生冲突,为了使得当前合法,可以把之前v点涂成col_u的点,强行改成col_v,继续递归。
那么总的时间复杂度为(m * 2 * n)
code
int f[3][1010][1010], pos[1010][1010], ans[maxn];
void dfs(int a, int b, int u, int v, int col_u, int col_v)
{
if(col_u == col_v)
{
f[a][u][col_u] = v, f[b][v][col_u] = u;
return ;
}
int temp = f[b][v][col_u];
f[a][u][col_u] = v, f[b][v][col_u] = u;
if(temp) //有矛盾
{
dfs(b, a, v, temp, col_v, col_u);
}
else //没有矛盾
{
f[b][v][col_v] = 0;
return ;
}
}
int main()
{
int a, b, m, maxx;
scanf("%d %d %d", &a, &b, &m);
maxx = 0;
for(int i = 1 ; i <= m ; i ++)
{
int x, y;
scanf("%d %d", &x, &y);
pos[x][y] = i;
int temp1 = 1,temp2 = 1;
while(f[0][x][temp1]) temp1 ++;
while(f[1][y][temp2]) temp2 ++;
maxx = max({maxx, temp1, temp2});
if(temp1 == temp2)
f[0][x][temp1] = y, f[1][y][temp2] = x;
else //暴力检查
{
dfs(0, 1, x, y, temp1, temp2);
}
}
for(int i = 1 ; i <= a; i ++)
{
for(int j = 1 ; j <= maxx ; j ++)
{
if(f[0][i][j])
{
ans[pos[i][f[0][i][j]]] = j;
}
}
}
printf("%d\n", maxx);
for(int i = 1 ; i <= m ; i ++)
printf("%d ", ans[i]);
return 0;
}