EZ 2018 07 06 NOIP模擬賽

又是慈溪那邊給的題目,此次終於沒有像上次那樣尷尬了,html

T1拿到了較高的暴力分,T2沒寫炸,而後T3寫了一個優雅的暴力就203pts,Rank3了。git

據說其它學校的分數廣泛100+,那咱們學校還不是強到飛起。數組

裝備(equipment.pas/cpp/c)

這題目出的真心長,讓我聯想到咱們語文老師說的:ui

之後教育改革可能不僅是語文,其餘學科的考試題目字數都要顯著增長了,到時後數學大題你看都看不完。spa

原來連OI的題目都有這種趨勢。code

咱們精簡題意後發現:給你兩段序列,求兩兩乘積的第\(k\)大值。htm

而後瞄一眼數據範圍,而後我就想到了一種經典的的解決方法。blog

先排序,而後把全部的\(a_i\cdot b_1\)的值都扔到大根堆裏,而後每次取出最大的值以後就把\(b_i\)的下標變成\(i+1\)再乘起來扔回去便可。排序

複雜度是\(O(q\cdot k\ log\ k)\)的,確定會炸。ip

這裏給出堆的代碼(至於又開了小根堆是爲了在\(k\)\((b-a+1)(r-l+1)-k+1\)中取一個較小的)

53ptsCODE

#include<cstdio>
#include<cctype>
#include<algorithm>
#include<queue>
using namespace std;
const int N=255,M=100005;
int n,m,a[N],b[M],c[N],d[M],q,opt,L,R,l,r,k;
struct Small
{
    int x,y,s;
    bool operator <(const Small a) const { return a.s<s; }
};
struct Big
{
    int x,y,s;
    bool operator <(const Big a) const { return a.s>s; }
};
priority_queue <Small> small;
priority_queue <Big> big;
inline char tc(void)
{
    static char fl[100000],*A=fl,*B=fl;
    return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
}
inline void read(int &x)
{
    x=0; char ch; while (!isdigit(ch=tc()));
    while (x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc()));
}
inline void write(int x)
{
    if (x>9) write(x/10);
    putchar(x%10+'0');
}
inline void copy(void)
{
    register int i;
    for (i=l;i<=r;++i)
    c[i-l+1]=a[i];
    for (i=L;i<=R;++i)
    d[i-L+1]=b[i];
}
inline bool small_cmp(int a,int b)
{
    return a<b;
}
inline bool big_cmp(int a,int b)
{
    return a>b;
}
inline void Big_solve(int k)
{
    sort(c+1,c+r-l+2,big_cmp); sort(d+1,d+R-L+2,big_cmp);
    while (!big.empty()) big.pop();
    for (register int i=1;i<=r-l+1;++i)
    big.push((Big){i,1,c[i]*d[1]});
    for (;;)
    {
        Big now=big.top(); big.pop();
        if (!(--k)) { write(now.s); putchar('\n'); return; }
        if (now.y^(R-L+1)) big.push((Big){now.x,now.y+1,c[now.x]*d[now.y+1]});
    }
}
inline void Small_solve(int k)
{
    sort(c+1,c+r-l+2,small_cmp); sort(d+1,d+R-L+2,small_cmp);
    while (!small.empty()) small.pop();
    for (register int i=1;i<=r-l+1;++i)
    small.push((Small){i,1,c[i]*d[1]});
    for (;;)
    {
        Small now=small.top(); small.pop();
        if (!(--k)) { write(now.s); putchar('\n'); return; }
        if (now.y^(R-L+1)) small.push((Small){now.x,now.y+1,c[now.x]*d[now.y+1]});
    }
}
int main()
{
    freopen("equipment.in","r",stdin); freopen("equipment.out","w",stdout);
    register int i; read(n); read(m);
    for (i=1;i<=n;++i) read(a[i]);
    for (i=1;i<=m;++i) read(b[i]); read(q);
    while (q--)
    {
        read(opt); read(l); read(r); read(L);
        if (opt)
        {
            read(R); read(k); int tot=(r-l+1)*(R-L+1); copy();
            if (k<tot-k+1) Big_solve(k); else Small_solve(tot-k+1);
        } else
        {
            if (l) b[r]=L; else a[r]=L;
        }
    }
    return 0;
}

