1. 程式人生 > >CCF-NOIP-2018 提高組(複賽) 模擬試題(五)

CCF-NOIP-2018 提高組(複賽) 模擬試題(五)

T1 相遇

【問題描述】

在一場奇怪的夢裡,小 Y 來到了一個神奇的國度。這個國度可以用一根數軸表示,小 Y 在 N 處,而小 Y 想吃的美食在 K 處。小 Y 有兩種方式移動, 一種叫做步行, 一種叫做瞬移。 對於每次步行操作,小 Y 可以從$ x $移動到 \(x + 1\) 或者 \(x – 1\), 而對於每次瞬移操作小 Y 可以從 \(x\) 瞬移到\(2x\)。那麼小 Y 最少要移動多少次才能到達 K 處吃到食物呢?

【輸入格式】

僅有兩個整數 \(N\)\(K\)

【輸出格式】

共一行,包含一個整數,表示最少的移動次數

【樣例1】

樣例輸入
5 17
樣例輸出
4

資料規模與約定

0 ≤ N, K ≤ 100,000

題解

方法很多,然而考試時本蒟蒻只想到了DP的做法。我們設\(f[i]\)表示從\(n\)\(i\)的最短路徑,此時我們已經可以得知所有\(\forall i \le n\)時的情況,因為噹噹前位置大於想要到達的位置時,我們只能通過每次移動到\(x-1\)來逐步逼近位置,即:\[f[i] = \begin{cases} 0 & i=n \\ f[i+1]+1 & i < n \end{cases}\]
此時我們發現,當\(i>n\)時,任意一個\(f[i]\)都可以通過\(f[i-1]\)到達。特殊的,當\(i\)

為偶數時,每個\(f[i]\)位置都可以經由\(f[i/2]\)到達。同時,我們仍然需要考慮\(f[i]=f[i-1]\)的情況。事實上若\(i\)為偶數時並不需要考慮,因為從\(f[i+1]\)到達\(f[i]\)的步驟必然要大於從\(min(f[i/2],f[i-1])\)到達的步驟(可以想想為什麼)而當\(i\)為奇數時,我們可以考慮取從\(f[i-1]\)到達\(f[i]\)所需步驟和從\(f[(i+1)/2]\)到達\(f[i]\)的步驟的最小值。因此,我們的轉移方程為:
\[f[i] = \begin{cases} 0 & i=n\\ f[i+1]+1 & i<n\\ min(f[i/2]+1,f[i-1]+1) & i>n,i\%2=0\\ min(f[(i+1)/2]+2,f[i-1]+1) & i>n,i\%2=1 \end{cases} \]

ps:本蒟蒻的dp水平極低,因此推出的式子極為冗長,但是整體思維層次不高,畢竟本蒟蒻都能AC
程式碼如下:

#include<bits/stdc++.h>
#define maxn 100000
using namespace std;
long long n,k;
long long f[maxn];
int main(){
    //freopen("meet.in","r",stdin);
    //freopen("meet.out","w",stdout);
    scanf("%d%d",&n,&k);
    f[n]=0;
    for(register long long i=n-1;i>=0;i--)f[i]=f[i+1]+1;
    for(register long long i=n+1;i<=k;i++){
        if(i%2==0){
            f[i]=min(f[i/2]+1,f[i-1]+1);
        }
        else f[i]=min(f[i-1]+1,f[(i+1)/2]+2);
        //cout<<i<<":"<<f[i]<<endl;
    }
    //for(register long long i=0;i<=n;i++)cout<<i<<":"<<f[i]<<endl;
    cout<<f[k]<<endl;
    return 0;
}

T2 祕密郵件

【問題描述】

\(A\)收到了一封來自外星球的祕密郵件。郵件由\(n\)個大寫英文字母組成,不巧的是小\(A\)收到郵件以後一不小心打亂了原來的字母順序。但是聰明的小\(A\)記住了原郵件的完整內容, 現在她每次可以選擇打亂後郵件中相鄰的兩個字母進行交換,問最少交換多少次能夠將打亂的郵件恢復成原郵件

【輸入格式】

第一行一個整數\(n\)表示郵件的長度。
第二行一個長度為\(n\)的只包含大寫字母的字串表示打亂後的郵件 。
第三行一個長度為\(n\)的只包含大寫字母的字串表示原郵件 。
為保證打亂後的郵件可以恢復成原郵件,所有的測試資料滿足任意一種大寫字母在兩封郵件中的出現次數相同。

【輸出格式】

共一行包含一個整數,表示最少的交換次數。

【樣例1】

樣例輸入
4
ABCD
DBCA
樣例輸出
5

資料規模與約定

$n \le 1,000,000 $

題解

