DAY 4模拟赛

DAY 4

zhx出题


 

T1

裂变连接

【问题描述】

你是能看到第一题的 friends 呢。git

——hja数组

众所周知,小葱同窗擅长计算,尤为擅长计算组合数,但这个题和组合数没什么关系。优化

如今有𝑁个异构体在前线排成一排参与战斗,但因为地方火力过于凶猛,异构体们都受到了不一样程度的损伤。为了可以让全部异构体继续战斗,避免因为能量不均衡致使的爆炸,异构体们须要使得他们彼此之间的能量值同样。记第𝑖个异构体当前𝑒𝑖的能量,每一个时刻,每一个异构体均可以作以下三种操做中的一种:ui

一、传递1的能量给本身左边相邻的异构体(若是存在)。spa

二、传递1的能量给本身右边相邻的异构体(若是存在)。code

三、传递1的能量给本身(摸鱼)。blog

为了尽快的回到前线做战,异构体们但愿在最短的时间内使得全部异构体的能量值同样,问最短期。数据保证有解。操做过程当中本身的能量能够变为负数。排序

【输入格式】

第一行一个整数𝑁表明异构体数量。游戏

接下来一行𝑁个整数表明能量值。ci

【输出格式】

输出一行一个数表明答案。

【样例输入】

3

1 0 5

【样例输出】

3

【数据规模与约定】

对于30%的数据,𝑁 ≤ 10, |𝑒𝑖| ≤ 10。

对于60%的数据,𝑁 ≤ 100。

对于另外20%的数据,能量值的序列必定先单调递增再单调递减。

对于100%的数据,1 ≤ 𝑁 ≤ 10^5, |𝑒𝑖| ≤ 10^5。

 

题解

肯定一个异构体在最优状况下传递的能量

 

 

每个都达到v

前缀和表示总能量

由于都要变成v,因此前i-1总能量应该是(i-1)*v

也就是说i不须要向左边输出能量

右边最后的能量值应该是(n-i)*v

节点的做用就是把左边的能量的差值所有转移到右边去,这个差值就是它传递的能量

枚举每个节点

总共四种状况

1.左边多,右边少 

2.左边少,右边多  

3.左边多,右边多,说明i本身少    此时须要两边与指望能量差值的max

4.左边少,右边少,说明i本身多    此时须要两边与指望能量差值的和

而后取max就好了

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>

using namespace std;

const int maxn=100010;

int n,z[maxn];

long long sum[maxn];

void read()
{
    scanf("%d",&n);
    for (int a=1;a<=n;a++)
        scanf("%d",&z[a]);
}

long long work()
{
    for (int a=1;a<=n;a++)
        sum[a] = sum[a-1]+z[a];
    if (sum[n]%n) return -1;
    long long ans=0,v=sum[n]/n;
    for (int a=1;a<=n;a++)
    {
        long long l=sum[a-1]-1ll*(a-1)*v;
        long long r=sum[n]-sum[a]-1ll*(n-a)*v;
        if (l<0 && r<0) ans=max(ans,-l-r);
        ans=max(ans,max(abs(l),abs(r)));
    }
    return ans;
}

int main()
{
    read();
    printf("%lld\n",work());

    return 0;
}

T2

死亡鸽者

【问题描述】

你是能看到第二题的 friends 呢。

——aoao

众所周知,小葱同窗擅长计算,尤为擅长计算组合数,但这个题和组合数没什么关系。

死亡鸽者最喜欢的事情就是咕咕咕,他的座右铭“风萧萧兮易水寒,壮士一去兮不复返,然鸽子至今未到”也天天被他所忘记。今天,死亡鸽者又要开始咕咕的旅行了。如今有𝑁座城市排成一排,死亡鸽者会从第一座城市一直走到最后一座城市。每一个城市都有一个数𝑎𝑖,每次死亡鸽者能够选择取走或者不取走这个数,但想要取走这个数的话要求这个数必须是全部已经取走的数的倍数或者约数。如今问死亡鸽者从第一座城市走到最后一座城市的过程当中,最多取走多少个数。

