1. 程式人生 > >【清北學堂2018-刷題衝刺】Contest 9

【清北學堂2018-刷題衝刺】Contest 9

 前幾天本蒟蒻一直在頹廢所以這篇題解咕了很久,而且最後一個題目不太會,最終也沒完成,非常慚愧。

 寫這些題目收穫相當大。後面的日子呢,我會繼續著手刷NOIP題目和Codeforces題目。

 到這裡就算結束了,終於算是為我國慶時七天的訓練畫上一個句號了。(雖然並不完美)

Task 1:破冰派對

【問題描述】

 由於計算機系的同學們都很宅,很多同學雖然身在⼀個系,但是⼊學很久還是相互不認識。學⽣會主席小\(Y\) 希望舉辦⼀次破冰派對,要讓同學們多從寢室裡⾛出來參加娛樂活動,也要讓儘量多不認識的同學們通過活動相互認識。自然的,如果參加活動的同學互相都不認識,那便是極好的。:)

 要辦⼀次成功的派對是很不容易的,不光需要有同學參加,優秀的⼯作⼈員也是必不可少的。他們需要為派對的籌辦付出很多的努⼒,因此⼀個和諧的團隊是非常重要的。小\(Y\)
希望所有⼯作⼈員都是相互認識的。

 計算機系⼀共有\(N\) 個同學,所有同學從1 到\(N\) 編號。有\(M\) 對同學相互認識,⽽其餘的同學相互不認識。

 小\(Y\) 希望從中選出⼀些⼯作⼈員組成⼯作團隊,讓這個⼯作團隊負責活動的組織,⽽其餘的所有非⼯作⼈員, 就自然都成為了活動的參與者。小\(Y\) 要求:

  • ⼯作團隊的成員必須相互認識;
  • 參與活動的同學必須相互不認識;
  • ⾄少有⼀個同學參與活動,也⾄少有⼀個同學是⼯作⼈員。

 ⼀共有多少種⼯作團隊的選擇⽅案呢?

【輸入格式】

 第⼀⾏讀⼊⼀個整數\(T\),表示測試資料的組數。

 接下來\(T\) 組資料,每組資料格式如下:

 第⼀⾏包含兩個整數\(N\)\(M\)

 接下來\(M\) ⾏,第\(i\) ⾏包含兩個不同的,在1 到\(N\) 之間的整數\(xi,yi\),表示編號為\(xi\)\(yi\) 的同學相互認識。

 輸⼊資料保證在每⼀組測試資料中,任意兩個同學之間的朋友關係都不會被列出兩次。

【輸出格式】

 輸出⽂件名為\(party.out\)

 對於每⼀組測試資料,輸出⼀⾏⼀個整數,表示可⾏的⽅案總數mod 1000003。

party.in party.out
2 0
1 0 3
4 4
1 2
1 3
2 3
3 4

【資料規模與約定】

 對於20% 的資料,有\(N <= 10\);

 對於40% 的資料,有\(N <= 30\);

 對於100% 的資料,有\(1 <= N <= 1000; 0 <= M <= N^2; 1 <= T <= 6\)

 直接搜尋極大團,不多解釋,輸入量大要用\(fread\)

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
int t,n,m,u,v,s,i,ans,cnt;
char ch[1<<25],*p=ch;
bool mp[1010][1010],inque[1010];

inline int read(){
    s=0;
    while(*p==' '||*p=='\n')++p;
    while('0'<=*p && *p<='9'){
        s=s*10+*p-'0';
        ++p;
    }
    return s;
}

