【Educational Codeforces Round 58 (Rated for Div. 2)】A.B.C.D.E.F.G

前言


A. Minimum Integer

题意

给出q次询问,每次询问给出l,r,d,问不在 [ l , r ] [l,r] [l,r]内的d的最小倍数是多少。

做法

1: l > d − > d l>d ->d l>d>d
2: ( r d + 1 ) × d \left( \frac{r}{d}+1 \right) \times d (dr+1)×d
代码

#include<stdio.h>
typedef long long ll;
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        ll a,b,c;
        scanf("%lld%lld%lld",&a,&b,&c);
        if(a>c) printf("%lld\n",c);
        else printf("%lld\n",1LL*(b/c+1)*c);
    }
    return 0;
}


B. Accordion

题意

定义手风琴的样子为"[:"+k*"|"+":]"
k可以是任何自然数。
问给定字符串通过删除某些字符之后可以得到的最长手风琴的长度

做法

从左往右找到第一个"[“之后的”:",找不到[或者找不到:,返回-1
从右往左找到第一个"]“之前的”:",找不到[或者找不到:或者找到的:在之前找到的:左边,返回-1
两个:重合时也要返回-1.最后答案就是4+两个冒号中间的|的个数

代码

#include<stdio.h>
#include<string.h>
const int maxn = 1e6+5;
char str[maxn];
int main()
{
    scanf("%s",str);
    int len=strlen(str);
    if(len<4)
    {
        printf("-1\n");
        return 0;
    }
    int tt=0;
    int cnt=0;
    int pos1=-1,pos2=-1;
    for(int i=0;i<len;i++)
    {
        if(tt==0&&str[i]=='[')
        {
            tt++;
            cnt++;
        }
        else if(tt==1&&str[i]==':')
        {
            tt++;
            cnt++;
            pos1=i;
            break;
        }
    }
    if(pos1==-1)
    {
        printf("-1\n");
        return 0;
    }
    tt=0;
    for(int i=len-1;i>=0;i--)
    {
        if(tt==0&&str[i]==']')
        {
            tt++;
            cnt++;
        }
        else if(tt==1&&str[i]==':')
        {
            tt++;
            cnt++;
            pos2=i;
            break;
        }
    }
    if(tt==0||pos1>=pos2)
    {
        printf("-1\n");
        return 0;
    }
    int ans=4;
    for(int i=pos1+1;i<=pos2-1;i++)
    {
        if(str[i]=='|') ans++;
    }
    printf("%d\n",ans);
    return 0;
}


C. Division and Union

题意

给你n条线段,问能不能把线段分成两个集合,保证来自不同集合的线段没有交点。

做法

只要把第一段出现空隙的线段拿出来放到第一个集合
其余线段放到第二个集合就可以了。

代码

#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 1e5+5;
struct data
{
    int l,r;
    int id;
}L[maxn];
bool cmp(const data &a,const data &b)
{
    if(a.l==b.l) return a.r<b.r;
    return a.l<b.l;
}
int ans[maxn];
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n;
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d%d",&L[i].l,&L[i].r);
            L[i].id=i;
            ans[i]=1;
        }
        sort(L+1,L+1+n,cmp);
        int maxx=L[1].r;
        int cnt=1;
        int flag=0;
        for(int i=2;i<=n;i++)
        {
            if(L[i].l>maxx)
            {
                flag=1;
                for(int j=i-1;j>=i-1-cnt+1;j--)
                {
                    ans[L[j].id]=2;
                }
                break;
            }
            else
            {
                cnt++;
                maxx=max(maxx,L[i].r);
            }
        }
        if(flag==0)
        {
            printf("-1\n");
            continue;
        }
        for(int i=1;i<=n;i++)
        {
            printf("%d",ans[i]);
            if(i==n) printf("\n");
            else printf(" ");
        }
    }
    return 0;
}


题意

给你一个n个点的带权树,第i个点的权值为a[i]求一条树上最长的路径,满足路径上所有点权的gcd不为1
1 &lt; = n &lt; = 2 ∗ 1 0 5 1&lt;=n&lt;=2*10^5 1<=n<=2105
1 &lt; = a i &lt; = 2 ∗ 1 0 5 1&lt;=a_i&lt;=2*10^5 1<=ai<=2105

做法

首先要想到,gcd是质因子就足够了,也就是说,肯定存在一条最长路径公约数是质数。
之后对于一个小于 2 ∗ 1 0 5 2*10^5 2105的数,他最多存在7个质因子
这里是因为, 2 ∗ 3 ∗ 5 ∗ 7 ∗ 11 ∗ 13 ∗ 17 = 510510 &gt; 2 ∗ 1 0 5 2*3*5*7*11*13*17=510510&gt;2*10^5 2357111317=510510>2105
所以对于树上的每个点,只需要求这些质因子最多能向下延伸的长度。
我们就dp[i][j]为以i为起点的向i的子树延伸的gcda[i]的第j个质因子的最长链的长度。
那么每次更新dp[i][j]一定是他某个儿子也含有这个质因子,我们想要更新dp[i][j]
就要知道这个质因子是他儿子的第几个质因子,
所以我们预处理的时候用map存储,map[pair<i,j>]=k表示ij中是第k个质因子。
存储质因子用vector存储,G[i][j]表示i的第j个质因子是哪个。
这样我们就可以得到更新dp[i][j]的转移方程
设当前访问的结点为rt,要转移的质因子为第j个,当前访问的儿子为to

	if(a[to]%G[rt][j]==0)
		int pos=mp[pii(G[rt][j],to)];//rt的第j个质因子是to的第pos个质因子
		dp[rt][j]=max(dp[rt][j],dp[to][pos]+1);