【输入格式】

第一行一个数𝑁。

接下来一行𝑁个数表明每一个城市的数。

【输出格式】

一行一个数表明答案。

【样例输入】

3

2 6 3

【样例输出】

2

【数据规模与约定】

对于20%的数据,𝑁 ≤ 10。

对于50%的数据,𝑁 ≤ 1000。

对于另外20%的数据,全部城市的数不重复。

对于100%的数据,1 ≤ 𝑁, 𝑎𝑖 ≤ 10^6。

 

题解

从左向右走?其实没啥用,答案和这个没有关系

能够把这些数从小到大排序,这样就只用考虑这个数是否是前边的数的倍数就好了

最长上升子序列

F[i]表示选择a[i]的状况下序列长度最长是多少

转移就是枚举它前面的数

只须要保证a[i]是a[j]的倍数就好了,由于a[j]必定已是前边的数的倍数了

f[i]=max(f[j])+1  a[i]|a[j]

50pts

 

若是没有重复的

答案的长度不超过logn

那么就能够去重,由于只要一个重复的选上,其余就必须选

去重以后跑搜索也能够过

或者把dp在去重以后的数组上作,也能够过

100pts

 

正解?

全部的数<=10^6这个条件尚未用过

能够开10^6个桶记录每一个数出现了多少次

用f[i]表示如今取出的序列在最后一个数为i的状况下最长有多长

如今取出的数是i,那么以后的都是i的倍数

f[k*i]=max(f[i]+cnt[k*i])

                                              

 

 

 

咱们并不关心每一个数是多少,只关心有多少个

#include<cstdio>
#include<cstdlib>
#include<cstring>

using namespace std;

const int maxn=1000010;

int n,cnt[maxn],f[maxn];

int main()
{
    scanf("%d",&n);
    for (int a=1;a<=n;a++)
    {
        int v;
        scanf("%d",&v);
        cnt[v]++;
        f[v]++;
    }
    int ans=0;
    for (int a=1;a<=1000000;a++)
        if (f[a])
        {
            if (f[a]>ans) ans=f[a];
            for (int b=a+a;b<=1000000;b+=a)
                if (cnt[b] && f[a]+cnt[b]>f[b]) f[b]=f[a]+cnt[b];
        }
    printf("%d\n",ans);


    return 0;
}

T3

进阶之灾

【问题描述】

你是能看到第三题的 friends 呢。

——laekov

众所周知,小葱同窗擅长计算,尤为擅长计算组合数,但这个题和组合数没什么关系。

TarjanLusa 是一款风靡全球的卡牌游戏,在这款游戏中,你须要一层一层的前进,击败像萌死戳、天启骑士等 boss,最终来到心脏面前一决胜负。为了可以有一套更好的卡牌来面对心脏,咱们须要在每一层选择更好的卡牌。假设咱们总共有𝑁层,在第𝑖层的时候,咱们能够从两张卡牌中选择一张加入咱们的卡组,这两张卡牌的战斗力分别为𝑎𝑖, 𝑏𝑖。在通过𝑁层的选择以后,咱们便会有一套𝑁张卡的卡组,而整套卡组的战斗力取决于卡牌与卡牌之间战斗力差值的绝对值的最小值。可是心脏是一个很是强大的敌人,若是咱们不能拥有强大的战斗力,人类就会一败涂地。因此,如今咱们想知道,战斗力最大多是多少。

【输入格式】

第一行一个整数𝑁。

接下来𝑁行每行两个整数𝑎𝑖, 𝑏𝑖。

【输出格式】

输出一行一个数表明答案。

【样例输入】

3

1 2

3 4

5 6

【样例输出】

2

【数据规模与约定】

对于20%的数据,1 ≤ 𝑁 ≤ 10。

对于50%的数据,1 ≤ 𝑁 ≤ 100。

对于另外20%的数据,全部卡牌战斗力的最大值减去最小值小于等于100。

对于100%的数据,1 ≤ 𝑁 ≤ 10^5, 1 ≤ 𝑎𝑖, 𝑏𝑖 ≤ 10^9

 

