2021牛客多校4(CEIJ)

目录

C:LCS(模拟)

E:Tree Xor(线段树区间合并)

I:Inverse Pair(逆序对,思维)

J:Average(浮点数二分)


C:LCS(模拟)

没什么好说的 模拟分情况讨论即可

#include <bits/stdc++.h>
typedef 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<yy.x;
}
bool cmp2(node xx,node yy)
{
    return xx.id<yy.id;
}
int main()
{
    int n;
    cin>>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;i<a[3].x;i++)
    {
        a[1].s.push_back('a');
        a[3].s.push_back('a');
    }
    for(int i=0;i<a[1].x;i++)
    {
        a[2].s.push_back('a');
    }
    for(int i=0;i<a[2].x-a[1].x;i++)
    {
        a[2].s.push_back('b');
        a[3].s.push_back('b');
    }
    for(int i=1;i<=3;i++)
    {
        if(a[i].s.size()>n)
        {
            cout<<"NO";
            return 0;
        }
        while(a[i].s.size()<n)
        {
            if(i==1)
            a[i].s.push_back('c');
            else if(i==2)
            a[i].s.push_back('d');
            else
            a[i].s.push_back('e');
        }
    }
    if(a[1].id==3&&a[3].id==1)//3 2 1
    {
        swap(a[2].s,a[3].s);
          for(int i=1;i<=3;i++)
        {
            cout<<a[i].s<<endl;
        }
        return 0;
    }
    if(a[2].id==3&&a[3].id==2)//1 3 2
    {
        swap(a[1].s,a[2].s);
        // swap(a[2].s,a[3].s);
           for(int i=1;i<=3;i++)
        {
            cout<<a[i].s<<endl;
        }
        return 0;
    }
    //2 1 3
    if(a[1].id==2&&a[2].id==1)
    {
        swap(a[1].s,a[3].s);
           for(int i=1;i<=3;i++)
        {
            cout<<a[i].s<<endl;
        }
        return 0;
    }
    //2 3 1
    if(a[1].id==3&&a[2].id==1)
    {
        swap(a[1].s,a[2].s);
        swap(a[2].s,a[3].s);
        for(int i=1;i<=3;i++)
        {
            cout<<a[i].s<<endl;
        }
        return 0;
    }
    //3 1 2
    if(a[1].id==2&&a[2].id==3)
    {
        swap(a[2].s,a[3].s);
        swap(a[1].s,a[2].s);
        for(int i=1;i<=3;i++)
        {
            cout<<a[i].s<<endl;
        }
        return 0;
    }
    //1 2 3
    for(int i=1;i<=3;i++)
        {
            cout<<a[i].s<<endl;
        }
        return 0;
}

E:Tree Xor(线段树区间合并)

题意:

你有一个一颗树节点数n(1≤n≤105),树上的边有一个边权w,点有一个点权ai​,你要保证有直接父子关系的点之间au​⊕av​=w,并且树上每个点他的点权选取范围为[li​,ri​],现在请问你能构造出多少颗不同的树,有任何一ai​不同就认为是一颗不同的树。

思路:

注意到一个非常重要的性质,如果有一个点权确定了,那么整棵树的点权都确定了。

我们假设a_{1}=0,那么接下来所有点权a_{i}都能求出来。  

我们把a_{1}异或一个值x,那么其他点a_{i}也要异或这个值x。

显然x这个值的范围是[l_{1},r_{1}].

那么问题就变成了所有的a_{i}异或一个区间[l_{1},r_{1}],有多少个值异或后都满足该范围[l_{i},r_{i}]内 

我们知道某个数异或一个区间的值并不是一段连续的区间,而是多段连续的区间

比如3异或一个区间[4,15]后的值就变为这几段区间的值:[7,4],[11,8],[15,12]

 问题就转换为了多个区间求并集,并集的大小就是答案。

然后就是这道题的难度,异或一个区间很难处理。

用线段树的区间性质考虑新建区间

一个数异或一个区间得到的多段区间巧合是线段树上的某个区间且是连续的。

这个性质非常重要是解决此题的关键。

(证明参考其他大佬博主,

 接下来,我们反着求,把不符合的区间标记起来,剩下的都是重复覆盖的区间,用所有区间数(1<<30)减去不符合区间的大小就是答案。

#include <bits/stdc++.h>
typedef 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(逆序对,思维)

求一下逆序对也没什么好说的。。。

#include <bits/stdc++.h>
typedef 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<<cnt;
}

J:Average(浮点数二分)

题意:

你有一个n*m的矩阵W,这个矩阵的W_{(i,j)}=a_{i}+b_{j},a,b序列给出。

你要再这个矩阵中挑选一个纵向长度大于等于x,横向长度大于等于y的子矩阵,求子矩阵的最大平均值。

思路:

我们选择n*m的子矩阵,那么它的平均数就是

avg=\frac{m*\sum_{i=1}^{n}a_{i}+n*\sum_{j=1}^{m}b_{j}}{n*m}

化简为:

avg=\frac{\sum_{i=1}^{n}a_{i}}{n}+\frac{\sum_{j=1}^{m}b_{j}}{m}

也就是序列a的连续n个数的最大平均数加上序列b的连续m个数的最大平均数。

考虑二分答案判断答案是否存在,用动态规划维护一下最小的前缀和即可。

#include <bits/stdc++.h>
typedef 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);
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值