AtCoder Beginner Contest 371
网址:ATcoder Beginner Contest 371
A.Jiro
题目翻译:
给定三个符号,分别是 S A B , S A C , S B C S_{AB}, S_{AC}, S_{BC} SAB,SAC,SBC,这三个符号只有 > 和 < > 和 < >和<两种。表示第一个字母和第二个字母的关系。问:既不是最大也不是最小的那个字母是谁?
题解:
签到题。
这里给出几种常见的解法。
题解A:
直接暴力打表,主要是别写错就可以了。
#include<iostream>
using namespace std;
int main(){
char a, b, c;
cin>>a>>b>>c;
if(a=='<'){
if(c=='<')cout<<'B'<<endl;
else{
if(b=='<')cout<<'C'<<endl;
else cout<<'A'<<endl;
}
}
else{
if(c=='>')cout<<'B'<<endl;
else{
if(b=='<')cout<<'A'<<endl;
else cout<<'C'<<endl;
}
}
return 0;
}
题解B:
把所有的大鱼号和小鱼号看成1和0
那么每一种其实是一个状态,你只需要把每个状态枚举出来就可以了。三个符号8种状态,离散学过的。
#include<iostream>
using namespace std;
int main(){
char a[4];
for(int i=1; i<=3; ++i)cin>>a[i];
int num[3];
for(int i=1; i<=3; ++i){
num[i-1]=(a[i]=='<');
}
int ans=num[0]*4+num[1]*2+num[2];
// cout<<ans<<endl;
switch(ans){
case 1:case 6:{
cout<<'C';
break;
}
case 0:case 7:{
cout<<'B';
break;
}
case 3:case 4:{
cout<<'A';
break;
}
default:{
cout<<"E"<<endl;
break;
}
}
return 0;
}
题解C:
这是一种没有什么用的解法。
我们思考这样的问题。反正就八个样例,你直接归个类。
我们注意到如果第二个字和第三个字不一样,那么不是无解就是C。因为不会有无解的样例所以可以直接输出C。然后就剩下四种情况了,且二三必定相同,那么观察得如果一二不同就是A,相同就是B
非常神奇,所以code也比较友好。
#include<iostream>
using namespace std;
int main(){
char a, b, c;
cin>>a>>b>>c;
bool A, B, C;
A=(a=='<');
B=(b=='<');
C=(c=='<');
if(B^C)cout<<'C'<<endl;
else if(A^B)cout<<'A'<<endl;
else cout<<'B'<<endl;
return 0;
}
个人喜欢解法A因为我视力不好,观察时间太长
B.谁是长子
题目翻译:
给你一个n一个m。表示一共n个从来没有孩子的家庭按顺序(?)生了m个小孩,并按顺序告诉你他们属于哪个家庭,性别是啥。问这个孩子是不是长子。
题解:
签到题plus
那很明显啊长子这个东西显然是一个萝卜一个坑的东西对吧,所以你就把坑提前开好就可以了,如果这个家庭生了个男孩就跳进去然后显示里面有人,并输出一个YES,如果还没跳坑里就有人了那就直接原地No了,女孩包No的。
#include<iostream>
#include<cstring>
using namespace std;
const int N=110;
bool wc[N];
int main(){
int n, m;
cin>>n>>m;
memset(wc, 0, sizeof wc);
for(int i=1; i<=m; ++i){
int u;
char v;
cin>>u>>v;
if(v=='F')cout<<"No"<<endl;
else{
if(wc[u])cout<<"No"<<endl;
else {
cout<<"Yes"<<endl;
wc[u]=true;
}
}
}
return 0;
}
advanced:
如果这道题的u是到2e9,在u的种类是2e5的情况下也是可以做的开个map即可
C.同构
读了半天题没看懂讲的是啥
题目翻译:
给两张图,一张G一张H,以及这张H链接或者删除i和j的代价,需要你用最小的代价使H和G同构。
解释同构:同构是指GH的形状相同 ⇒ \Rightarrow ⇒G本身有编号,你给H编号,存在一种编号是G
题解:
这是一道教学你怎么next_permulation的题目。
这里给出next_permulation最基础的解法:如果你想知道一个数组比它字典序稍微小一点,且只小一点的排列,(通俗地),你可以直接
next_permulation(a+1,a+n+1);
知道这个之后就很明显了。算法步骤如下:
- 获取一个新的排列
- 把这个节点序列按顺序排成升序
- 将新的图与G进行比对
- 如果节点的所有边都与G相同,那么这个节点合格
- 如果不同,多退少补,res+ w i j w_{ij} wij
- 循环到第一步
做到什么时候为止呢?
一个数列的排列,有且仅 n ! n! n!种,所以你就循环 n ! − 1 n!-1 n!−1就好了,因为初始12345不用循环,当然,边界问题并不重要,你甚至可以循环 n k n^k nk,反正也不好超时,不用特殊耗费脑子
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=100;
int h[3][N], ne[3][N], e[3][N], idx[3];
int w[10][10];
void add(int a, int b, int id){
int i=id;
int idd=idx[i];
e[i][idd]=b;
ne[i][idd]=h[i][a];
h[i][a]=idx[i]++;
}
int p[N]={0,1,2,3,4,5,6,7,8,9,10};
int behave(int i){
if(i==0 || i==1)return i;
return i*behave(i-1);
}
int solve(int n){
int ans=0;
for(int i=1; i<=n; ++i){
bool st[10];
memset(st, 0, sizeof st);
for(int j=h[1][i]; j!=-1; j=ne[1][j]){
st[e[1][j]]=true;
}
for(int j=h[2][p[i]]; j!=-1; j=ne[2][j]){
int x=e[2][j];
for(int k=1; k<=n; ++k){
if(p[k]==x){
x=k;
break;
}
}
if(x<=i)continue;
if(st[x]==false){
ans+=w[p[i]][p[x]];
}
else st[x]=false;
}
for(int j=i+1; j<=n; ++j){
if(st[j]==true){
ans+=w[p[i]][p[j]];
}
}
}
return ans;
}
int main(){
int n;
cin>>n;
memset(h, -1, sizeof h);
memset(idx, 0, sizeof idx);
for(int i=1; i<=2; ++i){
int m;
cin>>m;
for(int j=1; j<=m; ++j){
int u, v;
cin>>u>>v;
add(v, u, i);
add(u, v, i);
}
}
for(int i=1; i<n; ++i){
w[i][i]=0;
for(int j=i+1; j<=n; ++j){
int k;
cin>>k;
w[i][j]=k;
w[j][i]=k;
}
}
int ans=0x3f3f3f3f;
for(int i=1; i<=behave(n); ++i){
next_permutation(p+1, p+1+n);
ans=min(ans, solve(n));
}
cout<<ans<<endl;
return 0;
}
D.在只有x轴的MC里数村民
题目翻译:
给定 n n n个村庄和一个横轴, n n n个村庄在不同的横坐标,每个村庄有 p p p个村民,然后是 Q Q Q次问询,每次问询给定 L R LR LR,求区间内村民的总数
题解:
显然区间求和最简单易懂的前缀和问题。
直接前缀和打上去,然后找 L 和 R L和R L和R分别在那里,分析一下复杂度,爆搜包寄的,所以应该就是一个二分。
前缀和显然是 O ( 1 ) O(1) O(1)的查找, O ( N ) O(N) O(N)的预处理,然后二分是一个 O ( l o g n ) O(logn) O(logn)的复杂度,整体的复杂度应该是 m a x ( Q l o g n , N ) max(Qlogn, N) max(Qlogn,N)这样的一个复杂度,还是可以过的
#include<iostream>
#include<algorithm>
using namespace std;
const int N=2e5+100;
struct node{
int x, p;
}e[N];
bool cmp(node a, node b){
return a.x<b.x;
}
int n;
int find(int x){
int l=1, r=n+1;
while(l<r){
int mid=l+r>>1;
if(e[mid].x>=x)r=mid;
else l=mid+1;
}
if(x<=e[l].x)return l;
else return r;
}
long long w[N];
int main(){
cin>>n;
for(int i=1; i<=n; ++i)cin>>e[i].x;
for(int i=1; i<=n; ++i)cin>>e[i].p;
e[n+1].x=0x3f3f3f3f;
e[n+1].p=0;
w[0]=0;
for(int i=1; i<=n; ++i)w[i]=w[i-1]+e[i].p;
sort(e+1, e+n+2, cmp);
int q;
cin>>q;
for(int i=1; i<=q; ++i){
int l, r;
cin>>l>>r;
int xl=find(l);
int xr=find(r);
if(e[xr].x!=r)xr--;
cout<<w[xr]-w[xl-1]<<endl;
}
return 0;
}
E.唐题
题目翻译:
给定一串数列,问你它的 ∑ i = 1 n ∑ j = i n d i s t i , j \sum_{i=1}^n\sum_{j=i}^ndist_{i,j} ∑i=1n∑j=indisti,j。 d i s t i , j dist_{i,j} disti,j是 [ i , j ] [i,j] [i,j]区间里不同数字的个数
题解:
这是一个序列,而且要求的东西是一些有规律的区间,所以可以考虑元素的贡献队答案的贡献。对于每个元素,我们需要考虑针对于以这个元素为左端点的所有区间,它后面的元素对这些区间的总贡献。
那么我们可以得到这样的结论:
在第 i 位置 ( i ∈ [ 1 , n ] ) 的元素 = { 0 [ l , i ) 中存在 a i n − i + 1 e l s e 在第i位置(i\in [1, n])的元素=\begin{cases}0&[l, i)中存在a_i\\n-i+1&else \end{cases} 在第i位置(i∈[1,n])的元素={0n−i+1[l,i)中存在aielse
那么明显的,我们可以得到一张大表。
然后把这张表上所有的数据加在一起,显然就是结论了。那么我们考虑如何去优化它。
针对于在位置 i i i上的元素 a i a_i ai,它最多被加 n − i + 1 n-i+1 n−i+1共加 i i i次。那么如果前面有一个 a j = a i ( j < i ) a_j=a_i(j<i) aj=ai(j<i),那么它就会被置 ( j − 1 ) (j-1) (j−1)个 0 0 0,那么我们就知道,我们只需要检索它左面最近的和它相同的数和它的距离 ( i − j ) (i-j) (i−j),那么 a i a_i ai对 a n s ans ans的总贡献应该是 ( i − j ) × ( n − i + 1 ) (i-j)\times (n-i+1) (i−j)×(n−i+1)
所以答案应该是 ∑ i = 1 n ( i − j ) × ( n − i + 1 ) \sum_{i=1}^{n}(i-j)\times (n-i+1) ∑i=1n(i−j)×(n−i+1)并且 a j = a i a_j=a_i aj=ai且 ∄ k ∈ ( j , i ) , a k = a i \not\exist k\in(j, i), a_k=a_i ∃k∈(j,i),ak=ai
#include<iostream>
#include<cstring>
using namespace std;
const int N=2e5+100;
int a[N];
long long net[N], lst[N];
int main(){
int n;
cin>>n;
memset(lst, 0, sizeof lst);
long long ans=0;
for(int i=1; i<=n; ++i){
cin>>a[i];
ans+=(n-i+1)*(i-lst[a[i]]);
lst[a[i]]=i;
}
cout<<ans<<endl;
return 0;
}