题解

2-SAT?

然鹅不会写

最小值最大化

二分

看看可否构造出战斗力大于等于v的卡牌组

如今有a1,b1和a2,b2

若是|a1-a2|,那么a1,a2不能同时选,从a1向b2连边

同理,从a2向b2连边

直接n^2枚举卡牌,而后跑2-SAT

50pts

 

不能过100pts的缘由

缘由:枚举建边是n^2的

 

60~70 pts 直接输出0  ??? 抽屉原理...我吐了

 

发现边太多了,咱们想尽可能节省边的数量

线段树优化建图(大雾)

把a1b1,a2b2...anbn拿出来从小到大排序

c1,c2,...,c2n

考虑ci,必定对应原来的某个数

两条边之间建边的条件是|ai-aj|<v

也就是ci前面一段和ci后面一段,是连续的区间

李姐为是区间加边的操做

线段树

若是第五个节点排好序以后是a2  若是有一个冲突的,必定和b2连边

那么在a2这个节点下面挂一个b2

区间加边

假如a3想对2-6之间的节点加边

2-6拆成[2,2],[3,4],[5,6]

从a3对这三个区间加边

                                                         

 

 

 

连通性没有发生改变

图和原来的是等价的

Zhxtql

因此边数是n log^2 n

而后跑tarjan

 

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<algorithm>

using namespace std;

const int BUF_SIZE = 30;
char buf[BUF_SIZE], *buf_s = buf, *buf_t = buf + 1;
  
#define PTR_NEXT() \
    { \
        buf_s ++; \
        if (buf_s == buf_t) \
        { \
            buf_s = buf; \
            buf_t = buf + fread(buf, 1, BUF_SIZE, stdin); \
        } \
    }
   
#define readint(_n_) \
    { \
        while (*buf_s != '-' && !isdigit(*buf_s)) \
            PTR_NEXT(); \
        bool register _nega_ = false; \
        if (*buf_s == '-') \
        { \
            _nega_ = true; \
            PTR_NEXT(); \
        } \
        int register _x_ = 0; \
        while (isdigit(*buf_s)) \
        { \
            _x_ = _x_ * 10 + *buf_s - '0'; \
            PTR_NEXT(); \
        } \
        if (_nega_) \
            _x_ = -_x_; \
        (_n_) = (_x_); \
    }

#define readstr(_s_) \
    { \
        while (!isupper(*buf_s)) \
            PTR_NEXT(); \
        char register *_ptr_ = (_s_); \
        while (isupper(*buf_s) || *buf_s == '-') \
        { \
            *(_ptr_ ++) = *buf_s; \
            PTR_NEXT(); \
        } \
        (*_ptr_) = '\0'; \
    }

#define readlonglong(_n_) \
    { \
        while (*buf_s != '-' && !isdigit(*buf_s)) \
            PTR_NEXT(); \
        bool register _nega_ = false; \
        if (*buf_s == '-') \
        { \
            _nega_ = true; \
            PTR_NEXT(); \
        } \
        long long register _x_ = 0; \
        while (isdigit(*buf_s)) \
        { \
            _x_ = _x_ * 10 + *buf_s - '0'; \
            PTR_NEXT(); \
        } \
        if (_nega_) \
            _x_ = -_x_; \
        (_n_) = (_x_); \
    }

#define wmt 1,(n<<1),1
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1

const int maxn=100010;
const int maxp=maxn+(maxn<<2);
const int maxm=maxn+maxp+maxn*20;

int n,size,cnt,en,t,dfn[maxp],low[maxp],s[maxp],belong[maxp],pos[maxn];

bool instack[maxp];

struct edge
{
    int e;
    edge *next;
}*v[maxp],ed[maxm];

void add_edge(int s,int e)
{
    en++;
    ed[en].next=v[s];v[s]=ed+en;v[s]->e=e;
}

struct rec
{
    int v,p;
    rec(){}
    rec(int a,int b)
    {
        v=a;p=b;
    }
}z[maxn];