类似dp求树的直径,我们对所有儿子中的可更新父亲第j个因子的dp值取前两大
也就是以rt为根的子树内,gcda[rt]j个质因子的最长路径。
最后对每个子树内每个质因子能达到的最长路径取max就是答案。

代码

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<map>
#include<vector>
using namespace std;
typedef pair <int, int> pii;
const int maxn = 2e5+5;
int a[maxn];
int dp[maxn][10];
vector<int> G[maxn];
vector<int> zhuan[maxn];
vector<int> vec[maxn];
map<pii,int>  mp;
int ans=0;
void dfs(int rt,int fa)
{
    for(int i=0;i<G[rt].size();i++) dp[rt][i]=1;
    int maxx1[10],maxx2[10];
    for(int i=0;i<10;i++)
    {
        maxx1[i]=0;
        maxx2[i]=0;
    }
    for(int i=0;i<vec[rt].size();i++)
    {
        int to=vec[rt][i];
        if(to==fa) continue;
        dfs(to,rt);
        for(int j=0;j<G[rt].size();j++)
        {
            if(a[to]%G[rt][j]==0)
            {
                int pos=mp[pii(G[rt][j],to)];//rt的第j个质因子是to的第pos个质因子
                int tmp=dp[to][pos];
                if(maxx1[j]<tmp)
                {
                    maxx2[j]=maxx1[j];
                    maxx1[j]=tmp;
                }
                else if(maxx2[j]<tmp)
                {
                    maxx2[j]=tmp;
                }
            }
        }
    }
    for(int i=0;i<G[rt].size();i++)
    {
        dp[rt][i]+=maxx1[i];
        ans=max(ans,maxx1[i]+maxx2[i]+1);
    }
    return ;
}
int main()
{
    int n,u,v;
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    for(int i=1;i<=n;i++)
    {
        int k=a[i];
        for(int j=2;j*j<=k;j++)
        {
            if(k%j==0)
            {
                G[i].push_back(j);
                mp[pii(j,i)]=G[i].size()-1;
                while(k%j==0) k/=j;
            }
        }
        if(k>1)
        {
            G[i].push_back(k);
            mp[pii(k,i)]=G[i].size()-1;
        }
    }
    for(int i=1;i<=n-1;i++)
    {
        scanf("%d%d",&u,&v);
        vec[u].push_back(v);
        vec[v].push_back(u);
    }
    dfs(1,-1);
    printf("%d\n",ans);
    return 0;
}

E. Polycarp’s New Job

题意

一共有q次操作,每次操作有两种类型,
第一种类型+ x y,增加一个x*y的长方形
第二种类型? h w,当前所有长方形可以叠加,是否可以用一个h*w的长方形盖住之前左右长方形

做法

做法就是,维护一个最小需要的长方形的长宽。
长maxh是所有加进来的长方形的长的max
宽maxw是所有加进来的长方形的宽的max
之后对于每个要检查的长方形,只要maxh<=max(h,w)&&maxw<=min(h,w)就是YES否则是NO

代码

#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
    int n;
    scanf("%d",&n);
    int a[2];
    a[0]=0;
    a[1]=0;
    while(n--)
    {
        char op[2];
        int x[2];
        scanf("%s%d%d",op,&x[0],&x[1]);
        sort(x,x+2);
        sort(a,a+2);
        if(op[0]=='+')
        {
            a[0]=max(a[0],x[0]);
            a[1]=max(a[1],x[1]);
        }
        else
        {
            if(a[0]<=x[0]&&a[1]<=x[1])
            {
                puts("YES");
            }
            else
            {
                puts("NO");
            }
        }
    }
    return 0;
}


F. Ivan and Burgers

题意

有n个城市在x轴上,有m辆卡车,每辆卡车有四个属性,分别是起始城市s,终止城市f,每公里消耗燃料燃料消耗c,和可加油次数r。每次加油卡车油量加满,卡车的油量为V,所有卡车初始油量都是满的。求能让所有卡车从起点到达终点的最小油量V。

2 &lt; = n &lt; = 400 , 1 &lt; = m &lt; = 250000 2&lt;=n&lt;=400,1&lt;=m&lt;=250000 2<=n<=400,1<=m<=250000
1 &lt; = a i &lt; = 1 0 9 , a i &lt; a i + 1 1&lt;=a_i&lt;=10^9,a_i&lt;a_{i+1} 1<=ai<=109,ai<ai+1
做法

