赛时困得睡着了
第二题赛事一直在想模拟加贪心,却发现非常难实现,我们需要转变思维,这时候一般有3种可能
双指针?二分?dp?
后来清醒了一点我们判断一个答案是不是合法很容易,我们再考虑答案求最大值,显然具有单调性,所以我们考虑二分答案,前缀和u优化判断
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 // Problem: 摆放棋子 // Contest: AcWing // URL: https://www.acwing.com/problem/content/5562/ // Memory Limit: 256 MB // Time Limit: 1000 ms // // Powered by CP Editor (https://cpeditor.org) #include <bits/stdc++.h> using namespace std; #define ll long long //# define int long long #define ull unsigned long long #define pii pair<int,int> #define baoliu(x, y) cout << fixed << setprecision(y) << x #define endl "\n" #define debug1(x) cerr<<x<<" " #define debug2(x) cerr<<x<<endl const int N = 3e5 + 10; const int M = 1e6 + 10; const int inf = 0x3f3f3f3f; const int mod = 998244353; const double eps = 1e-8; int n, m; int a[N]; int s[N]; int k; bool check(int mid){ for(int i=1;i+mid-1<=n;i++){ int r=i+mid-1; //if(mid==4) cerr<<i<<" "<<r<<" "<<s[r]-s[i-1]<<endl; if(mid-s[r]+s[i-1]<=k)return true; } return false; } void solve(){ cin>>n>>k; for(int i=1;i<=n;i++)cin>>a[i]; for(int i=1;i<=n;i++)s[i]=s[i-1]+a[i]; int l=0,r=n; while(l<r){ //cerr<<l<<" "<<r<<endl; int mid=(l+r+1)>>1; if(check(mid))l=mid; else r=mid-1; } cout<<l<<endl; for(int i=1;i+l-1<=n;i++){ int r=i+l-1; if(l-s[r]+s[i-1]<=k){ for(int o=i;o<=r;o++)a[o]=1; for(int j=1;j<=n;j++)cout<<a[j]<<" "; return ; } } } int main() { cin.tie(0); ios::sync_with_stdio(false); int t; //cin>>t; t=1; while (t--) { solve(); } return 0; }
C题是个不错的题,本题需要动态建树在线回答直径,似乎只能用倍增,不能其他方法,所以还是要全能。
考察直径的性质,从一次改变入手
前置
思路
定理:
边权非负时,树的直径求法:任选一个点 a a a ,找到离它最远的点 b b b ,再找到离 b b b 最远的点 c c c ,则 b ∼ c b \sim c b ∼ c 是一条直径。
树上任意两点 a , b a, b a , b 间距离 d i s t ( a , b ) = d e p t h ( a ) + d e p t h ( b ) − 2 × d e p t h ( l c a ( a , b ) ) dist(a, b) = depth(a) + depth(b) - 2 \times depth(lca(a, b)) d i s t ( a , b ) = d e p t h ( a ) + d e p t h ( b ) − 2 × d e p t h ( l c a ( a , b ) ) ,其中 d e p t h ( x ) depth(x) d e p t h ( x ) 是 x x x 到根的距离,l c a ( a , b ) lca(a, b) l c a ( a , b ) 是 a , b a, b a , b 两点的最近公共祖先。
性质:
若上一次树的直径是 A ∼ B A \sim B A ∼ B ,现在要在 u u u 下增加两个子节点 x , y x, y x , y ,若直径变大,则新直径的一个端点必然是 x x x 或 y y y 。x , y x, y x , y 等价,下面仅考虑 x x x 。
若 d i s t ( A , B ) < m a x ( d i s t ( A , x ) , d i s t ( B , x ) ) dist(A, B) < max(dist(A, x), dist(B, x)) d i s t ( A , B ) < m a x ( d i s t ( A , x ) , d i s t ( B , x ) ) ,由于未添加节点前 u u u 到任何节点的距离小于等于 d i s t ( A , B ) dist(A, B) d i s t ( A , B ) ,因此 x x x 到任何节点的距离最多比 u u u 到任何节点的距离多 1 1 1 ,因此 d i s t ( A , x ) ≤ d i s t ( A , B ) + 1 dist(A, x) \le dist(A, B) + 1 d i s t ( A , x ) ≤ d i s t ( A , B ) + 1 。不妨设此时 d i s t ( A , x ) > d i s t ( A , B ) dist(A, x) > dist(A, B) d i s t ( A , x ) > d i s t ( A , B ) ,则 d i s t ( A , x ) = d i s t ( A , B ) + 1 dist(A, x) = dist(A, B) + 1 d i s t ( A , x ) = d i s t ( A , B ) + 1 。由于 x x x 到任何节点的距离不可能超过 d i s t ( A , B ) + 1 dist(A, B) + 1 d i s t ( A , B ) + 1 ,所以此刻 A ∼ x A \sim x A ∼ x 是一条新直径。同理,若 d i s t ( B , x ) > d i s t ( A , B ) dist(B, x) > dist(A, B) d i s t ( B , x ) > d i s t ( A , B ) ,B ∼ x B \sim x B ∼ x 是一条新直径。
若 d i s t ( A , B ) ≥ m a x ( d i s t ( A , x ) , d i s t ( B , x ) ) dist(A, B) \ge max(dist(A, x), dist(B, x)) d i s t ( A , B ) ≥ m a x ( d i s t ( A , x ) , d i s t ( B , x ) ) ,则距离 A A A 最远的点是 B B B ,且距离 B B B 最远的点是 A A A ,因此 A ∼ B A \sim B A ∼ B 仍是直径。
时间复杂度:m l o g ( n ) mlog(n) m l o g ( n ) ,其中 m m m 为询问次数,n n n 为最大节点数。
debug:嵌套数组下标的中括号混乱,表示不仔细看根本看不出来
debug:数组开的大小应该是len+1,数组开小了,越界出发未定义行为
https://www.acwing.com/solution/content/236111/
参考题解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 // Problem: 树的直径 // Contest: AcWing // URL: https://www.acwing.com/problem/content/description/5563/ // Memory Limit: 256 MB // Time Limit: 2000 ms // // Powered by CP Editor (https://cpeditor.org) #include <bits/stdc++.h> using namespace std; #define ll long long //# define int long long #define ull unsigned long long #define pii pair<int,int> #define baoliu(x, y) cout << fixed << setprecision(y) << x #define endl "\n" #define debug1(x) cerr<<x<<" " #define debug2(x) cerr<<x<<endl const int N = 1e6 + 10; const int M = 1e6 + 10; const int inf = 0x3f3f3f3f; const int mod = 998244353; const double eps = 1e-8; int n, m; int a[N]; int len=__lg(N); int fa[N][20];//绷数组越界出发ub int dep[N]; //掌握直径的性质:两次dfs在正权树上求直径 //这道题对于每次新加进来的点都需要重新维护倍增信息, // 首先是在线的,无法使用targan // 树跑需要提前预处理dfs序,而动态建树会改变重儿子,所以无法预处理两次dfs int lca(int x,int y){ //cerr<<"start"<<endl; //cerr<<x<<" "<<y<<endl; if(dep[x]<dep[y])swap(x,y); for(int i=len;i>=0;i--){ //不要嵌套数组写,还是多创建变量 if(dep[fa[x][i]]>=dep[y]){ x=fa[x][i]; //cerr<<"y "<<y<<" "<<dep[y]<<endl; //cerr<<"iter"<<" "<<i<<" "<<x<<" "<<dep[x]<<endl; } } //cerr<<"samehigh"<<" "<<x<<" "<<y<<endl; if(x==y)return x; for(int i=len;i>=0;i--){ if(fa[x][i]!=fa[y][i]){ //cerr<<x<<" "<<y<<endl; x=fa[x][i];y=fa[y][i]; } } // cerr<<x<<" "<<y<<endl; // cerr<<fa[x][0]<<endl; // cerr<<"end"<<endl; return fa[x][0]; } int dis(int x,int y){ int mid=lca(x,y); //cerr<<x<<" "<<y<<" "<<mid<<endl; return dep[x]+dep[y]-2*dep[mid]; //cerr<<x<<" "<<y<<" <<mid<<endl; } void solve(){ //cout<<"len"<<" "<<len<<endl; cin>>m; n=4; dep[1]=1; for(int i=2;i<=4;i++){ fa[i][0]=1; dep[i]=2; } int res1=2;int res2=3; //动态维护树的直径两个端点 int ans=2; while(m--){ int u;cin>>u; int v1=++n; int v2=++n; //动态维护新加的点的求lca所需信息 dep[v1]=dep[u]+1; dep[v2]=dep[u]+1; fa[v1][0]=fa[v2][0]=u; for(int i=1;i<=len;i++){ fa[v1][i]=fa[fa[v1][i-1]][i-1]; fa[v2][i]=fa[fa[v2][i-1]][i-1]; } //cerr<<"fa[5][1]"<<" "<<fa[5][1]<<endl; //cerr<<"fa[7][1]"<<" "<<fa[7][1]<<endl; int dis1=dis(v1,res1); int dis2=dis(v1,res2); if(dis1>=dis2&&dis1>=ans){ ans=dis1; res2=v1; } else if(dis2>=dis1&&dis2>=ans) {ans=dis2; res1=v1; } cout<<ans<<endl; } //for(int i=1;i<=n;i++)cerr<<i<<" "<<dep[i]<<" "<<endl; } int main() { cin.tie(0); ios::sync_with_stdio(false); int t; //cin>>t; t=1; while (t--) { solve(); } return 0; }