暑假算法康复训练day1

cf#804(div2)

A. The Third Three Number Problem

题意:

给定一个非负整数n,求是否存在三个整数使得

(ab)+(bc)+(ac)=n,并输出a,b,c

题解:

先讨论a,b,c转换为二进制后的最后一位

  • 000时,和为0
  • 001时,和为10
  • 001时,和为10
  • 111时,和为0

所以n只能是偶数

然后假设a=b,那么a⊕b=0,然后a⊕c=n/2,再假设a=b=0,那么c=n/2

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int T;
    cin>>T;
    while(T--)
    {
        int n;
        cin>>n;
        if (n%2==1) cout<<-1<<endl;
        else cout<<"0 0 "<<n/2<<endl;
    }
    return 0;
}

B. Almost Ternary Matrix

题意:

给定两个偶数 n , m ,求 n × m的二进制矩阵,满足对于矩阵内任意位置 ( i , j ),在上下左右的四个方向上均有恰好两个与该元素不同的元素。

题解:

从样例找规律

两行的时候:

10011001

01100110

四行的时候

10011001

01100110

01100110

10011001

八行的就不写了,太长了

不难看出答案是由下面这个拼接起来的

1001

0110

0110

1001

所以只需提前将这个矩阵保存起来,然后就将它按大小循环输出即可

#include<bits/stdc++.h>
using namespace std;
int a[4][4]={{1,0,0,1},
            {0,1,1,0},
            {0,1,1,0},
            {1,0,0,1}};
int main()
{
    int T;
    cin>>T;
    while(T--)
    {
        int n,m;
        cin>>n>>m;
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<m;j++)
                cout << a[i%4][j%4] << " ";
            cout<<endl;
        }

    }
    return 0;
}

C. The Third Problem

题意:

给定一个0到n-1的排列a,问有多少种0到n-1的排列b,满足排列a跟排列b相似

两个排列相似指两个排列的任意区间 [ l , r ] 上的子区间的MEX 值相同。 MEX值是指数组未出现的最小的非负整数。

题解:

我们考虑,如果一个区间[L,R]其mex([L,R])==x则说明0到x-1的数都在这个区间里出现过了。

所以我们需要从0到n-1枚举i,并且记录0到i-1出现的数的最左和最右的端点[L,R]

那么,如果枚举到的i的位置a[i]落在了区间[L,R]中,那么剩下的R-L+1-i是可以随便放的。

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
const int INF=0x3f3f3f3f;
const int mod=1e9+7;
typedef long long ll;
ll a[N];
int main()
{
    int T;
    cin>>T;
    while(T--)
    {
        ll n;
        cin>>n;
        for (ll i=1;i<=n;i++)
        {
            ll x;
            cin>>x;
            a[x]=i;
        }
        ll l=INF,r=-1;
        ll res=1;
        for (ll i=0;i<n;i++)
        {
            if (a[i]<=r && a[i]>=l)
            {
                res=res*(r-l+1-i)%mod;
            }
            l=min(l,a[i]);
            r=max(r,a[i]);
        }
        cout<<res<<endl;
    }
    return 0;
}

D. Almost Triple Deletions

题意:

给定一个长度为n的数组a,可以对数组进行操作,每次操作可以选择相邻的两个数,当且仅当两个数相同时可以删掉这两个数。求进行若干次操作后,剩下的数组长度最长是多少?

题解:

dp表示:

f[i]表示以第i位数结尾的剩下的长度中最长是多少。

当f[i]==f[j]时,需要解决的是中间的数能否删掉

如果能删掉,这个区间的长度必须是偶数,如果是奇数的话,一定会剩下一个的。并且我们有个结论,区间内的众数的次数不能超过总长度的一半。符合条件就能删掉。

第i个数的前面所有数都删掉或者n个数全部删掉也要做特殊处理。因为a[i]的范围是在1到n,所以只需设a[0]和a[n+1]为0即可。

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
const int INF=0x3f3f3f3f;
const int mod=1e9+7;
typedef long long ll;
ll a[N],f[N],b[N];
int main()
{
    int T;
    cin>>T;
    while(T--)
    {
        int n;
        cin>>n;
        for (int i=1;i<=n;i++) 
        {
            cin>>a[i];
            f[i]=0;
        }
        a[0]=0;
        a[n+1]=0;
        ll res=0;
        for (int i=1;i<=n+1;i++)
        {
            ll v=-INF;
            ll cnt=0;
            for (int j=0;j<=n;j++) b[j]=0;
            for (int j=i-1;j>=0;j--)
            {
                if (j+1<i)
                {
                    b[a[j+1]]++;
                    cnt=max(cnt,b[a[j+1]]);
                }
                if (cnt*2<=i-j-1 && (a[i]==a[j] || !a[i] || !a[j]) && (i^j)&1) v=max(v,f[j]+1);
            }
            f[i]=v;
        }
        cout<<f[n+1]-1<<endl;
    }
    return 0;
}

