ABC368 D-G题解 (AtCoder Beginner Contest 368)
D
树从叶子到根,对于某个点,如果其子树不存在需要的点,那么这个点和它的父亲所连的边,自然不需要,否则需要。
有一个问题,比如需要点2、4、5,那么点1和点2所连的边也算进去了。实际上,到了它们的LCS(最大公共祖先)后,这些边就不用算了。用一个变量统计当前遍历过多少需要的点,如果所有需要的点恰好都遍历过了,那么当前遍历的点为LCS,直接退出即可。
1 #include <bits/stdc++.h> 2 using namespace std; 3 #define LL long long 4 #define ULL unsigned long long 5 6 const LL mod_1=1e9+7; 7 const LL mod_2=998244353; 8 9 const double eps_1=1e-5; 10 const double eps_2=1e-10; 11 12 const int maxn=2e5+10; 13 14 vector<int> adj[maxn]; 15 bool vis[maxn]; 16 int k,r=0,value[maxn]; 17 18 void dfs(int d) 19 { 20 vis[d]=1; 21 for (auto dd:adj[d]) 22 if (!vis[dd]) 23 { 24 dfs(dd); 25 value[d]+=value[dd]; 26 } 27 if (value[d]>0) 28 r++; 29 if (value[d]==k) 30 { 31 cout<<r; 32 exit(0); 33 } 34 } 35 36 int main() 37 { 38 int n,i,a,b; 39 cin>>n>>k; 40 for (i=1;i<n;i++) 41 { 42 cin>>a>>b; 43 adj[a].push_back(b); 44 adj[b].push_back(a); 45 } 46 memset(value,0,sizeof(value)); 47 for (i=1;i<=k;i++) 48 { 49 cin>>a; 50 value[a]=1; 51 } 52 memset(vis,0,sizeof(vis)); 53 dfs(1); 54 return 0; 55 }
E
实际上,这道题挺好写的,不难。最少人做和做对,估计是:
1. 题意长,题面看着繁琐(挺多公式、变量),比较难懂,劝退了不少人
2. 实际上写方法、代码,也比较绕
3. G相对好做,大家先做了G
无法理解这么多WA,是因为它们没有考虑“X0也有可能在非首位(T最小)的位置”,这个?
TLE不少,难道是它们都尝试暴力做?或者是有什么"环“??没有对T进行排序?
另外,我看codeforces的讨论,E的讨论挺多的,它们有的往图的角度想,自然做不出来。
1 /* 2 T(到达时间)从小到大排序。 3 对于某一项,先确定X的值(即这一趟至少要延迟多长时间):对于这一站的起始城市Aj,即上一趟转乘的到达城市Bi(Aj=Bi),找到城市Bi中到达时间(Ti)<=这一项的出发时间(Sj)中,到达+延迟时间最大(Ti+Xi)的,然后和这一项的出发时间(Sj)做比较,取max,从而也获得了这一项的X的值,X=max( max{Ti+Xi} - Sj, 0)。 4 然后对于这一项的到达城市Bj,更新对于城市Bj,到达 / 到达+延迟时间的信息(记录第一个是到达时间,记录第二个对应的到达+延迟时间,城市Bj的这两项信息按到达时间从小到大的排序)。 5 X0也有可能在非首位(T最小)的位置 6 */ 7 #include <bits/stdc++.h> 8 using namespace std; 9 #define LL long long 10 #define ULL unsigned long long 11 12 const LL mod_1=1e9+7; 13 const LL mod_2=998244353; 14 15 const double eps_1=1e-5; 16 const double eps_2=1e-10; 17 18 const int maxn=2e5+10; 19 20 LL x[maxn]; 21 //a[maxn],b[maxn],s[maxn],t[maxn] 22 struct node 23 { 24 LL a,b,s,t,index; 25 bool operator<(node v) 26 { 27 return t < v.t; 28 } 29 } info[maxn]; 30 31 vector<LL> lat[maxn], tim[maxn]; 32 33 int main() 34 { 35 LL n,m,i,a,b,add; 36 memset(x,0,sizeof(x)); 37 cin>>n>>m>>x[1]; 38 for (i=1;i<=m;i++) 39 { 40 //cin>>a[i]>>b[i]>>s[i]>>t[i]; 41 cin>>info[i].a>>info[i].b>>info[i].s>>info[i].t; 42 info[i].index=i; 43 } 44 sort(info+1,info+m+1); 45 46 //cout<<"test "<<info[1].t<<endl; 47 48 for (i=1;i<=m;i++) 49 if (info[i].index==1) 50 break; 51 52 b = info[i].b; 53 tim[b].push_back( info[i].t ); 54 lat[b].push_back( info[i].t + x[1] ); 55 56 while (i<=m) 57 { 58 i++; 59 60 a = info[i].a; 61 auto it = upper_bound(tim[a].begin(), tim[a].end(), info[i].s); 62 if (it==tim[a].begin()) 63 continue; 64 65 it--; 66 add = max( *(it-tim[a].begin() + lat[a].begin()) - info[i].s , 0LL); 67 x[ info[i].index ] = add; 68 69 if (add!=0) 70 { 71 b = info[i].b; 72 if (lat[b].empty() || lat[b].back() < info[i].t + add) 73 { 74 tim[b].push_back(info[i].t); 75 lat[b].push_back(info[i].t+add); 76 } 77 } 78 } 79 80 for (i=2;i<=m;i++) 81 { 82 cout<<x[i]; 83 if (i!=m) 84 cout<<" "; 85 } 86 87 return 0; 88 }
F
这么多人做和做对,怀疑用了chatgpt,emmm。现在数论题,感觉chatgpt很容易搜出正确方法。
实际上,写代码过程中,vscode自动补全了很多代码。
公平组合游戏 - OI Wiki (oi-wiki.org)
所有SG(x)可以进行进行初始化。
求一个数的质因数,因为是1e5范围,可以用比较快、不容易写错的方式写。
1 #include <bits/stdc++.h> 2 using namespace std; 3 #define LL long long 4 #define ULL unsigned long long 5 6 const LL mod_1=1e9+7; 7 const LL mod_2=998244353; 8 9 const double eps_1=1e-5; 10 const double eps_2=1e-10; 11 12 const int maxn=1e5+10; 13 14 vector<int> zys[maxn]; 15 int sg[maxn]; 16 17 int main() 18 { 19 int n,i,j,x,max_sg=1e5,r=0; 20 vector<int> vec; 21 22 for (i=1;i<=max_sg;i++) 23 { 24 for (j=i+i;j<=max_sg;j+=i) 25 zys[j].push_back(i); 26 } 27 sg[1]=0; 28 for (i=2;i<=max_sg;i++) 29 { 30 vec.clear(); 31 for (auto z:zys[i]) 32 vec.push_back(sg[z]); 33 sort(vec.begin(),vec.end()); 34 vec.erase(unique(vec.begin(),vec.end()),vec.end()); 35 for (j=0;j<vec.size();j++) 36 if (vec[j]!=j) 37 break; 38 sg[i]=j; 39 } 40 41 cin>>n; 42 for (i=1;i<=n;i++) 43 { 44 cin>>x; 45 r^=sg[x]; 46 } 47 48 if (r==0) 49 cout<<"Bruno"<<endl; 50 else 51 cout<<"Anna"<<endl; 52 53 return 0; 54 }
最基础的SG:nim几堆,每次可以拿一堆的任意数目
1 /* 2 TODO 找例题做: 3 https://www.cnblogs.com/AWhiteWall/p/14403827.html 4 https://www.luogu.com.cn/article/1am7gm8b 5 */ 6 #include <bits/stdc++.h> 7 using namespace std; 8 #define LL long long 9 #define ULL unsigned long long 10 11 const LL mod_1=1e9+7; 12 const LL mod_2=998244353; 13 14 const double eps_1=1e-5; 15 const double eps_2=1e-10; 16 17 const int maxn=2e5+10; 18 19 vector<int> vec; 20 int sg[maxn]; 21 22 int main() 23 { 24 int i,j,n=10; 25 sg[0] = 0; 26 vec.push_back(sg[0]); 27 for (i=1;i<=n;i++) 28 { 29 for (j=0;j<vec.size();j++) 30 if (vec[j]!=j) 31 break; 32 sg[i]=j; 33 34 vec.push_back(sg[i]); 35 } 36 37 for (i=0;i<=n;i++) 38 cout<<sg[i]<<" "; 39 //0 1 2 3 4 5 6 7 8 9 10 40 41 return 0; 42 }
1 //luogu P2197 2 #include <bits/stdc++.h> 3 using namespace std; 4 #define LL long long 5 #define ULL unsigned long long 6 7 const LL mod_1=1e9+7; 8 const LL mod_2=998244353; 9 10 const double eps_1=1e-5; 11 const double eps_2=1e-10; 12 13 const int maxn=2e5+10; 14 15 int main() 16 { 17 int T,n,i,x,r; 18 cin>>T; 19 while (T--) 20 { 21 r=0; 22 cin>>n; 23 for (i=0;i<n;i++) 24 { 25 cin>>x; 26 r^=x; 27 } 28 if (r==0) 29 cout<<"No"<<endl; 30 else 31 cout<<"Yes"<<endl; 32 } 33 34 return 0; 35 }
UVA 1482
1 /* 2 https://vjudge.net/problem/UVA-1482 3 https://www.luogu.com.cn/problem/UVA1482 4 5 要写成1LL<<k,对于long long 6 */ 7 #include <bits/stdc++.h> 8 using namespace std; 9 #define LL long long 10 #define ULL unsigned long long 11 12 const LL mod_1=1e9+7; 13 const LL mod_2=998244353; 14 15 const double eps_1=1e-5; 16 const double eps_2=1e-10; 17 18 const int maxn=2e5+10; 19 20 int sg[maxn], valid_sg[maxn]; 21 vector<int> vec; 22 23 void get_sg() 24 { 25 int n=1000,i,j,k,siz; 26 sg[0]=0; 27 sg[1]=0; 28 29 for (i=2;i<=n;i++) 30 { 31 vec.clear(); 32 for (j=(i+1)/2;j<i;j++) 33 vec.push_back(sg[j]); 34 35 sort(vec.begin(), vec.end()); 36 vec.erase(unique(vec.begin(), vec.end()), vec.end()); 37 38 siz = vec.size(); 39 for (k=0;k<siz;k++) 40 if (vec[k]!=k) 41 break; 42 sg[i]=k; 43 } 44 45 for (i=1;i<=n;i++) 46 cout<<sg[i]<<" "; 47 } 48 49 void validate() 50 { 51 int n=1000,i,j,cnt; 52 53 for (i=1;i<=n;i++) 54 { 55 j=i; 56 cnt=1; 57 while (j) 58 { 59 if (j%2) 60 cnt++; 61 else 62 break; 63 j/=2; 64 } 65 66 valid_sg[i] = (i - (1<<(cnt-1)) + 1) / (1<<cnt); 67 } 68 69 for (i=1;i<=n;i++) 70 if (sg[i]!=valid_sg[i]) 71 cout<<"wrong "<<i<<endl; 72 } 73 74 int main() 75 { 76 /* 77 get_sg(); 78 validate(); 79 return 0; 80 */ 81 82 LL T,n,cnt=0,r,a,i; 83 cin>>T; 84 while (T--) 85 { 86 r=0; 87 cin>>n; 88 while (n--) 89 { 90 cin>>a; 91 92 i=a; 93 cnt=1; 94 while (i) 95 { 96 if (i%2) 97 cnt++; 98 else 99 break; 100 i/=2; 101 } 102 103 r ^= (a - (1LL<<(cnt-1LL)) + 1LL) / (1LL<<cnt); 104 105 } 106 107 if (r) 108 cout<<"YES"<<endl; 109 else 110 cout<<"NO"<<endl; 111 } 112 113 114 115 return 0; 116 }
G
可以看这个:AtCoder Beginner Contest 368 - ~Lanly~ - 博客园 (cnblogs.com)。
看standing,非常多人做出来。
其实样例1给了提示,在乘积为2的情况下,都选择加号,1+2+4=7。
如果Bi不为1,下一次B_{i+1}最小的数值必大于等于Bi*2。因为数目要小于等于10^18,2^60是最多了。即最多60次左右的乘法操作,然后相邻的加法操作要统一处理,由于加法和乘法是间隔的,所以加法操作也最多60次左右。
统计Bi="1"的区间,这样可以方便快速跳过它。
Ai区间和,统计Bi="1"的区间,都用树状数组来做。
对于区间首个Bi=1,用二分获取下一个非Bi=1的位置。
样例:
8 1 2 3 4 5 6 7 8 1 1 1 2 1 1 3 1 1 3 1 8 output: 77
1 1 1 2 1 1 3 1 以此分为若干组
1 /* 2 先分析Ai、Bj不进行修改时,怎么求Type 3 3 */ 4 #include <bits/stdc++.h> 5 using namespace std; 6 #define LL long long 7 #define ULL unsigned long long 8 9 const LL mod_1=1e9+7; 10 const LL mod_2=998244353; 11 12 const double eps_1=1e-5; 13 const double eps_2=1e-10; 14 15 const int maxn=1e5+10; 16 17 LL a[maxn], b[maxn],ca[maxn],cb[maxn]; 18 19 LL n; 20 21 void update(LL * arr, LL x, LL y) 22 { 23 LL i; 24 for (i=x;i<=n;i+=i&-i) 25 arr[i]+=y; 26 } 27 28 LL getsum(LL * arr, LL x) 29 { 30 LL i,ans=0; 31 for (i=x;i>=1;i-=i&-i) 32 ans+=arr[i]; 33 return ans; 34 } 35 36 int main() 37 { 38 LL i,q,type,l,r,value,num1,num2,w,l_old; 39 memset(ca,0,sizeof(ca)); 40 memset(cb,0,sizeof(cb)); 41 cin>>n; 42 for (i=1;i<=n;i++) 43 { 44 cin>>a[i]; 45 update(ca,i,a[i]); 46 } 47 for (i=1;i<=n;i++) 48 { 49 cin>>b[i]; 50 if (b[i]!=1) 51 update(cb,i,1); 52 } 53 54 cin>>q; 55 while (q--) 56 { 57 cin>>type>>l>>r; 58 if (type==1) 59 { 60 update(ca,l,r-a[l]); 61 a[l]=r; 62 } 63 else if (type==2) 64 { 65 if (b[l]==1 && r!=1) 66 update(cb,l,1); 67 else if (b[l]!=1 && r==1) 68 update(cb,l,-1); 69 b[l]=r; 70 } 71 else 72 { 73 value=0; 74 while (l<=r) 75 { 76 while (b[l]!=1 && l<=r) 77 { 78 value = max(value+a[l], value*b[l]); 79 l++; 80 } 81 if (l>r) 82 break; 83 84 l_old = l; 85 num1 = getsum(cb,l); 86 for (w = (LL)ceil( log(r-l)/log(2)+eps_2 ) ; w>=0; w--) 87 { 88 if (l+(1<<w)>r) 89 continue; 90 num2 = getsum(cb,l+(1<<w)); 91 if (num1==num2) 92 { 93 l+=(1<<w); 94 num1 = num2; 95 } 96 } 97 value += getsum(ca,l)-getsum(ca,l_old-1); 98 l++; 99 } 100 cout<<value<<endl; 101 } 102 } 103 104 return 0; 105 }