【清北学堂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:重建国家

好难啊根本不会写,以后再填坑吧。

posted @ 2018-10-15 17:05  maomao9173  阅读(599)  评论(0编辑  收藏  举报