其實這是一個經典的模板(我怎麼沒據說過),咱們首先二分答案\(x\),而後對於其中一個數組排序。

以後對於另外一個數組中的全部數只須要再次二分統計個數便可。

主要仍是一個套路題。

CODE

#include<cstdio>
#include<cctype>
#include<algorithm>
using namespace std;
const int N=255,M=100005;
int n,m,a[N],b[M],c[M],q,opt,L,R,l,r,k;
inline char tc(void)
{
    static char fl[100000],*A=fl,*B=fl;
    return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
}
inline void read(int &x)
{
    x=0; char ch; while (!isdigit(ch=tc()));
    while (x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc()));
}
inline void write(int x)
{
    if (x>9) write(x/10);
    putchar(x%10+'0');
}
inline bool cmp(int a,int b)
{
    return a>b;
}
inline void copy(void)
{
    register int i;
    for (i=L;i<=R;++i)
    c[i-L+1]=b[i]; 
    sort(c+1,c+R-L+2,cmp);
}
inline int check(int x)
{
    int rk=0;
    for (register int i=l;i<=r;++i)
    {
        int l_=1,r_=R-L+1;
        while (l_<=r_)
        {
            int mid=l_+r_>>1;
            if (c[mid]*a[i]<x) r_=mid-1; else l_=mid+1;
        }
        rk+=r_;
    }
    return rk;
}
inline void solve(void)
{
    int l=1,r=2e9;
    while (l<=r)
    {
        int mid=(r-l>>1)+l;
        if (check(mid)<k) r=mid-1; else l=mid+1;
    }
    write(r); putchar('\n');
}
int main()
{
    freopen("equipment.in","r",stdin); freopen("equipment.out","w",stdout);
    register int i; read(n); read(m);
    for (i=1;i<=n;++i) read(a[i]);
    for (i=1;i<=m;++i) read(b[i]); read(q);
    while (q--)
    {
        read(opt); read(l); read(r); read(L);
        if (opt) read(R),read(k),copy(),solve(); else l?b[r]=L:a[r]=L;
    }
    return 0;
}

又見食物鏈(chain.pas/cpp/c)

經典水題,聽說是NOI的題目。良心簽到不解釋

對於這種層級分明的關係,考慮拓撲+DP轉移。

咱們令\(f_i\)表示以\(i\)結尾的食物鏈的條數,發現轉移時:

\(f_{son[i]}+=f_i\)

而後寫一個拓撲轉移便可,最後對於出度爲\(0\)的點進行特判便可。

可是注意一點:單節點不構成食物鏈

CODE

#include<cstdio>
#include<cctype>
#include<cstring>
using namespace std;
const int N=100005;
struct edge
{
    int to,next;
}e[N<<1];
int head[N],in[N],out[N],q[N],n,m,x,y,cnt;
unsigned long long f[N],ans;
inline char tc(void)
{
    static char fl[100000],*A=fl,*B=fl;
    return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
}
inline void read(int &x)
{
    x=0; char ch; while (!isdigit(ch=tc()));
    while (x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc()));
}
inline void add(int x,int y)
{
    e[++cnt].to=y; e[cnt].next=head[x]; head[x]=cnt;
}
inline void reset(int now)
{
    for (register int i=head[now];i!=-1;i=e[i].next)
    ++f[e[i].to];
}
inline void top_sort(void)
{
    register int i; int H=0,T=0;
    for (i=1;i<=n;++i)
    if (!in[i]) q[++T]=i,reset(i);
    while (H<T)
    {
        int now=q[++H];
        for (register int i=head[now];i!=-1;i=e[i].next)
        {
            f[e[i].to]+=f[now];
            if (!(--in[e[i].to])) q[++T]=e[i].to;
        }
    }
}
int main()
{
    freopen("chain.in","r",stdin); freopen("chain.out","w",stdout);
    register int i; read(n); read(m);
    memset(head,-1,sizeof(head)); memset(e,-1,sizeof(e));
    for (i=1;i<=m;++i)
    {
        read(x); read(y);
        add(x,y); ++in[y]; ++out[x];
    }
    for (top_sort(),i=1;i<=n;++i)
    if (!out[i]) ans+=f[i];
    return printf("%lld",ans),0;
}

