2021牛客多校训练营4

C LCS

最长公共子序列,神奇的构造,先把他们的公共部分求出来,也就是最小值,然后在每个前面加上这么多个a,然后再依次看a,b,c,依次加入不同的字母,最后如果长度超过了n,肯定不存在。

#include<bits/stdc++.h>

using namespace std;

int a,b,c,n;

int main()
{
    string s1,s2,s3;
    s1 = "", s2 = "",s3 = "";
    cin >> a >> b >> c >> n;
    int t = min({a,b,c});
    for(int i = 0;i < t;i ++ ) s1 += 'a', s2 += 'a', s3 += 'a';
    a -= t,b -= t,c -= t;
    for(int i = 0;i < a;i ++ ) s1 += 'b', s2 += 'b';
    for(int i = 0;i < b;i ++ ) s2 += 'c', s3 += 'c';
    for(int i = 0;i < c;i ++ ) s1 += 'd', s3 += 'd';
    if(s1.size() > n || s2.size() > n || s3.size() > n)
    {
        puts("NO");
        return 0;
    }
    while(s1.size() < n) s1 += 'x';
    while(s2.size() < n) s2 += 'y';
    while(s3.size() < n) s3 += 'z';
    cout << s1 << endl;
    cout << s2 << endl;
    cout << s3 << endl;
    return 0;
}


E Tree Xor

这题能大概懂,但是代码还是不会写,01字典树也会,但是线段树好久没用了,只会用来求普通的区间修改和查询,稍微复杂点的就不知道怎么用了,其实是不知道正面维护信息,现附上一个我大概能看懂的代码,后面再学线段树的时候再回来看看。

#include<bits/stdc++.h>
#define N 100005
#define il inline
#define ll long long
#define debug puts("fuck");
using namespace std;
inline int read()
{
	int x=0;char ch=getchar();
	while(ch<'0'||ch>'9') ch=getchar();
	while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return x;
}
int n,root,tot,q,cnt;
int L[N],R[N];
int tl[N*30*2],tr[N*30*2],sum[N*30*2];
int head[N],nex[N<<1],to[N<<1],val[N<<1];
const int lim=(1<<30)-1;
void add(int &rt,int l,int r,int ql,int qr,int d)
{
	if(!rt) rt=++tot;
	if(r-l+1==sum[rt]) return;
	if(ql<=l&&r<=qr){sum[rt]=r-l+1;return;}
	int mid=(l+r)>>1;
	if(ql<=mid)
	{
		if(q&(1<<d)) add(tr[rt],l,mid,ql,qr,d-1);
		else add(tl[rt],l,mid,ql,qr,d-1);
	}
	if(qr>mid)
	{
		if(q&(1<<d)) add(tl[rt],mid+1,r,ql,qr,d-1);
		else add(tr[rt],mid+1,r,ql,qr,d-1);
	}
	sum[rt]=sum[tl[rt]]+sum[tr[rt]];
}
void dfs(int u,int f,int k)
{
	q=k;
	if(L[u]-1>=0) add(root,0,lim,0,L[u]-1,29);
	if(R[u]+1<=lim) add(root,0,lim,R[u]+1,lim,29);
	for(int i=head[u];i;i=nex[i])
		if(to[i]!=f) dfs(to[i],u,k^val[i]);
}
int main()
{
	n=read();int u,v,w;
	for(int i=1;i<=n;++i) L[i]=read(),R[i]=read();
	for(int i=1;i<n;++i)
	{
		u=read(),v=read(),w=read();
		++cnt;to[cnt]=v;val[cnt]=w;nex[cnt]=head[u];head[u]=cnt;
		++cnt;to[cnt]=u;val[cnt]=w;nex[cnt]=head[v];head[v]=cnt;
	}
	dfs(1,0,0);
	cout<<lim+1-sum[root]<<endl;
	return 0;
}

F Just a joke

签到题。两种操作,删除一条边,或者删除一个连通块,会使得边数-1或者-(k-1),
最后判断一下n+m的奇偶就行了。

#include<bits/stdc++.h>

using namespace std;

int n,m;

int main()
{
    cin >> n >> m;
    for(int i = 0;i < m;i ++ )
    {
        int a,b;
        cin >> a >> b;
    }
    if((n + m) % 2 == 0) puts("Bob");
    else puts("Alice");
}

I Inverse Pair

这是一个关于逆序对的题。首先可以用树状数组求出当前序列的逆序对,然后再来分析可以减少的逆序对的数量。对于当前这个数,如果这个数前面出现了比他大1的数,我们将这个数加1,就会将这个数逆序数减1,但是如果不符合这种情况,就没必要+1,所以可以再用依次树状数组,就判断当前数+1是否已经存在过了。