首先考虑暴力的 n 4 n^4 n4的做法。设 d p [ i ] [ j ] [ k ] dp[i][j][k] dp[i][j][k]为 从 i i i出发到达 j j j休息 k k k次的最小间隔。
d p [ i ] [ j ] [ k ] = min ⁡ j − 1 l = i + 1 ( max ⁡ ( d p [ i ] [ l ] [ k − 1 ] , a [ j ] − a [ l ] ) ) dp\left[ i \right] \left[ j \right] \left[ k \right] =\underset{l=i+1}{\overset{j-1}{\min}}\left( \max \left( dp\left[ i \right] \left[ l \right] \left[ k-1 \right] ,a\left[ j \right] -a\left[ l \right] \right) \right) dp[i][j][k]=l=i+1minj1(max(dp[i][l][k1],a[j]a[l]))
代码

#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn = 405;
int dp[maxn][maxn][maxn]; // dp[i][j][k] 表示从i出发到达j中间停留k次的最长间隔。
int a[maxn];
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    for(int i=1;i<=n;i++)
    {
        for(int j=i+1;j<=n;j++)
        {
            dp[i][j][0]=a[j]-a[i];
            for(int k=1;k<=n;k++)
            {
                dp[i][j][k]=a[j]-a[i];
                for(int l=i+1;l<j;l++)
                {
                    dp[i][j][k]=min(dp[i][j][k],max(dp[i][l][k-1],a[j]-a[l]));
                }
            }
        }
    }
    ll ans=0;
    for(int i=1;i<=m;i++)
    {
        int s,f,c,r;
        scanf("%d%d%d%d",&s,&f,&c,&r);
        ans=max(ans,1LL*dp[s][f][r]*c);
    }
    printf("%lld\n",ans);
    return 0;
}

首先我们可以滚动掉第一维,之后我们可以发现,
max ⁡ ( d p [ i ] [ l ] [ k − 1 ] , a [ j ] − a [ l ] ) \max \left( dp\left[ i \right] \left[ l \right] \left[ k-1 \right] ,a\left[ j \right] -a\left[ l \right] \right) max(dp[i][l][k1],a[j]a[l])
这个是满足决策单调性的,我们只需要一个单调队列维护,复杂度就变为 n 3 n^3 n3

代码

#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn = 405;
int dp[maxn][maxn]; // dp[i][j][k] 表示从i出发到达j中间停留k次的最长间隔。
int a[maxn];
int pos[maxn];
struct data
{
    int f,c,r;
    data(){}
    data(int ff,int cc,int rr)
    {
        f=ff;
        c=cc;
        r=rr;
    }
};
vector<data> G[maxn];
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    for(int i=1;i<=m;i++)
    {
        int s,f,c,r;
        scanf("%d%d%d%d",&s,&f,&c,&r);
        G[s].push_back(data(f,c,r));//离线处理每个起点为s的查询
    }
    ll ans=0;
    for(int i=1;i<=n;i++)
    {
        for(int j=i+1;j<=n;j++)
        {
            dp[j][0]=a[j]-a[i];
            int pos=0;
            for(int k=1;k<=n;k++)
            {
                dp[j][k]=a[j]-a[i];
                while(pos+1<=j&&dp[pos+1][k-1]<a[j]-a[pos+1]) pos++;//单调队列优化DP
                dp[j][k]=min(dp[j][k],min(a[j]-a[pos],dp[pos+1][k-1]));
            }
        }
        for(int j=0;j<G[i].size();j++)
        {
            int f=G[i][j].f;
            int c=G[i][j].c;
            int r=G[i][j].r;
            ans=max(ans,1LL*dp[f][r]*c);
        }
    }
    printf("%lld\n",ans);
    return 0;
}


G. (Zero XOR Subset)-less

题意

题意就是给你一个长度为n的序列每个数的大小为a[i]
要求把序列分为多个连续的段,保证分完之后,
无论选取那些段相异或答案都不是0,问最多可以分为多少段。

1 &lt; = n &lt; = 2 ∗ 1 0 5 1&lt;=n&lt;=2*10^5 1<=n<=2105
0 &lt; = a i &lt; = 1 0 9 0&lt;=a_i&lt;=10^9 0<=ai<=109
做法

首先由于答案要求是连续的段,而每一段的异或和是一个值,这个值刚好可以通过两个前缀异或和异或值得到,那么每一段的值就转换为前缀异或和,那么问题就变成,给你n个值,让你在其中选出尽量多的值,保证这些值任意组合异或和都不为0.这就变成线性基的经典问题。直接构造出n个前缀异或和的线性基,基底的个数便是答案。

代码

#include<stdio.h>
int p[65];
int main()
{
    int n,x,now=0;
    scanf("%d",&n);
    for(int i=0;i<n;i++)
    {
        scanf("%d",&x);
        now=now^x;
        x=now;
        for(int j=31;j>=0;j--)
        {
            if(x&(1<<j))
            {
                if(!p[j])
                {
                    p[j]=x;
                    break;
                }
                else x=x^p[j];
            }
        }
    }
    if(now==0)
    {
        printf("-1\n");
        return 0;
    }
    int ans=0;
    for(int i=31;i>=0;i--) if(p[i]) ans++;
    printf("%d\n",ans);
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值