AtCoder Beginner Contest 373
这场咋这么简单
A. September \(\text{diff 11}\)
给你 \(12\) 个字符串 \(S_1\) 到 \(S_{12}\),问你有多少字符串满足 \(|S_i|=i\)
点击查看代码
using namespace reader;
string s[13];
int main(){
for(int i=1;i<=12;++i){
cin>>s[i];
}
int ans=0;
for(int i=1;i<=12;++i){
if(s[i].length()==i){
ans++;
}
}
cout<<ans<<endl;
}
B. 1D Keyboard \(\text{diff 54}\)
一个线性排列的键盘,给你所有字母的位置,问你用一根手指打出
ABCDEFGHIJKLMNOPQRSTUVWXYZ
需要的最小移动键位数
点击查看代码
using namespace reader;
#define int long long
string s;
int locate[1256];
signed main(){
cin>>s;
int ans=0;
for(int i=0;i<=(int)s.length()-1;++i){
locate[s[i]]=i;
}
for(int i='A';i<'Z';++i){
ans+=abs(locate[i]-=locate[i+1]);
}
cout<<ans<<endl;
}
C. Max Ai+Bj \(\text{diff 75}\)
给定两个序列 \(A,B\),任意选定 \(i,j\),求 \(\max(A_i+B_j)\)
点击查看代码
using namespace reader;
#define int long long
int n;
int a[5000001],b[5000001];
signed main(){
read(n);
for(int i=1;i<=n;++i){
read(a[i]);
}
for(int i=1;i<=n;++i){
read(b[i]);
}
sort(a+1,a+n+1);
sort(b+1,b+n+1);
cout<<a[n]+b[n]<<endl;
}
D. Hidden Weights \(\text{diff 765}\)
一个有向图,每条边 \(i\) 都有其权值 \(w_i\),连接节点 \(u_i,v_i\),询问一种点权 \(x_i\) 的构造方案,使得对于任意边 \(i\),都满足 \(x_{v_i}-x_{u_i}=w_i\)
\(N\le 2\times 10^5\),保证有解
注意到,你可以直接设一个点是 \(0\),然后从这个点出发开始搜,对搜到的目标点赋成 \(x_{u_i}+w_i\) 的权值
但是这题是有向图,这意味着你从某一个点出发很可能搜不到连通块内所有的节点,这就会错,所以考虑把这道题变成无向图
无向图也很简单,你只需要对所有的 \((u_i,v_i,w_i)\),建一条 \((v_i,u_i,-w_i)\) 的反边即可
注意图不联通的情况
点击查看代码
using namespace reader;
#define int long long
int n,m;
struct edge{
int to,w;
};
vector<edge>e[200001];
bool vis[200001];
int dx[200001];
void dfs(int now,int _dx){
dx[now]=_dx;
vis[now]=true;
for(edge i:e[now]){
if(!vis[i.to]){
dfs(i.to,_dx+i.w);
}
}
}
signed main(){
read(n,m);
for(int i=1;i<=m;++i){
int x,y,z;
read(x,y,z);
e[x].push_back({y,z});
e[y].push_back({x,-z});
}
for(int i=1;i<=n;++i){
if(!vis[i]){
dfs(i,0);
}
}
for(int i=1;i<=n;++i){
cout<<dx[i]<<" ";
}
cout<<endl;
}
E. How to Win the Election \(\text{diff 1592}\)
\(N\) 个人举行选举,一个人胜出当且仅当有少于 \(M\) 个人的选票数量严格大于他
一共有 \(K\) 张选票,现在已经选出了若干张,给你当前每个人得票数量 \(A_i\)
请你对每个人判断,他至少需要从剩余选票里获得几张,才能保证胜出
无解输出 \(-1\)\(M,N\le 2\times 10^5\)
\(K,A_i\le 10^{12}\)
对每个 \(i\) 二分答案
考虑现在正在 check \(p\) 获得 \(t\) 张选票的情况,贪心地想,为了使大于 \(i\) 的人数最多,我们应该找出小于等于 \(i\) 的人里最大的,然后依次将剩余的 \(K-\sum A_i-t\) 依次分配给这些人,让每个人正好比 \(A_p+t\) 大 \(1\),这样是人数最大的情况,直接判断这个人数和 \(M\) 的大小关系即可
但这是 \(N^2\log\) 的做法,显然过不了,考虑对内层二分答案
设 \(sp=A_p+t\),只考虑不大于 \(sp\) 的人,那么从中能选出 \(r\) 个人,当且仅当
右边是定值,左边做法显然,维护一个前缀和即可 check
要注意的是,不能把本来就大于 \(sp\) 的人算进二分答案里,而是要直接用一个 upper_bound 找到数量然后加到答案里,因为式子里用的是 \(\sum(sp+1-A_{j})\),而不是 \(\sum(\max(0,sp+1-A_{j}))\),所以你把大于的人算进二分答案,那么直接对选票造成负贡献就错了
然后还有就是记得从二分里把这个人自己扣掉
点击查看代码
using namespace reader;
#define int long long
int n,m,k,remain;
struct node{
int id,a;
bool operator <(const node &A)const{
return a<A.a;
}
};
node a[400001];
int sum[400002];
bool check(int i,int _ans){
int nowa=a[i].a+_ans;
int rem=remain-_ans,tot=0;
int pos=upper_bound(a+1,a+n+1,node{n+1,nowa})-a;
tot+=n-pos+1;
if(tot>=m) return false;
int l=1,r=pos-1,ans=-1;
while(l<=r){
int mid=(l+r)/2;
if((pos-mid-(mid<=i?1:0))*(nowa+1)-(sum[mid]-sum[pos]-(mid<=i?a[i].a:0))<=rem){
r=mid-1;
ans=mid;
}
else{
l=mid+1;
}
}
if(ans==-1) return true;
tot+=pos-ans-(ans<=i?1:0);
return tot<m;
}
int aans[400001];
signed main(){
read(n,m,k);remain=k;
for(int i=1;i<=n;++i){
read(a[i].a);
a[i].id=i;
remain-=a[i].a;
}
sort(a+1,a+n+1);
for(int i=n;i>=1;--i){
sum[i]=sum[i+1]+a[i].a;
}
for(int i=1;i<=n;++i){
int l=0,r=remain,ans=-1;
while(l<=r){
int mid=(l+r)/2;
if(check(i,mid)){
r=mid-1;
ans=mid;
}
else{
l=mid+1;
}
}
aans[a[i].id]=ans;
}
for(int i=1;i<=n;++i){
cout<<aans[i]<<" ";
}
cout<<endl;
}
G. No Cross Matching \(\text{diff 2377}\)
二维平面内点 \(P_i,Q_i\) 各 \(N\) 个,请你找出一种连接方式,满足
- \(P_i,Q_i\) 全部连接
- 只存在 \(P_i,Q_i\) 间的连接
- 平面内无相交线段
\(N\le 300\)
考虑为 \(Q\) 分配一个排列 \(S\),使得 \(Q_{S_i}\) 与 \(P_i\) 连边
然后我们做类似冒泡排序的算法,枚举两条线 \(l_1,l_2\),如果 \(l_1\) 与 \(l_2\) 相交,则交换排列中对应位置的值,即交换两条线中的 \(Q\)
形象化的图
为什么我们做冒泡排序是对的,以三条线的情况为例
抽象化的图
通过简单手摸一下可以发现,这样交换线段,当前线段不会和之前已经交换过的线段相交,所以这就符合了我们做冒泡排序的性质,每一条线段总会以最多 \(O(n)\) 次的交换排到正确的位置上
所以直接对序列做冒泡排序即可
然后是怎么 check 两条线段是否相交
观察到可以直接求两条线段斜截式,设 \(l_1:k_1x+b_1,l_2:k_2x+b_2\),\(l_1\) 端点 \((x_1,y_1)\),\((x_2,y_2)\),\(l_2\) 端点 \((x_3,y_3)\),\((x_4,y_4)\)(\(y_2\ge y_1,y_4\ge y_3\)),则两条线段相交当且仅当
当然这只能处理能用斜截式表示的线段
对于竖线
- 若两条线段都是竖线,横坐标相等,则判断靠上直线下端点与靠下直线上端点
- 若两条线段都是竖线,横坐标不相等,不相交
- 否则,求出一条直线的斜截式,找另一条直线与其交点判断即可
点击查看代码
#include<bits/stdc++.h>
using namespace std;
struct node{
int x,y;
};
int n;
node p[501],q[501];
int ans[501];
struct line{
long double k,b;
inline long double cal(int x){
return x*k+b;
}
};
inline line line_cons(node x,node y){
line ans;
ans.k=(x.y-y.y)*1.0/(x.x-y.x);
ans.b=x.y-ans.k*x.x;
return ans;
}
inline bool iscross(node x1,node x2,node y1,node y2){
if(x1.x==x2.x){
if(y1.x==y2.x){
if(x1.x==y1.x){
return min(max(x1.y,x2.y),max(y1.y,y2.y))>=max(min(x1.y,x2.y),min(y1.y,y2.y));
}
return false;
}
line b=line_cons(y1,y2);
return b.cal(x1.x)>=min(x1.y,x2.y) and b.cal(x1.x)<=max(x1.y,x2.y);
}
if(y1.x==y2.x){
line a=line_cons(x1,x2);
return a.cal(y1.x)>=min(y1.y,y2.y) and a.cal(y1.x)<=max(y1.y,y2.y);
}
line a=line_cons(x1,x2),b=line_cons(y1,y2);
if((a.cal(y1.x)>=y1.y and a.cal(y2.x)<=y2.y) or (a.cal(y1.x)<=y1.y and a.cal(y2.x)>=y2.y)){
if((b.cal(x1.x)>=x1.y and b.cal(x2.x)<=x2.y) or (b.cal(x1.x)<=x1.y and b.cal(x2.x)>=x2.y)){
return true;
}
}
return false;
}
int main(){
cin>>n;
for(int i=1;i<=n;++i){
cin>>p[i].x>>p[i].y;
}
for(int i=1;i<=n;++i){
cin>>q[i].x>>q[i].y;
ans[i]=i;
}
while(1){
bool flag=false;
for(int i=1;i<=n;++i){
for(int j=1;j<=i-1;++j){
if(iscross(p[i],q[ans[i]],p[j],q[ans[j]])){
swap(ans[i],ans[j]);
flag=true;
}
}
}
if(!flag) break;
}
for(int i=1;i<=n;i++){
cout<<ans[i]<<" ";
}
cout<<"\n";
}
膜拜weige
啥玩意
不给参加运动会是吧,我们 BA 有自己的运动会
BA 是一款 16+ 的 4+ 游戏
优香:
来 让歌声响亮
白子:
我们在全场最中央
合:
让我们在一起 高喊胜利口号 (嗨)
Na LiLa BaLaBa
白子/泉奈:
准备好了就出发
合:
Na lila balaba
玛丽/晴奈:
在温暖的阳光下
合:
Na lila balaba
优香:
迈着整齐的步伐
合:
来将汗水挥洒 一起加油吧
晴奈:
各就位
优香:
背包整备
晴奈/优香:
礼炮后接啦啦队
白子:
原地起飞
玛丽:
注意补水
白子/玛丽:
破坏场地记得赔
泉奈:
美食摊位
晴奈:
好多美味
泉奈/晴奈:
是谁在喊要减肥
晴奈/优香:
就算再累
白子/玛丽:
也无所谓
合:
下一个对手是谁
Lalala lalalalalala lalala lalalalalalalala
Lalala lalalalalala lalala lalalalalalala
优香:
视线在搜寻
白子:
看台上你的身影
玛丽:
有没有看清
晴奈:
我努力
泉奈:
在挥手致意
优香:
广播夹杂电流音 但我听见你
白子:
为我 加油 打气
优香:
啊
让情绪更高涨
白子:
目标是终点更前方
玛丽:
让笑容在脸上
合:
尽情绽放
白子/泉奈:
让我陪在你身旁
玛丽/晴奈:
满怀蔚蓝色的希望
合:
让我们在一起 写下新的篇章
Na lila balaba
白子/泉奈:
准备好了就出发
合:
Na lila balaba
玛丽/晴奈:
在温暖的阳光下
合:
Na lila balaba
优香:
迈着整齐的步伐
合:
来将汗水挥洒 一起加油吧
来将汗水挥洒 一起加油吧