AtCoder Beginner Contest 409(A~F)
比赛链接:https://atcoder.jp/contests/abc409
G看了下,没想法就没去尝试做了
A.Conflict
思路:签到,正常扫一遍啦
#include<bits/stdc++.h>
#define ll long long
#define endl '\n'
using namespace std;
const ll N=5e5+45;
const ll mod=1e9+7;
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
ll t=1;
// cin>>t;
while (t--) {
string a,b;
ll n;
cin>>n;
cin>>a>>b;
ll ans=0;
for (ll i=0;i<n;i++) {
if (a[i]=='o'&&b[i]=='o')ans=1;
}
cout<<(ans==1?"Yes":"No")<<endl;
}
return 0;
}
B.Citation
思路:显然答案具有单调性,二分或者暴力枚举都可以
#include<bits/stdc++.h>
#define ll long long
#define endl '\n'
using namespace std;
const ll N=5e5+45;
const ll mod=1e9+7;
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
ll t=1;
// cin>>t;
while (t--) {
ll n;
cin>>n;
vector<ll>a(n+5);
for (ll i=1;i<=n;i++)cin>>a[i];
sort(a.begin()+1,a.begin()+1+n);
ll ans=0;
for (ll i=0;i<=100;i++) {
ll cnt=0;
for (ll j=1;j<=n;j++) {
if (a[j]>=i)cnt++;
}
if (cnt>=i)ans=i;
else break;
}
cout<<ans<<endl;
}
return 0;
}
C.Equilateral Triangle
思路:圆上的点想要构成一个等边三角形,肯定得有周长l为3的倍数(周长也会被三分),然后默认初始点为1,然后移动规则,对点进行移动并位置++,记得负数或者大于l的数转换到1~l内,随后再暴力遍历一遍算一次答案即可,这里算完得再除以三,因为你会对一个等边三角形算三遍
#include<bits/stdc++.h>
#define ll long long
#define endl '\n'
using namespace std;
const ll N=5e5+45;
const ll mod=1e9+7;
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
ll t=1;
// cin>>t;
while (t--) {
ll n,l;
cin>>n>>l;
vector<ll>a(n+5);
for (ll i=1;i<n;i++)cin>>a[i];
if (l%3!=0)cout<<0<<endl;
else {
vector<ll>pos(n+45);
pos[1]=1;
map<ll,ll>mp;
mp[1]++;
for (ll i=2;i<n+1;i++) {
pos[i]=a[i-1]+pos[i-1];
if (pos[i]>l)pos[i]-=l;
mp[pos[i]]++;
}
ll ans=0;
for (ll i=1;i<=l;i++) {
ll l1=i-l/3,r1=i+l/3;
if (l1<=0)l1+=l;
if (r1>l)r1-=l;
ans+=mp[i]*mp[l1]*mp[r1];
}
ans/=3;
cout<<ans<<endl;
}
}
return 0;
}
D.String Rotation
思路:构造字典序最小的字符串,在题目给出的操作中,实际就是某个段往前进了一位,多出来的移动最后面。我们可以简单地想到要移动的时候,即遇到第一个前一个字符比后一个字符大的情况,好我们多出来的移动到哪呢?应该移动到后面第一个比他大的字符的前面(保持字典序最小),至此答案得出
#include<bits/stdc++.h>
#define ll long long
#define endl '\n'
using namespace std;
const ll N=5e5+45;
const ll mod=1e9+7;
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
ll t=1;
cin>>t;
while (t--) {
string str;
ll n;
cin>>n;
cin>>str;
// ll n=str.size();
// vector<ll>pre(26,0);
// cout<<str.substr(0,0)<<endl;
ll flag=0;
string ans="";
char x;
for (ll i=0;i<str.size()-1;i++) {
if (str[i]>str[i+1]&&flag==0) {
string k=str.substr(0,i);
ans+=k;
x=str[i];
flag=1;
continue;
}
if (flag==1) {
if (x>=str[i]) {
ans+=str[i];
}
else ans+=x,flag=2,ans+=str[i];
}
else if (flag==2) {
ans+=str[i];
}
}
if (flag==0) {
ans=str;
}
else if (flag==1) {
if (str[n-1]<=x)ans+=str[n-1],ans+=x;
else ans+=x,ans+=str[n-1];
}
else ans+=str[n-1];
cout<<ans<<endl;
}
return 0;
}
E.Pair Annihilation
思路:看了题意后,可以发现最好是先合并再移动最好了,然后大家往某个点汇聚得出的答案是最小答案,可惜这个点不能直接得出,那么我们就试着一个每个点作为目标点(根)进行树上dp。首先以1为根建树,在过程中需要记录子树大小和这个子树上的点到这个这个子树上的根的花费,这个递归即可得出。随后就是dp(换根)时间,设此时儿子为to,权重为w,点为x。父亲的花费是包含儿子的所以\(qz[x]-=qz[to]\),随后又想到儿子to转移过来还需要儿子的移动费用,所以\(qz[x]-=abs(sz[to])*w\)(记得取绝对值哦),还有父亲的大小是包括儿子的,\(sz[x]-=sz[to]\),此时父亲的处理完成(成为儿子的第一步),那么儿子成为父亲其实就是模拟下建树过程就可了,\(qz[to]+=qz[x]+abs(sz[x])*w,sz[to]+=sz[x]\).随后进行递归即可,记得递归回来时得还原(上述代码倒着写一遍),至此边走边取min,答案得出
emm,好吧,电子总和为0,那么任意一点为根就好了(what can i say)
#include<bits/stdc++.h>
#define ll long long
#define endl '\n'
using namespace std;
const ll N=5e5+45;
const ll mod=1e9+7;
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
ll t=1;
// cin>>t;
while (t--) {
ll n;
cin>>n;
vector<ll>v(n+4);
vector<pair<ll,ll>>g[n+4];
for (ll i=1;i<=n;i++) {
cin>>v[i];
}
for (ll i=1;i<n;i++) {
ll l,r,w;
cin>>l>>r>>w;
g[l].push_back({r,w});
g[r].push_back({l,w});
}
vector<ll>sz(n+4,0),qz(n+4,0);
function<void(ll,ll)>dfs=[&](ll x,ll f) {
sz[x]=v[x];
for (auto [to,w]:g[x]) {
if (to==f)continue;
dfs(to,x);
sz[x]+=sz[to];
qz[x]+=abs(sz[to]*w)+qz[to];
}
};
dfs(1,0);
ll ans=1e18;
function<void(ll,ll)>df=[&](ll x,ll f) {
ans=min(ans,qz[x]);
for (auto [to,w]:g[x]) {
if (to==f)continue;
qz[x]-=abs(sz[to]*w)+qz[to];
sz[x]-=sz[to];
sz[to]+=sz[x];
qz[to]+=abs(sz[x]*w)+qz[x];
df(to,x);
qz[to]-=abs(sz[x]*w)+qz[x];
sz[to]-=sz[x];
sz[x]+=sz[to];
qz[x]+=abs(sz[to]*w)+qz[to];
}
};
df(1,0);
cout<<ans<<endl;
return 0;
}
}
F.Connecting Points
思路:个人觉得是非常板子得并查集题了。看了下数据范围,最多添加3000个点,记得写静态提前开好。首先使用并查集和优先队列(以边值为权重,从小往大排序)。对于操作1:就是加个点,然后我们把此时点所在位置也存起来,然后把1~n所有点和他构成的边,点对存到优先队列里,把他的并查集赋值为自己。对于操作2:首先对优先队列进行查看,是否点对在同一个并查集,直到踢到点对不在一个并查集或没有位置,如果队列是空的,那么肯定所有点都在一个并查集里,否则就设当前优先队列头顶边值为最小值,然后一直更新即可(踢到边值改变,过程中记得判断点对是否在同一个并查集(得直接踢掉))。对于操作3:就是正常并查集操作了,至此答案得出。
#include<bits/stdc++.h>
#define ll long long
#define endl '\n'
using namespace std;
const ll N=5e5+45;
const ll mod=1e9+7;
struct s {
ll l,r,v;
friend bool operator<(const s&a,const s&b) {
return a.v>b.v;
}
};
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
ll t=1;
// cin>>t;
while (t--) {
ll n,q;
cin>>n>>q;
vector<ll>b(3500,0);
vector<pair<ll,ll>>g(3500);
function<ll(ll)>find=[&](ll x) {
if (b[x]==x)return x;
else return b[x]=find(b[x]);
};
for (ll i=1;i<=n;i++) {
b[i]=i;
}
for (ll i=1;i<=n;i++) {
ll l,r;
cin>>l>>r;
g[i]={l,r};
}
priority_queue<s>que;
for (ll i=1;i<=n;i++) {
for (ll j=i+1;j<=n;j++) {
que.push({i,j,abs(g[i].first-g[j].first)+abs(g[i].second-g[j].second)});
}
}
while (q--) {
ll op;
cin>>op;
if (op==1) {
ll l,r;
cin>>l>>r;
for (ll i=1;i<=n;i++) {
que.push({i,n+1,abs(g[i].first-l)+abs(g[i].second-r)});
}
n++;
b[n]=n;
g[n]={l,r};
}
else if (op==2) {
while (!que.empty()) {
auto [l,r,w]=que.top();
if (find(l)==find(r))que.pop();
else break;
}
if (que.size()==0)cout<<-1<<endl;
else {
ll x=que.top().v;
cout<<x<<endl;
while (!que.empty()) {
auto [l,r,v]=que.top();
l=find(l),r=find(r);
if (l==r)que.pop();
else if (v==x) {
b[l]=r;
que.pop();
}
else break;
}
}
}
else {
ll l,r;
cin>>l>>r;
if (find(l)==find(r))cout<<"Yes"<<endl;
else cout<<"No"<<endl;
}
}
return 0;
}
}

浙公网安备 33010602011771号