Codeforces Round#620(Div.2)题解 by_Hile

A. Two Rabbits

题意:有两只兔子,甲兔在 x x x点,乙兔在 y y y点, x < y x<y x<y,甲兔每秒往右 a a a步,乙兔每秒往左 b b b步,判断两兔何时能在同一点相遇。

解析:判断 ( y − x ) % ( a + b ) (y-x)\%(a+b) (yx)%(a+b)是否为0,是输出 ( y − x ) / ( a + b ) (y-x)/(a+b) (yx)/(a+b),否输出 − 1 -1 1

代码

#include <bits/stdc++.h>
#define ll long long
using namespace std;
int T;
int main()
{
    ios::sync_with_stdio(false);
    cin>>T;
    while(T--)
    {
        ll x,y,a,b,d,k;
        cin>>x>>y>>a>>b;
        d=y-x,k=a+b;
        cout<<(d%k?-1:d/k)<<"\n";
    }
}

B. Longest Palindrome

题意:求出在 n n n个长度为 m m m的互不相同的字符串中挑选任意个字符串组合成的最长回文串。

解析:把所有字符串放进 s e t set set里,然后统计一个字符串 r e v e r s e reverse reverse后是否在 s e t set set里,有则加到答案里。注意要特判该字符串是否回文,然后把最长回文串加入答案字符串中间。

代码

#include <bits/stdc++.h>
#define ll long long
using namespace std;
string s[111];
set<string> se;
int n,m,sum,now;
int mx,id;//记录最长的回文串长度和下标
string ans[111];//答案字符串
int main()
{
    ios::sync_with_stdio(false);
    cin>>n>>m;
    for(int i=0;i<n;i++)cin>>s[i],se.insert(s[i]);
    for(int i=0;i<n;i++)
    {
        if(!se.count(s[i]))continue;
        string t=s[i];
        reverse(t.begin(),t.end());
        if(t==s[i]&&t.size()>mx)
        {
            mx=t.size();
            id=i;
        }
        else if(se.count(t))
        {
            sum+=s[i].size()*2;
            ans[now]=s[i];
            ans[n-now-1]=t;
            se.erase(t);
            now++;
        }
        se.erase(s[i]);
    }
    cout<<sum+mx<<"\n";
    if(sum+mx!=0)
    {
        for(int i=0;i<now;i++)cout<<ans[i];
        if(mx)cout<<s[id];
        for(int i=n-1-now+1;i<n;i++)cout<<ans[i];
    }
}

C. Air Conditioner

题意 n n n个客人,每个客人有 t i , l i , r i t_i,l_i,r_i ti,li,ri三种属性,表示客人在 t i t_i ti时来到,要求此时空调温度在 [ l i , r i ] [l_i,r_i] [li,ri]之内,空调温度初始( t = 0 t=0 t=0时)为 m m m,判断是否能满足所有客人的需求。

解析:直接按时间顺序更新温度可能到达的上下界,若不可能在 [ l i , r i ] [l_i,r_i] [li,ri]内就是NO。有一说一,这题应该相当于平时Div2B的难度,算是比较送分了。

代码

#include <bits/stdc++.h>
#define ll long long
using namespace std;
int T,n;
ll m;
struct node
{
    ll t,l,r;
    bool operator < (const node &s)
    {
        return t<s.t;
    }
}a[111];
int main()
{
    ios::sync_with_stdio(false);
    cin>>T;
    while(T--)
    {
        cin>>n>>m;
        ll l=m,r=m,t=0;
        int flag=0;
        for(int i=1;i<=n;i++)
            cin>>a[i].t>>a[i].l>>a[i].r;
        sort(a+1,a+1+n);//按时间排序
        for(int i=1;i<=n;i++)
        {
            ll d=a[i].t-t;//表示此时空调温度最大可以变化d
            if(r+d<a[i].l||l-d>a[i].r)flag=1;
            r=min(r+d,a[i].r);//更新边界
            l=max(l-d,a[i].l);//更新边界
            t=a[i].t;
        }
        cout<<(flag?"NO":"YES")<<"\n";
    }
}

D. Shortest and Longest LIS

