NOIP2017 賽前模擬(2017.10.6)

本次考試三道題的難度適中,T1沒有認真分析,把情況考慮全面,就炸了。T2的話主要是二分的check()寫錯了;T3是異或的套路題然而我並不知道這個套路

T1 :
題目描述:
給出 n 個數。請找出一個排列使得相鄰兩個數的差的絕對值的和最大。請求出這個最大值。
題解:
通過樣例數據我們可以發現:
n爲偶數時,只存在一種情況 ,對於一個數列 1 4 2 3 ,ans=-1+4+4-2-2+3
n爲奇數時,存在兩種情況,分別是將最大的數放在中間,將最小的數放在中間,例:
對於數列 1 5 2 4 3 當前的 ans=-1+5+5-2-2+4+4-3
對於數列 4 2 5 1 3 當前的 ans=+4-2-2+5+5-1-1+3
我們不難發現,對於兩種情況我們分別把小的一半中的最大的兩個放兩邊爲最優解,和把大的一般的最小的兩個數放兩邊爲最優解,最後在取max就行了

#include<cstdio>
#include<algorithm>
#include<iomanip>
#include<iostream>
#include<cstring>
#include<string>
#include<cmath>
#include<ctime>
#include<cctype>
using namespace std;
int n,sum,T,a[60],sum0,sum1;
//---------------------
inline int Readint(){
    int i=0,f=1;char ch;
    for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
    if(ch=='-') f=-1,ch=getchar();
    for(;ch>='0'&&ch<='9';ch=getchar()) i=(i<<1)+(i<<3)+ch-'0';
    return i*f;
}
//---------------------
int main(){
    //freopen("sequence.in","r",stdin);
    //freopen("sequence.out","w",stdout);

    T=Readint();
    for(int i=1;i<=T;i++){
        cout<<"Case "<<i<<": ";
        memset(a,0,sizeof(a));
        sum=0;
        n=Readint();
        for(int j=1;j<=n;j++) a[j]=Readint();
        sort(a+1,a+1+n);
        if(n==1) cout<<sum<<endl;
        else {
            if(n%2==0){
                sum0=a[n/2],sum1=a[n/2+1];
                for(int i=1;i<n/2;i++) sum0+=2*a[i];
                for(int i=n/2+2;i<=n;i++) sum1+=2*a[i];
                sum=sum1-sum0;
            }
            else{
                sum0=0,sum1=a[n/2+1]+a[n/2+2];
                for(int i=1;i<=n/2;i++) sum0+=2*a[i];
                for(int i=n/2+3;i<=n;i++) sum1+=2*a[i];
                sum=sum1-sum0;

                sum1=0,sum0=a[n/2]+a[n/2+1];
                for(int i=1;i<n/2;i++) sum0+=2*a[i];
                for(int i=n/2+2;i<=n;i++) sum1+=2*a[i];
                sum=max(sum,sum1-sum0);
            }
            cout<<sum<<endl;
        }
    }
    return 0;
}

T2:
題目描述:
今天是ABC的生日,他製作了一個巧克力蛋糕!你可以把它理解成一個有 R×C 個小格子組成的矩形。每個格子上都有一些巧克力 chips ,第 i 行,第 j 列的格子上有 A[i][j] 個巧克力 chips 。

有 A×B 個人要出席 ABC 的生日晚會(包括ABC自己),每個人都想得到一塊蛋糕。於是他想要把他的蛋糕切成 A×B 個小塊。首先,他先橫着切(A-1)刀,蛋糕就變成了 A 條,然後,對於每一條,他都縱着切(B-1)刀,就的到了(A×B)個小塊。爲了體現自己的大度, ABC 決定最後選蛋糕,而 ABC 的朋友就不會這麼想了,他們總是拿走巧克力最多的蛋糕,所以,ABC 得到的蛋糕永遠是巧克力最少的那一塊。

聰明的 ABC 很喜歡喫巧克力,他想得到更多的巧克力,但是他正忙着準備自己的生日晚會,於是他來尋求你的幫助,希望你不要讓他失望。

題解:
題目看完,正常人都能看出是二分,而且都知道直接貪心check即可,然而考試的時候我並沒有想出這個貪心的方法..

回到正題,由題意可知,每次橫着切是必定會把蛋糕徹底切開,所以我們貪心的時候只需要對於每一橫排,看它能不能被切成B塊,如果不能的話,再加上下一排,最後再看看排數有沒有A 即可

