Codeforces Educational Codeforces Round 42 (Rated for Div. 2) D,E,F

15 篇文章 0 订阅
3 篇文章 0 订阅

D

题意

每次把最小的两个给合起来,然后把右边的一个变成这个数的两倍,问最后的序列

分析

我的做法有点蠢,直接暴力合并,开一个权值线段树,叶子结点开个set存位置,然后维护同种元素最多出现多少个,一边找一边并就好
最后把所有的叶子结点给扫一遍,map一下

代码

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const ll inf = 1e18;
const ll N = 1234567;
inline ll read()
{
  ll p=0; ll f=1; char ch=getchar();
  while(ch<'0' || ch>'9'){if(ch=='-') f=-1; ch=getchar();}
  while(ch>='0' && ch<='9'){p=p*10+ch-'0'; ch=getchar();}
  return p*f;
}

ll rt,tot,lc[N<<2],rc[N<<2]; set<ll> s[N<<2]; ll c[N<<2];
void link(ll &u,ll L,ll R,ll k,ll cc)
{
  if(!u) u=++tot;
  if(L==R)
  {
    s[u].insert(cc);
    c[u] = s[u].size();
    return ;
  }
  ll mid = (L+R)>>1;
  if(k<=mid) link(lc[u],L,mid,k,cc);
  else link(rc[u],mid+1,R,k,cc);
  c[u] = max(c[lc[u]] , c[rc[u]]);
}

void find(ll u,ll L,ll R)
{
  if(L==R)
  {
    s[u].erase(s[u].begin());
    link(rt,1,inf,2*L,*s[u].begin());
    s[u].erase(s[u].begin());
    c[u] = s[u].size();
    //printf("%d\n",L);
    return ;
  }
  ll mid=(L+R)>>1;
  if(c[lc[u]] >= 2) find(lc[u],L,mid);
  else find(rc[u],mid+1,R);
  c[u] = max(c[lc[u]] , c[rc[u]]);
}


map<ll,ll> mp;

void qry(ll u,ll L,ll R)
{
  if(!u) return ;
  if(L==R)
  {
    for(auto i : s[u]) mp[i]=L;
    return ;
  }
  ll mid=(L+R)>>1;
  qry(lc[u],L,mid);
  qry(rc[u],mid+1,R);
}

ll ans[N];


int main()
{

  ll n = read(); rt=tot=0;
  for(ll i=1;i<=n;i++)
  {
    ll x=read();
    link(rt,1,inf,x,i);
  }

  while(1)
  {
    if(c[1] <= 1) break;
    find(rt,1,inf);
  }

  qry(rt,1,inf);

  printf("%d\n",mp.size());
  for(auto i:mp) printf("%lld ",i.second);

  return 0;
}

E

题意

在坐标轴上有很多个点,R,B,P,然后你要把这些点给连边,代价为坐标的绝对值的差, 然后使得这个图去掉R点后B和P都仍然全部联通或者去掉B点后R和P仍然全部都联通

分析

考虑P点之间的情况,要不就是P点之间连两条链,要不就是首先两个P点先连,然后对于R的点连向P点的情况就是分成两边,一边连前面的端点,一边连后面的端点,对于B也这样贪心,肯定有两个决策点,看看是连两条链优还是断开优