题意:给定一个整数 n n n和一个由" < < <“和” > > >"组成的长度为 n − 1 n-1 n1的字符串 s s s a a a为一个 1 − n 1-n 1n的排列, s s s的第 i i i位若为 > > >,则有 a i > a i + 1 a_i>a_{i+1} ai>ai+1,若为 < < <,则有 a i < a i + 1 a_i<a_{i+1} ai<ai+1。要求构造出 L I S LIS LIS(最长上升子序列)最短and最长且符合字符串限制的 a a a

解析A:比赛时想到的树状数组+贪心乱搞方法。如果要使 L I S LIS LIS尽可能短,则要让位置在前面的数尽可能大,后面的数尽可能小。因此我们可以遍历一遍 s s s,遇到 > > >就要让 > > >前面所有数为 + 1 +1 +1然后把当前的数置为 1 1 1,遇到 < < <也要把之前的所有数 + 1 +1 +1,同时令当前数设置为前一个数 + 1 +1 +1,符合贪心策略,区间加就用树状数组维护。 L I S LIS LIS最长同理。

代码A

#include <bits/stdc++.h>
#define N 200010
#define ll long long
using namespace std;
int T,n;
string s;
int c[N],d[N];
int lowbit(int x){return x&(-x);}
void add(int l,int r,int x)//a[l:r]+=x;
{
    for(int i=l;i<=n;i+=lowbit(i))c[i]+=1ll*x,d[i]+=1ll*l*x;
    for(int i=r+1;i<=n;i+=lowbit(i))c[i]-=1ll*x,d[i]-=(r+1ll)*x;
}
int query(int l,int r)//sum of a[l]~a[r]
{
    int ans=0;
    for(int i=l-1;i;i-=lowbit(i))ans-=1ll*l*c[i]-d[i];
    for(int i=r;i;i-=lowbit(i))ans+=(r+1ll)*c[i]-d[i];
    return ans;
}
int main()
{
    ios::sync_with_stdio(false);
    cin>>T;
    while(T--)
    {
        cin>>n>>s;
        for(int i=1;i<=n;i++)c[i]=d[i]=0;
        add(1,1,1);
        int pre=0;
        for(int i=1;i<=s.size();i++)
        {
            if(s[i-1]=='>')
            {
                pre=i;
                add(1,i,1);
                add(i+1,i+1,1);
            }
            else
            {
                if(pre)add(1,pre,1);
                add(i+1,i+1,query(i,i)+1);
            }
        }
        for(int i=1;i<=n;i++)cout<<query(i,i)<<(i==n?"\n":" ");
        for(int i=1;i<=n;i++)c[i]=d[i]=0;
        add(1,1,1);
        pre=1;
        for(int i=1;i<=s.size();i++)
        {
            if(s[i-1]=='<')
            {
                add(i+1,i+1,i+1);
                pre=i+1;
            }
            else
            {
                add(pre,i,1);
                add(i+1,i+1,query(i,i)-1);
            }
        }
        for(int i=1;i<=n;i++)cout<<query(i,i)<<(i==n?"\n":" ");
    }
}

解析B:赛后听winterzz1大佬说的方法。直接记录相邻数字的相对大小,增减分别用 1 1 1 I N F INF INF(一个足够大的数)表示,离散化后就是答案(winterzz1tql!)。

代码B

#include <bits/stdc++.h>
#define N 200010
#define INF 0x3f3f3f3f
#define ll long long
using namespace std;
int n,m;
ll a[N],b[N],c[N];
string s;
void discret(ll *a,ll n)//将a离散化,O(nlogn)
{
    for(int i=1;i<=n;i++)c[i]=a[i];
    sort(c+1,c+1+n);
    for(int i=1;i<=n;i++)a[i]=lower_bound(c+1,c+1+n,a[i])-c;
}
int main()
{
    ios::sync_with_stdio(false);
    int T;
    cin>>T;
    while(T--)
    {
        cin>>n>>s;
        for(int i=2;i<=n;i++)
        {
            if(s[i-2]=='<')
            {
                a[i]=a[i-1]+1;
                b[i]=b[i-1]+INF;
            }
            else
            {
                a[i]=a[i-1]-INF;
                b[i]=b[i-1]-1;
            }
        }
        discret(a,n);
        discret(b,n);
        for(int i=1;i<=n;i++)cout<<a[i]<<(i==n?"\n":" ");
        for(int i=1;i<=n;i++)cout<<b[i]<<(i==n?"\n":" ");
    }
}