#include<cstdio>
#include<algorithm>
#include<iomanip>
#include<iostream>
#include<cstring>
#include<string>
#include<cmath>
#include<ctime>
#include<cctype>
using namespace std;
int n,m,a,b,val[520][520];
long long L,R,tmp[520];
//--------------------- 
inline int Readint(){
    int i=0,f=1;char ch;
    for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
    if(ch=='-') f=-1,ch=getchar();
    for(;ch>='0'&&ch<='9';ch=getchar()) i=(i<<1)+(i<<3)+ch-'0';
    return i*f;
}
//---------------------
inline bool check(long long x){
    memset(tmp,0,sizeof(tmp));
    int cnt0=0,cnt1,w;
    for(int i=1;i<=n;i++){
        cnt1=w=0;
        for(int j=1;j<=m;j++) tmp[j]+=val[i][j];
        for(int j=1;j<=m;j++){
            w+=tmp[j];
            if(w>=x) cnt1++,w=0;
        }
        if(cnt1>=b){
            memset(tmp,0,sizeof(tmp));
            cnt0++;
        }
        if(cnt0>=a) return true;
    }
    return cnt0>=a;
}
//---------------------
int main(){
    //freopen("cut.in","r",stdin);
    //freopen("cut.out","w",stdout);

    n=Readint(),m=Readint(),a=Readint(),b=Readint();
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            val[i][j]=Readint();
            R+=val[i][j];
        }
    }
    while(L<=R){
        long long mid=(L+R)>>1;
        if(check(mid)) L=mid+1;
        else R=mid-1;
    }
    cout<<R<<endl;
    return 0;
}

T3:
題目描述:
求一棵帶邊權的樹的一條最大 Xor 路徑的值。這裏的“路徑”不一定從根到葉子結點,中間一段路徑只要滿足條件也可以。

其實這道題就是一個在一個二進制tri樹上的貪心,前提是我考試的時候要先知道Xor路徑是什麼意思。
首先, Xor路徑就是路徑上的每條邊的長度都與前面的值異或一下的結果。
然後,你要知道 a^b^a=b 得到,a^b^b=a;
所以對於樹上的任意兩點間的路徑的異或值,等於他們到根節點的異或值再異或一下,因爲lca以上的部分被抵消了;
當這些都已經知道了,就只剩下了tri樹上的套路了:
將每個點到根節點的異或路徑的值都用二進制加入到tri樹中,由異或的性質我們可以知道,要想兩條路徑的異或值最大,對於一條已知的路徑,我們只需要在tri樹上從根節點開始出發,每次儘量找以當前位不同的數繼續走(比如當前是1,就找0,反之亦然)

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<cmath>
#include<ctime>
#include<iomanip>
#include<iostream>
#include<cctype>
using namespace std;
//---------------------
const int N = 100005,L=35;
struct node{
    int son[2];
}tri[N*L];
int n,len,mx;
int tot,first[N],to[N<<1],next[N<<1],val[N<<1];
int dis[N];
//---------------------
inline int Readint(){
    int i=0,f=1;char ch;
    for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
    if(ch=='-') f=-1,ch=getchar();
    for(;ch>='0'&&ch<='9';ch=getchar()) i=(i<<1)+(i<<3)+ch-'0';
    return i*f;
}
//---------------------
inline void add(int x,int y,int z){
    next[++tot]=first[x];first[x]=tot;
    to[tot]=y;val[tot]=z;
}
//---------------------
inline void dfs(int now,int fa){
    for(int e=first[now];e;e=next[e]){
        int v=to[e];
        if(v!=fa){
            dis[v]=dis[now]^val[e];
            dfs(v,now);
        }
    }
}
//---------------------
inline void Insert(int val){
    int po=0;
    for(int i=len-1;i>=0;i--){
        int t=(val>>i)&1;
        if(!tri[po].son[t]) tri[po].son[t]=++tot;
        po=tri[po].son[t];
    }
}
//---------------------
inline int find(int val){
    int po=0;
    for(int i=len-1;i>=0;i--){
        int t=(val>>i)&1;
        if(tri[po].son[t^1]) po=tri[po].son[t^1],val|=(1<<i);
        else po=tri[po].son[t],val^=(t<<i);
    }
    return val;
}
//---------------------
int main(){
//  freopen("xor.in","r",stdin);

    int x,y,z;
    n=Readint();
    for(int i=1;i<n;i++){
        x=Readint(),y=Readint(),z=Readint();
        mx=max(mx,z);
        add(x,y,z); add(y,x,z);
    }

    dfs(1,0);

    while(mx) mx>>=1,len++;
    tot=0;
    for(int i=1;i<=n;i++) Insert(dis[i]);
    for(int i=1;i<=n;i++){
        dis[i]=find(dis[i]);
        mx=max(mx,dis[i]);
    }
    cout<<mx<<endl;
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章