Codeforces Global Round 3

Codeforces Global Round 3

A. Another One Bites The Dust

有若干個a,有若干個b,有若干個ab。你如今要把這些串拼成一個串,使得任意兩個相鄰的位置都是不一樣字符,求可能的最長串長度。ios

枚舉一下\(a\)開頭仍是\(b\)開頭,那麼接下來就被惟一肯定了。this

#include<iostream>
#include<cstdio>
using namespace std;
int a,b,c;long long ans;
int main()
{
    scanf("%d%d%d",&a,&b,&c);
    ans=0ll+c*2+min(a,b+1)+min(a,b);
    if(b)--b,ans=max(ans,1ll+c*2+min(a,b+1)+min(a,b));
    printf("%lld\n",ans);
    return 0;
}

B. Born This Way

\(n\)個航班從\(A\)前往\(B\),起飛時間分別是\(a_1,a_2,...,a_n\),飛行時間都是\(t_a\)。有\(m\)個航班從\(B\)前往\(C\),起飛時間分別是\(b_1,b_2,...,b_m\),飛行時間是\(t_b\)。如今有一我的要從\(A\)\(C\),你能夠取消不超過\(k\)個航班,使得這我的到達\(C\)的時間最晚。
求出最晚時間,若是可讓這我的沒法到達,輸出\(-1\)spa

顯然這我的越早到達\(B\)越好,因此咱們取消掉的必定是\(a\)的一段前綴。而到達\(B\)以後必定會坐上最先出發的航班,因此同理刪去到達以後的一段連續的航班。
那麼枚舉一下在\(a\)刪掉的前綴長度就好了。
注意判斷一下\(n\le k,m\le k\)的狀況code

#include<iostream>
#include<cstdio>
using namespace std;
#define MAX 200200
inline int read()
{
    int x=0;bool t=false;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=true,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return t?-x:x;
}
int n,m,ta,tb,k,ans;
int a[MAX],b[MAX];
int main()
{
    n=read();m=read();ta=read();tb=read();k=read();
    for(int i=1;i<=n;++i)a[i]=read();
    for(int i=1;i<=m;++i)b[i]=read();
    if(n<=k||m<=k){puts("-1");return 0;}
    for(int i=0,p=1;i<=n&&i<=k;++i)
    {
        while(p<=m&&b[p]<a[i+1]+ta)++p;
        if(m-p+1+i<=k){puts("-1");return 0;}
        ans=max(ans,b[p+k-i]+tb);
    }
    printf("%d\n",ans);
    return 0;
}

C. Crazy Diamond

你有一個長度爲\(n\)的一個排列\(p\),其中\(n\)是一個偶數。
你的任務是要把這個排列排序,兩個位置能夠交換當前僅當知足\(2|i-j|\ge n\)
你須要構造一個交換次數不超過\(5n\)的交換方式。排序

考慮順次把每一個數歸位的過程。
分狀況討論一下,假設當前第\(i\)個數在位置\(p\)
若是\(p,i\)兩個位置能夠直接交換,那麼就直接交換。
不然若是\(p,i\)均可以和\(1\)交換,那麼經過\(1\)進行交換就行。
不然若是\(p,i\)均可以和\(n\)交換,那麼經過\(n\)進行交換就行。
不然\(p\)能夠和\(1\)\(i\)能夠和\(n\)交換,那麼經過\((p,1),(i,n),(1,n),(p,1),(i,n)\)這樣\(5\)次操做就能夠交換。
因此最壞狀況下就是\(5n\)次。get