解析C:也是听大佬说的方法,但是自己没(bu)去(hui)实现。长度为 n n n的排列中, L I S LIS LIS最短的情况是 5 , 4 , 3 , 2 , 1 5,4,3,2,1 5,4,3,2,1,如果 s s s < < > < <<>< <<><,我们只需要翻转区间 [ 1 , 3 ] , [ 4 , 5 ] [1,3],[4,5] [1,3],[4,5],得到的答案为 3 , 4 , 5 , 1 , 2 3,4,5,1,2 3,4,5,1,2。也就是说可以通过翻转所有的 < < <区间即可得到答案,具体的翻转操作要用线段树实现(不会所以没写QAQ,下次一定学)。

E. 1-Trees and Queries

题意:给定一个 n n n n − 1 n-1 n1条边的树,有 q q q个询问,每个询问有 x , y , a , b , k x,y,a,b,k x,y,a,b,k五个数,表示询问在 x , y x,y x,y之间加一条边的话,是否存在 a a a b b b长度为 k k k的路(每个点和边都可以重复经过)。

解析:由于可以重复,在 a a a b b b之间长度小于 k k k的情况下就可以任选路径上的一个边反复横跳,也就是说只要满足 a a a b b b的长度和 k k k的奇偶性相同即可。多了一条 x x x y y y的边,之前 a , b a,b a,b之间的路径就多了两种可能: a − x − y − b a-x-y-b axyb a − y − x − b a-y-x-b ayxb,我们只需要判断三条路径是否有一条的长度和 k k k奇偶性相同,树上两点的距离等于 d [ u ] + d [ v ] − 2 ∗ d [ l c a ( u , v ) ] d[u]+d[v]-2*d[lca(u,v)] d[u]+d[v]2d[lca(u,v)] d d d为深度, l c a ( u , v ) lca(u,v) lca(u,v) u , v u,v u,v的最近公共祖先。

代码

#include <bits/stdc++.h>
#define N 200010
#define pb push_back
#define ll long long
#define VI vector<int>
using namespace std;
int n,m,q,fa[N][20],dep[N];
bool vis[N];
VI G[N];
void dfs(int u,int pre)
{
    dep[u]=dep[pre]+1;
    vis[u]=1;
    fa[u][0]=pre;
    for(int v:G[u])
        if(!vis[v])
            dfs(v,u);
}
void init(int n)//O(NlogN)
{
    for(int i=1;(1<<i)<=n;i++)
        for(int j=1;j<=n;j++)
            fa[j][i]=fa[fa[j][i-1]][i-1];
}
int lca(int u,int v)//O(logN)
{
    if(dep[u]<dep[v])swap(u,v);
    int d=dep[u]-dep[v];
    for(int i=0;(1<<i)<=d;i++)
        if(d&(1<<i))u=fa[u][i];
    if(u==v)return u;
    for(int i=17;i>=0;i--)
        if(fa[u][i]!=fa[v][i])u=fa[u][i],v=fa[v][i];
    return fa[u][0];
}
int dis(int u,int v)
{
    return dep[u]+dep[v]-2*dep[lca(u,v)];
}
int main()
{
    ios::sync_with_stdio(false);
    cin>>n;m=n-1;
    for(int i=1;i<=m;i++)
    {
        int u,v;
        cin>>u>>v;
        G[u].push_back(v);
        G[v].push_back(u);
    }
    dfs(1,1);
    init(n);
    cin>>q;
    while(q--)
    {
        int x,y,a,b,k,d[3],flag=1;
        cin>>x>>y>>a>>b>>k;
        d[0]=dis(a,b);
        d[1]=dis(a,x)+dis(b,y)+1;
        d[2]=dis(x,b)+dis(a,y)+1;
        for(int i=0;i<3;i++)
            if(d[i]<=k&&(k-d[i])%2==0)
                flag=0;
        cout<<(flag?"NO":"YES")<<"\n";
    }
}

F题待补。。。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值