OJ 中的目錄 (oj.pas/cpp/c)

我去題目怎麼又是那麼長。

可是讀懂題意以後你就會發現,這道題就是NOI2010 超級鋼琴的樹上版本。

對於超級鋼琴,不會的同窗能夠看一下sol,這裏就再也不贅述。

而後在樹上怎麼弄,很簡單的套路倍增

考慮維護兩個東西\(father_{i,j}\)表示\(i\)向上\(2^j\)個點的節點,\(f_{i,j}\)同理,表示的是最大值。

而後咱們肯定這條鏈下方的點,那麼對於上面的點就要求\(sum_{father_{i,0}}\)最小了。仍是倍增\(log\)級別解決。

後面的堆等操做大同小異,其實關於樹的題目倍增真的很萬能

CODE

#include<cstdio>
#include<cctype>
#include<queue>
using namespace std;
const int N=500005,P=25;
int n,m,l,r,sum[N],father[N][P],dep[N];
long long ans;
struct data
{
    int s,l,r,t;
    bool operator <(const data x) const { return sum[x.s]-sum[father[x.t][0]]>sum[s]-sum[father[t][0]]; }
};
struct RMQ
{
    int x,num;
}f[N][P];
priority_queue<data> big;
inline char tc(void)
{
    static char fl[100000],*A=fl,*B=fl;
    return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
}
inline void read(int &x)
{
    x=0; char ch; int flag=1; while (!isdigit(ch=tc())) flag=ch^'-'?1:-1;
    while (x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc())); x*=flag;
}
inline int min(int a,int b)
{
    return a<b?a:b;
}
inline void RMQ_init(void)
{
    register int i,j;
    for (j=1;j<P;++j)
    for (i=1;i<=n;++i)
    father[i][j]=father[father[i][j-1]][j-1];
    for (j=1;j<P;++j)
    for (i=1;i<=n;++i)
    if (dep[i]>=1<<j) f[i][j]=f[i][j-1].x<f[father[i][j-1]][j-1].x?f[i][j-1]:f[father[i][j-1]][j-1];
}
inline int getfa(int x,int y)
{
    for (register int i=P-1;i>=0;--i)
    if (y&(1<<i)) x=father[x][i];
    return x;
}
inline int getmin(int x,int y)
{
    RMQ MIN=f[x][0];
    for (register int i=P-1;i>=0;--i)
    if (dep[father[y][i]]>=dep[x]) MIN=MIN.x<f[y][i].x?MIN:f[y][i],y=father[y][i];
    return MIN.num;
}
int main()
{
    freopen("oj.in","r",stdin); freopen("oj.out","w",stdout);
    register int i; read(n);
    for (i=1;i<=n;++i) read(father[i][0]);
    for (i=1;i<=n;++i) 
    {
        read(sum[i]); sum[i]+=sum[father[i][0]]; 
        dep[i]=dep[father[i][0]]+1; f[i][0]=(RMQ){sum[father[i][0]],i};
    }
    read(m); read(l); read(r); RMQ_init();
    for (i=1;i<=n;++i)
    if (dep[i]>=l)
    {
        int L=getfa(i,min(r-1,dep[i]-1)),R=getfa(i,l-1);
        big.push((data){i,L,R,getmin(L,R)});
    }
    while (m--)
    {
        data now=big.top(); big.pop(); ans+=sum[now.s]-sum[father[now.t][0]];
        if (now.l^now.t) big.push((data){now.s,now.l,father[now.t][0],getmin(now.l,father[now.t][0])});
        if (now.r^now.t) 
        {
            int next=dep[now.s]-dep[now.t]; next=getfa(now.s,next-1);
            big.push((data){now.s,next,now.r,getmin(next,now.r)});
        }
    }
    return printf("%lld",ans),0;
}
相關文章
相關標籤/搜索