A:读错题导致难度飙升。
这里题意是任意两个下标满足条件都能换,那就是个水题了
#include<bits/stdc++.h> using namespace std; typedef long long LL; typedef long double ld; typedef pair<LL,int> pii; const int N = 1e5+5; const int M = 1e6+5; const LL Mod = 998244353; #define rg register #define pi acos(-1) #define INF 1e8 #define INM INT_MIN #define dbg(ax) cout << "now this num is " << ax << endl; inline int read() { int x = 0,f = 1;char c = getchar(); while(c < '0' || c > '9'){if(c == '-') f = -1;c = getchar();} while(c >= '0' && c <= '9'){x = (x<<1)+(x<<3)+(c^48);c = getchar();} return x*f; } int a[55],vis[105]; int main() { int ca;ca = read(); while(ca--) { int n;n = read(); memset(vis,0,sizeof(vis)); for(int i = 1;i <= n;++i) { a[i] = read(); vis[a[i]]++; } sort(a+1,a+n+1); int f = 0; for(int i = 1;i < n;++i) { if(vis[a[i]+1] != 0) continue; if(vis[a[i]] > 1){vis[a[i]]--;continue;} f = 1; } if(f) printf("NO\n"); else printf("YES\n"); } // system("pause"); return 0; }
B:水题
#include<bits/stdc++.h> using namespace std; typedef long long LL; typedef long double ld; typedef pair<LL,int> pii; const int N = 1e3+5; const int M = 1e6+5; const LL Mod = 998244353; #define rg register #define pi acos(-1) #define INF 1e18 #define INM INT_MIN #define dbg(ax) cout << "now this num is " << ax << endl; inline LL read() { LL x = 0,f = 1;char c = getchar(); while(c < '0' || c > '9'){if(c == '-') f = -1;c = getchar();} while(c >= '0' && c <= '9'){x = (x<<1)+(x<<3)+(c^48);c = getchar();} return x*f; } LL a[55],b[55]; int main() { int ca;ca = read(); while(ca--) { int n;n = read(); LL mi1 = INF,mi2 = INF; for(int i = 1;i <= n;++i) a[i] = read(),mi1 = min(mi1,a[i]); for(int i = 1;i <= n;++i) b[i] = read(),mi2 = min(mi2,b[i]); LL ans = 0; for(int i = 1;i <= n;++i) { int dis = a[i]-mi1; ans += dis; int ma = b[i]-mi2; if(ma > dis) ans += ma-dis; // dbg(ans); } printf("%lld\n",ans); } // system("pause"); return 0; }
C:一开始想到二分,但发现不满足二分性质。
但是这也为解题提供了一个思路,可以发现组合的权值最多只有[1,100]的可能性,那么取枚举这个权值即可
#include<bits/stdc++.h> using namespace std; typedef long long LL; typedef long double ld; typedef pair<LL,int> pii; const int N = 1e3+5; const int M = 1e6+5; const LL Mod = 998244353; #define rg register #define pi acos(-1) #define INF 1e18 #define INM INT_MIN #define dbg(ax) cout << "now this num is " << ax << endl; inline int read() { int x = 0,f = 1;char c = getchar(); while(c < '0' || c > '9'){if(c == '-') f = -1;c = getchar();} while(c >= '0' && c <= '9'){x = (x<<1)+(x<<3)+(c^48);c = getchar();} return x*f; } int w[55],n,vis[105],num[105]; int check(int x) { for(int i = 0;i <= 100;++i) num[i] = vis[i]; int ans = 0; for(int i = 1;i <= n;++i) { if(w[i] > x) continue; if(x-w[i] == w[i]) { if(num[w[i]] > 1) num[w[i]] -= 2,ans++; } else if(num[x-w[i]] > 0 && num[w[i]] > 0) { num[x-w[i]]--; num[w[i]]--; ans++; } } return ans; } int main() { int ca;ca = read(); while(ca--) { n = read(); memset(vis,0,sizeof(vis)); for(int i = 1;i <= n;++i) w[i] = read(),vis[w[i]]++; int ans = 0; for(int i = 1;i <= 100;++i) ans = max(ans,check(i)); printf("%d\n",ans); } // system("pause"); return 0; }
D:感觉我想的稍微麻烦了。。能AC就是好题解(逃
可以发现对于每个1,我们要将后面的没有被接入的第一个0接入当前序列。(0也是同理)
这样显然是可以满足最小的子序列数的。
那么对于这个后面的最小位置,我们可以用线段树维护这个位置。
然后一个位置被接入,就去更新线段树。因为是单点更新,复杂度nlogn,也挺快了。
#include<bits/stdc++.h> using namespace std; typedef long long LL; typedef long double ld; typedef pair<LL,int> pii; const int N = 2e5+5; const int M = 1e6+5; const LL Mod = 998244353; #define rg register #define pi acos(-1) #define INF 1e8 #define INM INT_MIN #define dbg(ax) cout << "now this num is " << ax << endl; inline int read() { int x = 0,f = 1;char c = getchar(); while(c < '0' || c > '9'){if(c == '-') f = -1;c = getchar();} while(c >= '0' && c <= '9'){x = (x<<1)+(x<<3)+(c^48);c = getchar();} return x*f; } int ans[N];//0,1 char s[N]; struct Node{int L,r,mi;}node[2][N<<2]; void build(int L,int r,int idx,int id)//id-0,id-1 { node[id][idx].L = L,node[id][idx].r = r; if(L == r) { if(s[L] == '0' && id == 0 || s[L] == '1' && id == 1) node[id][idx].mi = L; else node[id][idx].mi = INF; return ; } int mid = (L+r)>>1; build(L,mid,idx<<1,id); build(mid+1,r,idx<<1|1,id); node[id][idx].mi = min(node[id][idx<<1].mi,node[id][idx<<1|1].mi); } void update(int x,int idx,int id) { if(node[id][idx].L == node[id][idx].r) { node[id][idx].mi = INF; return ; } int mid = (node[id][idx].L + node[id][idx].r)>>1; if(mid >= x) update(x,idx<<1,id); else update(x,idx<<1|1,id); node[id][idx].mi = min(node[id][idx<<1].mi,node[id][idx<<1|1].mi); } int query_mi(int L,int r,int idx,int id) { if(node[id][idx].L >= L && node[id][idx].r <= r) return node[id][idx].mi; int mid = (node[id][idx].L+node[id][idx].r)>>1,mi = INF; if(mid >= L) mi = min(mi,query_mi(L,r,idx<<1,id)); if(mid < r) mi = min(mi,query_mi(L,r,idx<<1|1,id)); return mi; } int main() { int ca;ca = read(); while(ca--) { int n;n = read(); scanf("%s",s+1); build(1,n,1,0); build(1,n,1,1); int ma = 0; memset(ans,0,sizeof(ans)); for(int i = 1;i <= n;++i) { if(ans[i] == 0) ans[i] = ++ma; int tmp; if(s[i] == '0') tmp = query_mi(i+1,n,1,1); else tmp = query_mi(i+1,n,1,0); if(tmp == INF) continue; ans[tmp] = ans[i]; if(s[tmp] == '0') update(tmp,1,0); else update(tmp,1,1); } printf("%d\n",ma); for(int i = 1;i <= n;++i) printf("%d%c",ans[i],i == n ? '\n' : ' '); } //system("pause"); return 0; }
E1:读错题导致难度飙升(梅开二度,老笨蛋了)
注意的是,这里是要使总(root-leaves)路径权值和小于s。
那么,我们可以去记录路径被经过的次数,然后乘上这个路径的w-w/2,这就是让它折半一次的能对总权值减少的贡献。
那么显然我们每次都选最大的,就能在最少的次数内满足。
这里这个被经过次数很显然就是子节点数。堆来维护最大值即可
#include<bits/stdc++.h> using namespace std; typedef long long LL; typedef long double ld; typedef pair<LL,int> pii; const int N = 1e5+5; const int M = 1e6+5; const LL Mod = 998244353; #define rg register #define pi acos(-1) #define INF 1e18 #define INM INT_MIN #define dbg(ax) cout << "now this num is " << ax << endl; inline LL read() { LL x = 0,f = 1;char c = getchar(); while(c < '0' || c > '9'){if(c == '-') f = -1;c = getchar();} while(c >= '0' && c <= '9'){x = (x<<1)+(x<<3)+(c^48);c = getchar();} return x*f; } struct Node{int to;LL dis;}; vector<Node> G[N]; int ssize[N]; LL sum = 0; struct Point{ LL cost,w,cnt; bool operator < (const Point &a)const{ return cost < a.cost; } }; priority_queue<Point> Q; LL cal(LL w,int cnt) { return 1LL*cnt*(w-w/2); } void dfs(int u,int fa,LL dis) { if(G[u].size() == 1) ssize[u] = 1; for(auto v : G[u]) { if(v.to == fa) continue; dfs(v.to,u,v.dis); ssize[u] += ssize[v.to]; } if(dis != 0) { //printf("dis is %lld u is %d ssize is %d\n",dis,u,ssize[u]); sum += 1LL*ssize[u]*dis; Q.push(Point{cal(dis,ssize[u]),dis,ssize[u]}); } } int main() { int ca;ca = read(); while(ca--) { int n;LL s; n = read(),s = read(); for(rg int i = 1;i <= n;++i) G[i].clear(),ssize[i] = 0; for(rg int i = 1;i < n;++i) { int u,v;LL w; u = read(),v = read(),w = read(); G[u].push_back(Node{v,w}); G[v].push_back(Node{u,w}); } while(!Q.empty()) Q.pop(); sum = 0; dfs(1,0,0); int ans = 0; while(!Q.empty() && sum > s) { ans++; Point q = Q.top(); Q.pop(); sum -= q.cost; q.w /= 2; q.cost = cal(q.w,q.cnt); Q.push(q); } printf("%d\n",ans); } // system("pause"); return 0; }
E2:最小化花费了,可以用二分来。
因为c只有1,2,那么我们可以去维护两个堆。
一个维护1,一个维护2。开两个前缀和数组
用数组1来维护花费代价为i时能减少的最大权值。
数组2来维护花费代价为2*i时能减少的最大权值。
我们枚举1的花费,然后去二分最小的2代价使得sum-两个前缀和数组和 <= s
memset初始化数组T了,其实没必要,只要让第0个为0即可(改了之后快的飞起~~芜湖)
#include<bits/stdc++.h> using namespace std; typedef long long LL; typedef long double ld; typedef pair<LL,int> pii; const int N = 1e5+5; const int M = 2e6+5; const LL Mod = 998244353; #define rg register #define pi acos(-1) #define INF 1e18 #define INM INT_MIN #define dbg(ax) cout << "now this num is " << ax << endl; inline LL read() { LL x = 0,f = 1;char c = getchar(); while(c < '0' || c > '9'){if(c == '-') f = -1;c = getchar();} while(c >= '0' && c <= '9'){x = (x<<1)+(x<<3)+(c^48);c = getchar();} return x*f; } struct Node{int to;LL dis,cost;}; vector<Node> G[N]; struct Point{ int cnt;LL w,cost; bool operator < (const Point &a)const{ return cost < a.cost; } }; LL cal(LL w,int cnt) { return 1LL*cnt*(w-w/2); } priority_queue<Point> Q1,Q2; int ssize[N]; LL sum = 0,pre1[M],pre2[M],s; void dfs(int u,int fa,LL dis,int c) { if(G[u].size() == 1) ssize[u] = 1; for(auto v : G[u]) { if(v.to == fa) continue; dfs(v.to,u,v.dis,v.cost); ssize[u] += ssize[v.to]; } if(dis != 0) { sum += 1LL*ssize[u]*dis; if(c == 1) Q1.push(Point{ssize[u],dis,cal(dis,ssize[u])}); else Q2.push(Point{ssize[u],dis,cal(dis,ssize[u])}); } } bool check(LL tmp,int x) { return (tmp-pre2[x]) <= s; } int main() { int ca;ca = read(); while(ca--) { int n; n = read(),s = read(); for(int i = 1;i <= n;++i) G[i].clear(),ssize[i] = 0; for(rg int i = 1;i < n;++i) { int u,v,w,c; u = read(),v = read(),w = read(),c = read(); G[u].push_back(Node{v,w,c}); G[v].push_back(Node{u,w,c}); } while(!Q1.empty()) Q1.pop(); while(!Q2.empty()) Q2.pop(); pre1[0] = pre2[0] = sum = 0; dfs(1,0,0,0); int cnt1 = 0,cnt2 = 0; while(!Q1.empty() && sum-pre1[cnt1] > s) { Point q = Q1.top();Q1.pop(); ++cnt1; pre1[cnt1] = pre1[cnt1-1]+q.cost; q.w /= 2; q.cost = cal(q.w,q.cnt); if(q.cost != 0) Q1.push(q); } while(!Q2.empty() && sum-pre2[cnt2] > s) { Point q = Q2.top();Q2.pop(); ++cnt2; pre2[cnt2] = pre2[cnt2-1]+q.cost; q.w /= 2; q.cost = cal(q.w,q.cnt); if(q.cost != 0) Q2.push(q); } LL ans = INF; for(int i = 0;i <= cnt1;++i) { LL ma = sum-pre1[i]; int L = 0,r = cnt2,ta = 1e8; while(L <= r) { int mid = (L+r)>>1; if(check(ma,mid)) { ta = mid; r = mid-1; } else L = mid+1; } ans = min(ans,1LL*i+ta*2); } printf("%lld\n",ans); } //system("pause"); return 0; }
F:区间dp的思想
我们可以先处理出每个区间内部能嵌套的最多区间数,然后就找到了这个区间的最大权值。
既然要去找内部的嵌套,那么显然满足内部区间的长度比该区间小,所以我们可以按长度排序。
然后dp,因为已经按长度排序了,所以dp时肯定内部的权值已经处理好了。
具体的dp:
如果内部有个区间,且这个区间前面没有可以和它连接的,那么直接dp转移就行。
如果有相连的区间,我们可以让dp时一直传递值。
那么由于这里很特殊的一个地方,如果两个连续的区间,他们相接的地方重合,那么这两个区间是无法相连的。
所以我们对于当前区间,从p[i.]L-1]来转移,就不会重叠了
我们把满足条件的内部区间放入,然后当前位置满足由区间为右端点,就从左端点转移。
数据过大,先离散化。
#include<bits/stdc++.h> using namespace std; typedef long long LL; typedef long double ld; typedef pair<LL,int> pii; const int N = 2e5+5; const int M = 2e6+5; const LL Mod = 998244353; #define rg register #define pi acos(-1) #define INF 1e18 #define INM INT_MIN #define dbg(ax) cout << "now this num is " << ax << endl; inline int read() { int x = 0,f = 1;char c = getchar(); while(c < '0' || c > '9'){if(c == '-') f = -1;c = getchar();} while(c >= '0' && c <= '9'){x = (x<<1)+(x<<3)+(c^48);c = getchar();} return x*f; } struct Node{int L,r,len,id;}p[N]; bool cmp(Node a,Node b){return a.len < b.len;} int a[N<<1],n,cnt; LL dp[N],f[N];//到i号 vector<Node> G[N]; void slove() { p[n+1] = {1,cnt+1,cnt+1,n+1}; sort(p+1,p+n+2,cmp); for(int i = 1;i <= n+1;++i) { for(int j = p[i].L-1;j <= p[i].r;++j) G[j].clear(),dp[j] = 0; for(int j = 1;j < i;++j) { if(p[j].L >= p[i].L && p[j].r <= p[i].r) G[p[j].r].push_back(p[j]); } for(int j = p[i].L;j <= p[i].r;++j) { dp[j] = dp[j-1];//先继承 for(auto t : G[j])//如果有线段的右边界为它 { dp[j] = max(dp[j],dp[t.L-1]+f[t.id]); } } f[p[i].id] = dp[p[i].r]+1; } printf("%lld\n",f[n+1]-1); } int main() { int ca;ca = read(); while(ca--) { memset(f,0,sizeof(f)); n = read(); cnt = 0; for(int i = 1;i <= n;++i) { p[i].L = read(),p[i].r = read(),p[i].id = i; a[++cnt] = p[i].L,a[++cnt] = p[i].r; } sort(a+1,a+cnt+1); cnt = unique(a+1,a+cnt+1)-a-1; for(int i = 1;i <= n;++i) { p[i].L = lower_bound(a+1,a+cnt+1,p[i].L)-a; p[i].r = lower_bound(a+1,a+cnt+1,p[i].r)-a; p[i].len = p[i].r-p[i].L; } slove(); } system("pause"); return 0; } /* 4 5 1 5 2 3 2 5 3 5 2 2 */