代码

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const ll N = 1000010;
inline ll read()
{
  ll p=0; ll f=1; char ch=getchar();
  while(ch<'0' || ch>'9'){if(ch=='-') f=-1; ch=getchar();}
  while(ch>='0' && ch<='9'){p=p*10+ch-'0'; ch=getchar();}
  return p*f;
}
ll col[N],a[N]; ll n; ll p[N],plen = 0;
int main()
{

  n=read();
  for(ll i=1;i<=n;i++)
  {
    ll x = read(); char ch; scanf("\n%c",&ch);
    if(ch=='R') col[i] = 1; else if(ch=='B') col[i] = 2; else col[i] = 3;
    a[i] = x;
  }

  ll s=0,d=n+1;
  for(ll i=1;i<=n;i++) if(col[i] == 3){s=i; break;}
  for(ll i=n;i>=1;i--) if(col[i] == 3){d=i; break;}
  if(s==0)
  {
    ll lst = 0; ll ans = 0;
    for(ll i=1;i<=n;i++)
    {
      if(col[i] == 2) continue;
      if(lst) ans+=a[i] - a[lst];
      lst = i;
    }
    lst = 0;
    for(ll i=1;i<=n;i++)
    {
      if(col[i] == 1) continue;
      if(lst) ans+=a[i] - a[lst];
      lst = i;
    }printf("%lld\n",ans);
    return 0;
  }

  ll lst = 0; ll ans = 0;
  for(ll i=1;i<=s;i++)
  {
      if(col[i] == 2) continue;
      if(lst) ans+=a[i] - a[lst];
      lst = i;
  }
  lst = 0;
  for(ll i=1;i<=s;i++)
  {
    if(col[i] == 1) continue;
    if(lst) ans+=a[i] - a[lst];
    lst = i;
  }

  lst = d;
  for(ll i=d+1;i<=n;i++)
  {
      if(col[i] == 2) continue;
      if(lst) ans+=a[i] - a[lst];
      lst = i;
  }

  lst = d;
  for(ll i=d+1;i<=n;i++)
  {
      if(col[i] == 1) continue;
      if(lst) ans+=a[i] - a[lst];
      lst = i;
  }

  ll nx = 0;
  for(ll i=s;i<d;i=nx)
  {
    ll ss = 0;
    nx = i+1; while(col[nx] !=3) nx++;

    plen = 0;
    for(ll j=i+1;j<nx;j++)
    {
      if(col[j] == 1) p[++plen] = j;
    }

    ll minx = LLONG_MAX;
    for(ll j=0;j<=plen;j++)
    {
      ll o = 0; if(j) o += a[p[j]] - a[i]; if(j+1<=plen) o += a[nx] - a[p[j+1]]; 
      minx = min(minx , o);
    }ss+=minx;

    plen = 0;
    for(ll j=i+1;j<nx;j++)
    {
      if(col[j] == 2) p[++plen] = j;
    }

    minx = LLONG_MAX;
    for(ll j=0;j<=plen;j++)
    {
      ll o = 0; if(j) o += a[p[j]] - a[i]; if(j+1<=plen) o += a[nx] - a[p[j+1]]; 
      minx = min(minx , o);
    }ss+=minx;

    ss += a[nx] - a[i];
    ss = min(ss , 2 * (a[nx] - a[i]));

    // printf("%lld\n",ss);
    ans += ss;
  }

  return printf("%lld\n",ans),0;
}

F

题意

问哪些边在简单环上

分析

简单环就是一个点联通分量,点数和边数相同,一条边找到dfs序比较大的点,也就是深度比较大的点,这条边属于的联通分量就是这条边属于的联通分量
(因为可能一个点属于多个联通分量)

代码

#include <bits/stdc++.h>
#define pb push_back
using namespace std;
const int N = 1234567;
inline int read()
{
  int p=0; int f=1; char ch=getchar();
  while(ch<'0' || ch>'9'){if(ch=='-') f=-1; ch=getchar();}
  while(ch>='0' && ch<='9'){p=p*10+ch-'0'; ch=getchar();}
  return p*f;
}
struct node{int x,y,next;}edge[N]; int len,first[N];
void ins(int x,int y){len++; edge[len].x=x; edge[len].y=y; edge[len].next=first[x]; first[x]=len;}
int dfn[N],low[N],id=0,cnt=0; stack<int> s; int n,m;
vector<int> scc[N]; int bel[N];
void dfs(int x)
{
  dfn[x] = low[x] = ++id; s.push(x);
  for(int k=first[x];k!=-1;k=edge[k].next)
  {
    int y = edge[k].y;
    if(dfn[y]==-1)
    {
      dfs(y); low[x] = min(low[x] , low[y]);
      if(low[y] >= dfn[x])
      {
        cnt++; scc[cnt].pb(x); int i;
        do
        {
          i = s.top();
          scc[cnt].pb(i); bel[i] = cnt; s.pop();
        }while(i!=y);
      }
    }
    else low[x] = min(low[x],dfn[y]);

  }
}

vector<int> siz[N];
vector<int>v;
int main()
{
  len = 1; memset(first,-1,sizeof(first));
  n = read(); m = read();
  for(int i=1;i<=m;i++)
  {
    int x = read(); int y = read();
    ins(x,y); ins(y,x);
  }
  memset(dfn,-1,sizeof(dfn));
  memset(low,-1,sizeof(low));
  //dfs(3);
  cnt = 0; for(int i=1;i<=n;i++) if(dfn[i] == -1){while(!s.empty()) s.pop();
    dfs(i);}

  for(int i=2;i<=len;i+=2)
  {
    int p = dfn[edge[i].x] > dfn[edge[i].y] ? edge[i].x : edge[i].y;
    siz[bel[p]].pb(i/2);
  }

  for(int i=1;i<=cnt;i++)
  {
    if(scc[i].size() == siz[i].size()) for(auto j:siz[i]) v.pb(j);
  }

  printf("%d\n",v.size()); sort(v.begin(),v.end());
  for(int i=0;i<v.size();i++) printf("%d%c",v[i]," \n"[i==v.size()-1]);

  return 0;
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值