蒟蒻的我考試時竟然只寫了60分的暴力程式碼QAQ事實上,這道題比T1還要簡單。我們只需要給第二個字串中每個字元一個雜湊值,並將這個雜湊值帶入第一個字串中,求一下逆序對的數量即可!
貼出程式碼QAQ

#include <bits/stdc++.h>
using namespace std;
long long a[1000100],b[1000100],ans = 0;
long long n;
void merge_sort(long long l,long long r){
    long long p1,p2,p,mid;
    if(l == r){
        return ;
    }
    mid = (l+r) >> 1;
    merge_sort(l,mid);
    merge_sort(mid+1,r);
    p1 = l,p2 = mid+1,p = 0;
    while(p1 <= mid || p2 <= r){
        if(p1 <= mid && (p2 > r || a[p1] <= a[p2])){
            b[++p] = a[p1];
            p1++;
        }else{
            b[++p] = a[p2];
            p2++;
            ans += mid-p1+1;
        }
    }
    for(long long i = 1;i <= p;i++){
        a[l+i-1] = b[i];
    }
}
int main(){
    //freopen("letter.in","r",stdin);
    //freopen("letter.out","w",stdout);
    cin >> n;
    string A,B;
    cin >> A >> B;
    queue<int> q[26];
    for(int i = 0;i < A.size();i++){
        q[(int)(A[i]-'A')].push(i+1);
    }
    for(int i = 0;i < B.size();i++){
        a[i+1] = q[(int)(B[i]-'A')].front();
        q[(int)(B[i]-'A')].pop();
    }
    ans = 0;
    merge_sort(1,n);
    cout << ans;
    return 0;
}

T3 小喬

【問題描述】

