目录
C:LCS(模拟)
E:Tree Xor(线段树区间合并)
I:Inverse Pair(逆序对,思维)
J:Average(浮点数二分)
C:LCS(模拟)
没什么好说的 模拟分情况讨论即可
#includetypedef long long ll; using namespace std; // const int N struct node { int x,id; string s; }a[4]; bool cmp1(node xx,node yy) { return xx.x >a[1].x>>a[2].x>>a[3].x>>n; a[1].id=1,a[2].id=2,a[3].id=3; sort(a+1,a+4,cmp1); a[3].s=""; a[2].s=""; a[1].s=""; for(int i=0;in) { cout<<"NO"; return 0; } while(a[i].s.size() E:Tree Xor(线段树区间合并)
题意:
你有一个一颗树节点数n(1≤n≤105),树上的边有一个边权w,点有一个点权ai,你要保证有直接父子关系的点之间au⊕av=w,并且树上每个点他的点权选取范围为[li,ri],现在请问你能构造出多少颗不同的树,有任何一ai不同就认为是一颗不同的树。
思路:
注意到一个非常重要的性质,如果有一个点权确定了,那么整棵树的点权都确定了。
我们假设=0,那么接下来所有点权都能求出来。
我们把异或一个值x,那么其他点也要异或这个值x。
显然x这个值的范围是.
那么问题就变成了所有的异或一个区间,有多少个值异或后都满足该范围内
我们知道某个数异或一个区间的值并不是一段连续的区间,而是多段连续的区间
比如3异或一个区间[4,15]后的值就变为这几段区间的值:[7,4],[11,8],[15,12]
问题就转换为了多个区间求并集,并集的大小就是答案。
然后就是这道题的难度,异或一个区间很难处理。
用线段树的区间性质考虑新建区间
一个数异或一个区间得到的多段区间巧合是线段树上的某个区间且是连续的。
这个性质非常重要是解决此题的关键。
(证明参考其他大佬博主,接下来,我们反着求,把不符合的区间标记起来,剩下的都是重复覆盖的区间,用所有区间数(1<<30)减去不符合区间的大小就是答案。
#includetypedef long long ll; // #define int long long using namespace std; const int N = 6e6 + 10; int h[N], ne[N], W[N], e[N], idx; int n; int l[N], r[N]; int sum[N], ch[N][2]; bool tag[N]; int root = 1, cnt = 1; void add(int a, int b, int c) { e[idx] = b, W[idx] = c, ne[idx] = h[a], h[a] = idx++; } void build(int &rt, int l, int r, int x, int y, int w) { int len = r - l + 1; int l1 = l ^ (w & (~(len - 1))); // ~(len-1)代表求出类11000000的格式,再将w前面的几位屏蔽掉,在和L异或,就是他们的起点了。 int r1 = l1 + len - 1; // l~r这个区间可以由l1~r1这个区间异或w得到 if (l1 >= x && r1 <= y) //说明这个区间符合 return; if (!rt) rt = ++cnt; if (l1 > y || r1 < x) //说明这个区间不符合 { tag[rt] = 1; sum[rt] = len; return; } int mid = l + r >> 1; build(ch[rt][0], l, mid, x, y, w); build(ch[rt][1], mid + 1, r, x, y, w); if (tag[rt]) sum[rt] = len; else sum[rt] = sum[ch[rt][0]] + sum[ch[rt][1]]; } void dfs(int u, int fa, int w) { build(root, 0, (1 << 30) - 1, l[u], r[u], w); for (int i = h[u]; ~i; i = ne[i]) { int v = e[i]; if (v == fa) continue; dfs(v, u, w ^ W[i]); } } signed main() { memset(h, -1, sizeof h); cin >> n; for (int i = 1; i <= n; i++) { cin >> l[i] >> r[i]; } for (int i = 0; i < n - 1; i++) { int a, b, c; cin>>a>>b>>c; add(a, b, c); add(b, a, c); } dfs(1, -1, 0); cout << (1 << 30) - sum[1]; } I:Inverse Pair(逆序对,思维)
求一下逆序对也没什么好说的。。。
#includetypedef long long ll; #define int long long using namespace std; const int N=2e5+10; int a[N],sum[N]; int ca[N]; int tmpA[N], cnt = 0; void merge_sort(int l, int r, int *A) { if (l >= r) return; int mid = (l + r) >> 1; merge_sort(l, mid, A); merge_sort(mid + 1, r, A); int pl = l, pr = mid + 1, tmpp = 0; while (pl <= mid && pr <= r) { if (A[pl] <= A[pr]) tmpA[tmpp++] = A[pl++]; else tmpA[tmpp++] = A[pr++], cnt += mid - pl + 1; } while (pl <= mid) tmpA[tmpp++] = A[pl++]; while (pr <= r) tmpA[tmpp++] = A[pr++]; for (int i = 0; i < tmpp; i++) A[i + l] = tmpA[i]; } signed main() { int n; scanf("%lld",&n); for(int i=1;i<=n;i++) { scanf("%lld",&a[i]); ca[i]=a[i]; } // 2 2 4 4 6 6 merge_sort(1, n, a); for(int i=1;i<=n;i++) { if(sum[ca[i]+1]) { cnt-=sum[ca[i]+1]; sum[ca[i]+1]++; } else { sum[ca[i]]++; } } cout< J:Average(浮点数二分)
题意:
你有一个的矩阵W,这个矩阵的,a,b序列给出。
你要再这个矩阵中挑选一个纵向长度大于等于x,横向长度大于等于y的子矩阵,求子矩阵的最大平均值。
思路:
我们选择n*m的子矩阵,那么它的平均数就是
化简为:
也就是序列a的连续n个数的最大平均数加上序列b的连续m个数的最大平均数。
考虑二分答案判断答案是否存在,用动态规划维护一下最小的前缀和即可。
#includetypedef long long ll; // #define int long long using namespace std; const int N = 1e5 + 10; const double eps=1e-6; int a[N], b[N]; double res[N]; bool check(int a[],int n,int x,double mid) { for(int i=1;i<=n;i++) { res[i]=res[i-1]+a[i]-mid; } double mn=0; double ans=-1e6; for(int i=x;i<=n;i++) { mn=min(mn,res[i-x]); ans=max(ans,res[i]-mn); } return ans>=0; } double slove(int a[],int n,int x) { double l=0,r=1e6; while(r-l>eps) { double mid=(l+r)/2; if(!check(a,n,x,mid)) r=mid; else l=mid; } return l; } signed main() { int n, m, x, y; scanf("%d%d%d%d", &n, &m, &x, &y); for (int i = 1; i <= n; i++) { scanf("%d", &a[i]); } for (int i = 1; i <= m; i++) { scanf("%d", &b[i]); } double ans1=slove(a,n,x); double ans2=slove(b,m,y); double ans=ans1+ans2; printf("%.10lf",ans); }