cf#802(div2)

A. Optimal Path

题意:

给定一个n*m的矩阵,a[i][j]=(i-1)*m+j,每次只能往右或者下移动,问从左上角到右下角,经过的位置的和的最小值

题解:

走第一行,最后一列

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
    int T;
    cin>>T;
    while(T--)
    {
        ll n,m;
        cin>>n>>m;
        ll res=0;
        for (ll i=1;i<=m;i++) res+=i;
        for (ll i=2;i<=n;i++) res+=i*m;
        cout<<res<<endl;
    }
    return 0;
}

B. Palindromic Numbers

题意:

给定一个长度为 n 的长整数, 求出一个长度为 n (没有前导零), 且和它相加为回文串的整数.

题解:

  1. 保证要求的数长度为n
  2. 回文串可以每一位都一样
  3. 当第一位数小于9时,可以令回文串为n个9。
  4. 当第一位数大于等于9时,根据第一条,只能进位,可以令回文串为n+1个1
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n;
string sub1(string s,int x)
{
    string res="";
    int t=0;
    for (int i=n-1;i>=0;i--)
    {
        int a=x-t,b=s[i]-'0';
        t=0;
        if (a<b)
        {
            t=1;
            a+=10;
        }
        a-=b;
        res=char(a+'0')+res;
    }
    return res;
}
int main()
{
    int T;
    cin>>T;
    while(T--)
    {
        cin>>n;
        string s;
        cin>>s;
        if (s[0]=='9') cout<<sub1(s,1)<<endl;
        else cout<<sub1(s,9)<<endl;
    }
    return 0;
}

C. Helping the Nature

题意:

给定一个数组a,有三种操作:

  1. 使a[1到i]-=1
  2. 使a[i到n]-=1
  3. 使a[1到n]+=1

求使所有数变为 0 的最小操作数.

题解:

看到区间的加和减,用差分

设b为a的差分数组

让a[]全变为0等价于让b[]全变为0

只有操作 1 和操作 2能让b[2, n] 负数变 0 和正数变 0.

先利用操作1和2把b[2, n]全变0,再将b[1]变为0即可

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e6+5;
ll a[N],b[N];
int main()
{
    int T;
    cin>>T;
    while(T--)
    {
        int n;
        cin>>n;
        ll res=0;
        for (int i=1;i<=n;i++) 
        {
            cin>>a[i];
            b[i]=a[i]-a[i-1];
        }
        for (int i=2;i<=n;i++)
        {
            if (b[i]>0) res+=b[i];
            else res-=b[i],b[1]+=b[i];
        }
        res+=abs(b[1]);
        cout<<res<<endl;
    }
    return 0;
}

D. River Locks

题意:

有 n 个水库, 当第i个水库满时, 第 i 个水库可以向第 i + 1 个水库倒水(瞬间完成), 最后一个水库会将多余的水瞬间传递到河.

每个水库的容量是 v[i], v为降序排列. 每个水库有一个管道连接. 管道打开时每秒传输 1 升水给水库.

有 q 次询问, 每次询问时所有管道关闭, 水库清空. 求最少需要同时打开多少个管道才可以在 t 时间内填满所有水库.若无法填满, 输出 -1.

题解:

定义 pre[i] 为 v[i] 的前缀和.

为了填满第 i 个水库, 必须消耗 pre[i] / i 的时间

填满所有水库的最少时间为最大的pre[i] / i

如果小于最短事件直接输出-1

实际时间=pre[n]/res<=t,res就是要求的答案,用二分查找答案即可

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define IOS ios::sync_with_stdio(0), cin.tie(0),cout.tie(0)
#define endl "\n"
const int N=2e6+5;
double v[N],pre[N];
int main()
{
    IOS;
    int n;
    cin>>n;
    for (int i=1;i<=n;i++)
    {
        cin>>v[i];
        pre[i]=pre[i-1]+v[i];
    }
    double mint=0;
    for (int i=1;i<=n;i++) mint=max(mint,pre[i]/(1.0*i));
    int m;
    cin>>m;
    while(m--)
    {
        double t;
        cin>>t;
        if (t<mint)
        {
            cout<<-1<<endl;
            continue;
        }
        int l=1,r=n;
		while(l<r)
        {
			int mid=(l+r)/2;
			if(pre[n]/(mid*1.0)<=t) r=mid;
			else l=mid+1; 
		}
		cout<<r<<endl;
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值