bool operator<(const rec &a,const rec &b)
{
    return a.v<b.v;
}

void dfs(int p)
{
    t++;
    dfn[p]=low[p]=t;
    instack[p]=true;
    s[++size]=p;
    for (edge *e=v[p];e;e=e->next)
        if (!dfn[e->e])
        {
            dfs(e->e);
            low[p]=min(low[p],low[e->e]);
        }
        else
        {
            if (instack[e->e]) low[p]=min(low[p],dfn[e->e]);
        }
    if (dfn[p]==low[p])
    {
        cnt++;
        while (s[size]!=p)
        {
            belong[s[size]]=cnt;
            instack[s[size]]=false;
            size--;
        }
        belong[p]=cnt;
        instack[p]=false;
        size--;
    }
}

void build(int l,int r,int rt)
{
    if (l==r)
    {
        add_edge(rt+(n<<1),z[l].p<=n?z[l].p+n:z[l].p-n);
        return;
    }
    int m=(l+r)>>1;
    build(lson);
    build(rson);
    add_edge(rt+(n<<1),(rt<<1)+(n<<1));
    add_edge(rt+(n<<1),(rt<<1|1)+(n<<1));
}

void insert(int l,int r,int rt,int nowl,int nowr,int p)
{
    if (nowl<=l && r<=nowr)
    {
        add_edge(p,rt+(n<<1));
        return;
    }
    int m=(l+r)>>1;
    if (nowl<=m) insert(lson,nowl,nowr,p);
    if (m<nowr) insert(rson,nowl,nowr,p);
}

bool check(int k)
{
    en=0;cnt=0;
    memset(v,0,sizeof(v));
    memset(dfn,0,sizeof(dfn));

    build(wmt);

    int r=1,l=1;

    for (int a=1;a<=(n<<1);a++)
    {
        int op,p=z[a].p;
        if (p<=n) op=pos[p+n];
        else op=pos[p-n];
        while (r<=a && z[r].v <= z[a].v-k)
            r++;
        if (r<a && r>=1 && z[r].v > z[a].v-k) 
        {
            if (op>=r && op<=a-1)
            {
                if (op>r) insert(wmt,r,op-1,z[a].p);
                if (op<a-1) insert(wmt,op+1,a-1,z[a].p);
            }
            else insert(wmt,r,a-1,z[a].p);
        }
        while (l<=(n<<1) && z[l].v < z[a].v+k)
            l++;
        l--;
        if (l>a && l<=(n<<1) && z[l].v < z[a].v+k) 
        {
            if (op>=a+1 && op<=l)
            {
                if (op>a+1) insert(wmt,a+1,op-1,z[a].p);
                if (op<l) insert(wmt,op+1,l,z[a].p);
            }
            else insert(wmt,a+1,l,z[a].p);
        }
    }

    for (int a=1;a<=(n<<1);a++)
        if (!dfn[a]) dfs(a);
    for (int a=1;a<=n;a++)
        if (belong[a]==belong[a+n]) return false;
    return true;
}

int main()
{
    readint(n);
    int minv=0x3f3f3f3f,maxv=-0x3f3f3f3f;
    int x=0;
    for (int a=1;a<=n;a++)
    {
        int v1,v2;
        readint(v1);
        readint(v2);
        z[++x]=rec(v1,a);
        z[++x]=rec(v2,a+n);
        minv=min(minv,min(v1,v2));
        maxv=max(maxv,max(v1,v2));
    }
    if (maxv-minv+1 < n)
    {
        printf("0\n");
        return 0;
    }
    sort(z+1,z+x+1);
    for (int a=1;a<=(n<<1);a++)
        pos[z[a].p]=a;
    int l=0,r=1000000001;
    while (l+1!=r)
    {
        int m=(l+r)>>1;
        if (check(m)) l=m;
        else r=m;
    }
    printf("%d\n",l);

    return 0;
}

 

 

 

思考题:

N 1,2,3,4...n

有m组边 第i组边是从i向li~ri连一个长度为di的边

求1~n的最短路

相关文章
相关标签/搜索