#include<iostream>
#include<cstdio>
#include<vector>
using namespace std;
#define MAX 300300
inline int read()
{
    int x=0;bool t=false;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=true,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return t?-x:x;
}
int n,p[MAX],b[MAX];
bool chk(int i,int j){return 2*abs(i-j)>=n;}
vector<pair<int,int> > Ans;
void add(int i,int j){if(i==j)return;swap(p[i],p[j]);b[p[i]]=i;b[p[j]]=j;Ans.push_back(make_pair(i,j));}
int main()
{
    n=read();
    for(int i=1;i<=n;++i)b[p[i]=read()]=i;
    for(int i=1;i<=n;++i)
    {
        if(i==b[i])continue;
        if(chk(b[i],i))add(i,b[i]);
        else
        {
            int p=b[i];
            if(chk(1,p)&&chk(1,i))add(1,p),add(1,i),add(1,p);
            else if(chk(n,i)&&chk(p,n))add(n,i),add(p,n),add(n,i);
            else if(chk(1,p)&&chk(i,n))add(1,p),add(i,n),add(1,n),add(1,p),add(i,n);
        }
    }
    printf("%d\n",(int)Ans.size());
    for(auto p:Ans)printf("%d %d\n",p.first,p.second);
    return 0;
}

D. Dirty Deeds Done Dirt Cheap

給定兩個長度爲\(n\)的數列\(a,b\),保證全部數都在\([1,2n]\)中且各不相同。
如今你要選出儘量多的在\([1,n]\)的數,使他們構成一個數列\(\{i\}\),知足數列:
\(a_{i1}b_{i1}a_{i2}b_{i2}...a_{im}b_{im}\)是一個波動序列。
即每一個數都同時大於相鄰的兩個數或者小於相鄰的兩個數。it

首先數列有兩種形式,第一種是\(<><><>\)這樣子,第二種是\(><><><\)在這樣子。
那麼這樣子就肯定了\((a_i,b_i)\)組內的大小關係,能夠把二元組分紅兩類,兩類只能分別構造答案。
那麼一個二元組能夠連在另一個二元組前面,當前僅當\(b_i<a_j\)或者\(b_i>a_j\)
假如咱們只考慮\(b_i<a_j\)的狀況。考慮把全部數按照\(a_i\)排序,這樣子每一個二元組的出邊就是一個後綴,那麼咱們只須要貪心的把邊連到\(b\)最小的上面去就好了,這樣子拿線段樹就能夠維護了,或者進一步,發現\(b\)是單增的,因此用堆之類的東西維護就好了。
然而這樣子很呆。
咱們換一種考慮的方法,由於\(b\)是單增的,因此咱們直接按照\(b\)排序,此時發現按照排序以後的結果就是合法的。
由於\(a_{i}>b_{i}<b_{i+1},a_{i+1}>b_{i+1}\)
因此有\(a_{i}>b_{i}<a_{i+1}>b_{i+1}\)
相似的,反過來\(b_i>a_j\)按照\(a\)排序就好了。io

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define MAX 300300
inline int read()
{
    int x=0;bool t=false;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=true,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return t?-x:x;
}
int n,r,a[MAX],b[MAX],p[MAX];
bool cmpb(int x,int y){return b[x]<b[y];}
bool cmpa(int x,int y){return a[x]<a[y];}
int main()
{
    n=read();
    for(int i=1;i<=n;++i)a[i]=read(),b[i]=read(),r+=a[i]>b[i],p[i]=i;
    printf("%d\n",max(r,n-r));
    if(r>n-r)
    {
        sort(&p[1],&p[n+1],cmpb);
        for(int i=1;i<=n;++i)
            if(a[p[i]]>b[p[i]])printf("%d ",p[i]);
    }
    else
    {
        sort(&p[1],&p[n+1],cmpa);
        for(int i=n;i;--i)
            if(a[p[i]]<b[p[i]])printf("%d ",p[i]);
    }
    puts("");
    return 0;
}

E. Earth Wind and Fire

數軸上有\(n\)個石頭,一開始時第\(i\)個石頭在位置\(s_i\),每次你能夠選擇兩個石頭\(i,j\),知足\(s_i<s_j\),而後選擇一個\(d\),知足\(2d\le |s_i-s_j|\),而後把\(i\)移動到\(s_i+d\)位置,\(j\)移動到\(s_j-d\)位置。
給定一個長度爲\(n\)的數列\(t\),表示最終在\(t_i\)位置要有一個石頭。
問可否知足條件。
若是能夠構建一個方案。class