\(戀之微風·小喬,是手遊《王者榮耀》中的法師型英雄,擅長遠端消耗。小喬有一把神奇的扇子,藉助靈活的走位可以對敵人造成高額的傷害。小喬是小A最喜歡(會玩)的英雄之一。在一場夢裡,小\)A\(與小喬來到了一個異次元世界。異次元世界位於極座標系中。小喬定義了一個值m,以等分[-π,π]弧度(詳見樣例) 。小喬還有一把神奇的扇子,她將進行\)n\(次“綻放之舞”操作。對於第\)i\(次"綻放之舞”操作,小喬將設定半徑\)r_i\(,起始位置s_i,終止位置t_i,她藉助自己神奇的扇子,以座標系原點為圓心,)r_i為半徑,將圓心角\frac{πs_i}{m}到圓心角\frac{πt_i}{m}這部分扇形區域逆時針疊加一層“治癒微笑” 。\)
小喬想到了一個有趣(奇怪)的問題,她希望知道有多大面積的區域被疊加過至少2層“治癒微笑” 。這個問題難倒了平日裡善於發現並解決問題的小 A。現在小 A 求助於你,希望你能幫他解決這個問題。我們設答案的值為\(T\),為了方便表達,你只需要輸出\(T*\frac{2m}{π}\)(可以證明這是一個非負整數)的值即可。

【輸入格式】

第一行是三個整數\(n,m,k\)
接下來\(n\)行,依次描述每個“綻放之舞”操作,每行包含三個整數\(r_i,s_i,t_i\)

【輸出格式】

輸出只包含一個整數,表示\(T*\frac{2m}{π}\)的值。

【樣例輸入1】

3 8 2
1 -8 8
3 -7 3
5 -5 5

【樣例輸出1】

樣例輸出
76

image

【資料規模與約定】

$ 1\le n \le 100000,1 \le m \le 10^5 $

【題解】

原諒我淺薄的幾何知識。
image

#include <bits/stdc++.h>
#define mp make_pair
#define fi first
#define se second
 
using namespace std;

typedef long long int64;
const int MAXN=400005;

int n,m,lim;
int na,nb;

struct A{
    int l,r,R;
    A(void){}
    A(int _l,int _r,int _R):l(_l),r(_r),R(_R){}
    inline void print()
    {
        printf("l = %d, r = %d, R = %d.\n",l,r,R);
    }
}a[MAXN],b[MAXN];

struct B{
    int p,r;
    bool f;
    #define SHANCHU 0
    #define CHARU 1
    B(void){}
    B(int _p,int _r,bool _f):p(_p),r(_r),f(_f){}
    friend bool operator < (const B &a,const B &b)
    {
        return a.p<b.p;
    }
}eve[MAXN];
int elen;

struct Size_Balanced_Tree{
    int left,right,size,data;
}SBT[MAXN*5];
int pn,root;

inline void left_rotate(int &p)
{
    int k=SBT[p].right;
    SBT[p].right=SBT[k].left;
    SBT[k].left=p;
    SBT[k].size=SBT[p].size;
    SBT[p].size=SBT[SBT[p].left].size+SBT[SBT[p].right].size+1;
    p=k;
}

inline void right_rotate(int &p)
{
    int k=SBT[p].left;
    SBT[p].left=SBT[k].right;
    SBT[k].right=p;
    SBT[k].size=SBT[p].size;
    SBT[p].size=SBT[SBT[p].left].size+SBT[SBT[p].right].size+1;
    p=k;
}

void maintain(int &p,bool f)
{
    if (!f)
    {
        if (SBT[SBT[SBT[p].left].left].size>SBT[SBT[p].right].size) right_rotate(p);
        else if (SBT[SBT[SBT[p].left].right].size>SBT[SBT[p].right].size) left_rotate(SBT[p].left),right_rotate(p);
        else return;
    }
    else
    {
        if (SBT[SBT[SBT[p].right].right].size>SBT[SBT[p].left].size) left_rotate(p);
        else if (SBT[SBT[SBT[p].right].left].size>SBT[SBT[p].left].size) right_rotate(SBT[p].right),left_rotate(p);
        else return;
    }
    maintain(SBT[p].left,0);
    maintain(SBT[p].right,1);
    maintain(p,0);
    maintain(p,1);
}

void Insert(int &p,int v)
{
    if (!p)
    {
        p=++pn;
        SBT[p].left=SBT[p].right=0;
        SBT[p].size=1;
        SBT[p].data=v;
    }
    else
    {
        SBT[p].size++;
        if (v<SBT[p].data) Insert(SBT[p].left,v);
        else Insert(SBT[p].right,v);
        maintain(p,v>=SBT[p].data);
    }
}

int Delete(int &p,int v)
{
    SBT[p].size--;
    int k=SBT[p].data;
    if (v==k||(v<k&&!SBT[p].left)||(v>k&&!SBT[p].right))
    {
        if (!SBT[p].left||!SBT[p].right) p=SBT[p].left+SBT[p].right;
        else SBT[p].data=Delete(SBT[p].left,v+1);
        return k;
    }
    else
    {
        if (v<k) return Delete(SBT[p].left,v);
        else return Delete(SBT[p].right,v);
    }
}

inline int Find_Kth(int k)
{
    int p=root;
    while (SBT[SBT[p].left].size+1!=k)
    {
        if (SBT[SBT[p].left].size+1<k) k-=SBT[SBT[p].left].size+1,p=SBT[p].right;
        else p=SBT[p].left;
    }
    return SBT[p].data;
}

inline int64 work(A *a,int n)
{
    pn=root=elen=0;
    for (int i=1;i<=n;i++)
    {
        eve[++elen]=B(a[i].l,a[i].R,CHARU);
        eve[++elen]=B(a[i].r,a[i].R,SHANCHU);
    }
    sort(eve+1,eve+elen+1);
    int64 res=0;
    int last=eve[1].p;
    int l=1,r=1;
    while (l<=elen)
    {
        while (r<=elen&&eve[r].p==eve[l].p) r++;
        if (SBT[root].size>=lim)
        {
            int64 x=Find_Kth(SBT[root].size-lim+1);
            res+=x*x*(eve[l].p-last);
        }
        for (int i=l;i<r;i++)
        {
            if (eve[i].f==CHARU) Insert(root,eve[i].r);
            else Delete(root,eve[i].r);
        }
        last=eve[l].p;
        l=r;
    }
    return res;
}

int main()
{
    //freopen("xiaoqiao.in","r",stdin);
    //freopen("xiaoqiao.out","w",stdout);
    scanf("%d%d%d",&n,&m,&lim);
    for (int i=1;i<=n;i++)
    {
        int R,l,r;
        scanf("%d%d%d",&R,&l,&r);
        if (l==m) l=-m;
        if (r==-m) r=m;
        if (l==r) continue;
        if (l<=0&&r<=0)
        {
            if (l<r) a[++na]=A(l,r,R);
            else if (l>r)
            {
                if (l!=0) a[++na]=A(l,0,R);
                if (-m!=r) a[++na]=A(-m,r,R);
                if (0!=m) b[++nb]=A(0,m,R);
            }
        }
        else if (l>=0&&r>=0)
        {
            if (l<r) b[++nb]=A(l,r,R);
            else if (l>r)
            {
                if (l!=m) b[++nb]=A(l,m,R);
                if (0!=r) b[++nb]=A(0,r,R);
                if (-m!=0) a[++na]=A(-m,0,R);
            }
        }
        else if (l<=0&&r>=0)
        {
            if (l!=0) a[++na]=A(l,0,R);
            if (0!=r) b[++nb]=A(0,r,R);
        }
        else if (l>=0&&r<=0)
        {
            b[++nb]=A(l,m,R);
            a[++na]=A(-m,r,R);
        }
    }
    printf("%lld\n",work(a,na)+work(b,nb));
    return 0;
}