void dfs(int pos,int sz){
    if(pos>n){
        if(sz!=0&&sz!=n)
            ans++;
        //至少一個人參加 
        return;
    }
    bool can_in=true,can_out=true;
    for(i=1;i<pos;++i)
        if(inque[i]){
            if(!mp[pos][i])
                can_in=false;

            //have any len not linked to graph 
        }else{
            if(mp[pos][i])
                can_out=false;

            //have any len linked to outside
        }

    if(can_in){
        inque[pos]=true; 
        dfs(pos+1,sz+1);
    } 
    if(can_out){
        inque[pos]=false;
        dfs(pos+1,sz);
    }
}
int main(){
    freopen("party.in","r",stdin);
    freopen("party.out","w",stdout);
    fread(ch,1,1<<25,stdin);
    t=read();
    register int i;
//  printf("%d\n",t);
    while(t--){
        ans=0;
        memset(inque,0,sizeof(inque));
        memset(mp,0,sizeof(mp));
        n=read(),m=read();
//      printf("%d %d\n",n,m);
        for(i=1;i<=m;++i){
            u=read(),v=read();
            mp[u][v]=true;
            mp[v][u]=true;
        }
        dfs(1,0);
        printf("%d\n",ans); 
    }
} 

Task 2:無聊遊戲

【問題描述】

 小\(N\)和小\(A\) 在玩這樣的⼀個遊戲:給定初始數列\(Q\),小\(N\) 先把某個字首(可以為空) 的數字全部乘上\(-A\),小\(A\) 再把某個字尾(可以為空) 的數字全部乘上\(-B\),小\(N\) 想讓最後所有數的和儘量⼤,⽽小\(A\) 想讓最後所有數的和儘量的小。

 因為小\(A\) ⽆比聰明絕對不會失誤,所以小\(N\) 想找到某個⽅法使得最後所有數的和儘量⼤,請幫助小\(N\) 求出最⼤的值是多少吧。

【輸入格式】

 輸⼊⽂件名為\(game.in\)

 第⼀⾏三個正整數\(N,A,B\),表示數列的長度和小\(N\)\(A\)乘的數。

 第⼆⾏有\(N\)個整數表示數列\(Q\)

【輸出格式】

 輸出⽂件名為\(game.out\)

 輸出⼀⾏⼀個整數S,表示最後數列的和的最⼤值。

game.in game.out
3 1 1 0
-1 -2 -3

【樣例解釋】

 如果小N 修改前0 個數,那麼小A 修改後0 個數,數列是{1,2,3},和為6

 如果小N 修改前1 個數,那麼小A 修改後0 個數,數列是{1,2,-3},和為4

 如果小N 修改前2 個數,那麼小A 修改後0 個數或後3 個數,數列是{1,2,-3} 或{-1,-2,3},和為0

 如果小N 修改前3 個數,那麼小A 修改後3 個數,數列是{-1,-2,-3},和為-6

 最後答案是max{-6,-4,0,-6} = 0

【資料規模與約定】

\(對於70\% 的資料,有n<=100000\)

\(對於100\% 的資料,有n<=1000000,1<=A,B<=100,| Qi |<=10^9\)

 推公式和結論的題目,稍微有一點難。

 首先可以想到兩個人選的數實際上就是一個字首區間和一個字尾區間。為了統一處理,我們把字尾和轉化成字首和。

 設第一個人選\([1,p]\),第二個人選\([k+1,n]\)考慮所有選擇情況,有以下兩種分類:

  • \(k>p\)的時候,兩個區間沒有重疊部分,分別計算,此時答案可以整理為:
    • \(ss=-(A+1)*Sp+(B+1)Smin[p,n]-B*Sn\)
  • \(k<=p\)時,兩個區間開始有重疊,考慮此時答案:
    • \(ss=-(A+AB)*Smax[1,p-1]+(B+AB)Sp-B*Sn\)

\(A,B\)均為正數,對於第一個人的每一種決策第二個人都會選擇兩種方案分類中最佳的一個回答,只需要考慮第二個人的最優答案的最大值即可。

 (第二個人的最優答案:->所有數儘可能小->選和儘可能大的區間變化)

第二個人為了自己的方案最優會使答案最小,所以就只需要考慮k>p時的sum最大值和k<=p時的sum最小值更新答案即可。

Task 3:重建國家

好難啊根本不會寫,以後再填坑吧。