不難發現若是合法咱們必定能夠不改變相對順序。
那麼起始位置和目標位置作差以後,若是是要向右移動,那麼咱們直接壓進棧裏面。
不然從棧頂取元素進行移動。stream

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
#define MAX 300300
#define mp make_pair
inline int read()
{
    int x=0;bool t=false;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=true,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return t?-x:x;
}
int n,t[MAX],f;
pair<int,int> s[MAX],Q[MAX];
vector<pair<pair<int,int>,int> >Ans;
void add(int i,int j,int d)
{
    i=s[i].second,j=s[j].second;
    Ans.push_back(mp(mp(i,j),d));
}
int main()
{
    n=read();
    for(int i=1;i<=n;++i)s[i]=make_pair(read(),i);
    for(int i=1;i<=n;++i)t[i]=read();
    sort(&t[1],&t[n+1]);sort(&s[1],&s[n+1]);
    for(int i=1;i<=n;++i)
        if(s[i].first<t[i])
            Q[++f]=mp(i,t[i]-s[i].first);
        else
        {
            if(s[i].first==t[i])continue;
            int p=s[i].first-t[i];
            while(p&&f)
            {
                int mv=min(p,Q[f].second);
                add(Q[f].first,i,mv);
                p-=mv;Q[f].second-=mv;
                if(!Q[f].second)--f;
            }
            if(p){puts("NO");return 0;}
        }
    if(f){puts("NO");return 0;}
    puts("YES");
    printf("%d\n",(int)Ans.size());
    for(auto p:Ans)printf("%d %d %d\n",p.first.first,p.first.second,p.second);
    return 0;
}

F. Foo Fighters

你有\(n\)個物品,每一個物品有兩個權值\(val\)\(mask\)
你能夠選擇一個數\(s\),而後修改全部數的\(val\),若是\(val\& mask\)有奇數個\(1\)就把\(val\)變成\(-val\)
輸出一個\(s\),使得權值和符號變反。

顯然能夠把全部位分開考慮,若是選擇了這一位而且這一位的\(mask\)上有\(1\)就能夠直接取反。
如今的問題變成了咱們要選擇哪些位置。
首先位與位之間是麼有順序關係的,因此咱們隨意用什麼順序考慮都是可行的,不妨從高位往低位處理。
咱們用相似概括法的思想來考慮,咱們先不妨令數字和是正數(若是是負數能夠把全部東西所有取反)
若是咱們已經處理完了若干位,若是已經讓數字和變成了負數,那麼結束了。
不然的話,考慮是否改變這一位上的全部值,可是若是隻考慮位的話若是這一位選了會影響後面的選擇,因此不能直接這樣貪心。
咱們欽定每一個數的正負只在其最低位的時候考慮,由於這些數若是在這一位不改就不能再改了,因此每一個數就只會被考慮一次。那麼若是以這一位爲最低位的數的權值和爲正數,那麼直接取反就好了。
至於爲啥是對的。。。emmm,窩感受很對QwQ。

#include<iostream>
#include<cstdio>
using namespace std;
#define ll long long
#define MAX 300300
inline ll read()
{
    ll x=0;bool t=false;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=true,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return t?-x:x;
}
int n,v[MAX];
ll a[MAX],s,sum;
int main()
{
    n=read();
    for(int i=1;i<=n;++i)v[i]=read(),a[i]=read(),sum+=v[i];
    if(sum<0)for(int i=1;i<=n;++i)v[i]=-v[i];
    for(int i=62;~i;--i)
    {
        ll ss=0;
        for(int j=1;j<=n;++j)if(a[j]==(1ll<<i))ss+=v[j];
        if(ss>0)s|=1ll<<i;
        for(int j=1;j<=n;++j)
            if(a[j]&(1ll<<i))
            {
                a[j]^=1ll<<i;
                if(ss>0)v[j]=-v[j];
            }
    }
    printf("%lld\n",s);
    return 0;
}

G. Gold Experience

\(n\)個點,每一個點有一個點權\(a_i\),若是兩個點權的\(gcd>1\),那麼他們之間就會連上一條邊。
定義一個集合中的一個點是好的,當前僅當這個點和點集中其餘全部點都有連邊。
你要找到一個大小爲\(k\)的點集,知足其中全部點都是好的或者全部點都是很差的。

咕咕咕咕

H. Holy Diver

咕咕咕

相關文章
相關標籤/搜索