#include<bits/stdc++.h>

using namespace std;

typedef long long ll;

const int N = 1e6;
int a[N];
int n;
int tr[4 * N];

int lowbit(int x)
{
    return x & (-x);
}

void add(int x,int c)
{
    for(int i = x;i <= n;i += lowbit(i)) tr[i] += c;
}

ll sum(int x)
{
    ll ans = 0;
    for(int i = x;i > 0;i -= lowbit(i)) ans += tr[i];
    return ans;
}

int main()
{
    scanf("%d",&n);
    for(int i = 1;i <= n;i ++ )
    {
        scanf("%d",&a[i]);
    }
    ll res = 0;
    for(int i = 1;i <= n;i ++ )
    {
        add(a[i],1);
        res += (ll)sum(n) - sum(a[i]);
    }
    memset(tr,0,sizeof tr);
    ll s = 0;
    for(int i = 1;i <= n;i ++ )
    {
        if(i != 1)
        {
            if((sum(a[i] + 1) - sum(a[i])) > 0)
            {
                add(a[i] + 1,1);
                s ++;
                //cout << sum(a[i] + 1) - sum(a[i]) << endl;
            }
            else
            {
                add(a[i],1);
            }
        }
        if(i == 1) add(a[i],1);
        //cout << s << endl;
    }
    //cout << res << endl;
    printf("%lld\n",res - s);
}

J Average

之前看这么多人做出来!!!然后想了一下列个式子,就可以知道直接就是求A,B的最大平均值之和,然后又不知道最大平均值怎么求了。后面搜到了,二分答案,用当前数减去平均值再求前缀和,判断前缀和是否大于零就可以得到当前这个平均值是大了还是小了,如果大于零,说明还有进步的空间,mid小了,l=mid,else 人= mid。

#include<bits/stdc++.h>

using namespace std;

const int N = 1e5 + 10;

double sum[N];

double solve(double a[],int n,int x)
{
    for(int i = 0;i <= n;i ++ ) sum[i] = 0;
    double l = -1e6, r = 1e6;
    while((r - l) > 1e-7)
    {
        double mid = (l + r) / 2;
        sum[0] = 0;
        for(int i = 1;i <= n;i ++ ) sum[i] = sum[i - 1] + a[i] - mid;
        double MIN = 1e10, MAX = -1e10;
        for(int i = x;i <= n;i ++ )
        {
            MIN = min(MIN,sum[i - x]);
            MAX = max(MAX,sum[i] - MIN);
        }
        if(MAX >= 0) l = mid;
        else r = mid;
    }
    return l;
}



int main()
{
    double a[N],b[N];
    int n,m,x,y;
    scanf("%d %d %d %d",&n,&m,&x,&y);
    for(int i = 1;i <= n;i ++ ) scanf("%lf",&a[i]);
    for(int i = 1;i <= m;i ++ ) scanf("%lf",&b[i]);
    double res = 0.0;
    res = solve(a,n,x) + solve(b,m,y);
    printf("%.9lf\n",res);
    return 0;
}

补充 P1419 寻找段落 洛谷

这题是J的补充,J是规定长度至少为L,所以我们每次只需要减去0 - i - L的最小值就行了。但是这道题还规定了长度不能超过T,也就是 只能找I-S 到i-T,的最小值,就可以用单调队列维护,可以看一下acwing的滑动窗口https://www.acwing.com/problem/content/156/

#include<bits/stdc++.h>

using namespace std;

const int N = 1e5 + 10;

int n;
int a[N];
double sum[N];
int q[N];
int S,T;


bool check(double x)
{
    sum[0] = 0;
    for(int i = 1;i <= n;i ++ ) sum[i] = sum[i - 1] + (double)a[i] - x;
    int hh = 0, tt = -1;
    for(int i = S;i <= n;i ++ )
    {
        if(hh <= tt && (i - T) > q[hh]) hh ++;
        while(hh <= tt && sum[q[tt]] >= sum[i - S]) tt --;
        q[++ tt] = i - S;
        if(sum[i] - sum[q[hh]] >= 0) return true;
    }
    return false;
}



int main()
{
    scanf("%d",&n);
    scanf("%d %d",&S,&T);
    for(int i = 1;i <= n;i ++ ) scanf("%d",&a[i]);
    double l = -1e4, r = 1e4;
    while(r - l > 1e-4)
    {
        double mid = (l + r) / 2;
        if(check(mid)) l = mid;
        else r = mid;
    }
    printf("%.3lf\n",l);
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值