Codeforces gym 2011-2012, Samara Interacademic Programming

Codeforces gym 2011-2012, Samara Interacademic Programming

题目地址:http://codeforces.com/gym/100030

今天(2014.11.12,周日)用了3个半小时完成了7道简单题,最后回来又过了一题,共8题

题目比较水,但有些题目比较好,也不是那么好想的,所以就认真做了下这篇总结

其中用到了博弈,dp,二进制状压及位运算,two pointers,lower_bound与upper_bound搜索

总的来说,都需要模拟或贪心,考一定算法而又不考很高深算法,属于简单题目


还有几道题没写出来,最后看了一下,目前还没过,改天过了再更新本博文

本比赛输入输出方式均为文件输入方式

所以根据要求代码要添加下面两行

freopen("input.txt","r",stdin);

freopen("output.txt","w",stdout);





这题是博弈论题,感觉算一道好题


注意到n和a[i]范围很小,应该是博弈判断来解决。这种题有点像博弈论里Nim游戏的题目,Nim游戏可以用异或 (^) 解决,但这里不是用异或解决

此博弈满足零和博弈,即“你死我活”,先手胜后手必败,先手败后手必胜

发现一:对于一叠石子,其数量为a[i],a[i]如果是奇数,那么先手必胜,否则先手必败

那么,对于n叠石子

如果这一叠是最后一叠,直接通过“发现一”来解决

如果不是最后一叠,其可以通过上一叠石子状态来推得,如下:

假设第  i  和 第 i+1  叠石子,假设 i 叠为奇数,则 i+1 叠先手后手顺序与 i 叠一致,否则就要变换

假设C为 第 i 叠的先手,M为后手,a[i]为奇数,那么对于这一叠来说,C必败,即C会进行到不能再进行的一步,所以C在下一叠还是先手

同理,加入a[i]为偶数,M会进行到不能再进行的一步,所以M在下一叠就是先手,此时先后说顺序要改变


所以我们可以知道第一叠的先手顺序和第一叠石子的奇偶性,由此可以推出第二叠(如果有的话)石子的先手顺序,又知道第二叠石子的奇偶性......

所以,就可以知道第n叠石子的先手顺序,又知道第n叠石子的奇偶性,利用“发现一”就可以知道最终赢家是谁

具体代码如下:

//Hello. I'm Peter.
#include<cstdio>
#include<iostream>
#include<sstream>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<functional>
#include<cctype>
#include<ctime>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
using namespace std;
typedef long long ll;
typedef long double ld;
#define peter cout<<"i am peter"<<endl
#define input freopen("data.txt","r",stdin)
#define randin srand((unsigned int)time(NULL))
#define INT (0x3f3f3f3f)*2
#define LL (0x3f3f3f3f3f3f3f3f)*2
#define gsize(a) (int)a.size()
#define len(a) (int)strlen(a)
#define slen(s) (int)s.length()
#define pb(a) push_back(a)
#define clr(a) memset(a,0,sizeof(a))
#define clr_minus1(a) memset(a,-1,sizeof(a))
#define clr_INT(a) memset(a,INT,sizeof(a))
#define clr_true(a) memset(a,true,sizeof(a))
#define clr_false(a) memset(a,false,sizeof(a))
#define clr_queue(q) while(!q.empty()) q.pop()
#define clr_stack(s) while(!s.empty()) s.pop()
#define rep(i, a, b) for (int i = a; i < b; i++)
#define dep(i, a, b) for (int i = a; i > b; i--)
#define repin(i, a, b) for (int i = a; i <= b; i++)
#define depin(i, a, b) for (int i = a; i >= b; i--)
#define pi acos(-1.0)
#define eps 1e-6
#define MOD 1000000007
#define MAXN 2014
#define N 100
#define M
int a[N],n;
int main()
{
    freopen("input.txt","r",stdin);
    freopen("output.txt","w",stdout);
    cin>>n;
    repin(i,1,n){
        scanf("%d",a+i);
    }
    bool Cfirst=true;
    bool Cwin=true;
    repin(i,1,n){
        if(i==n){
            if(Cfirst && a[i]%2) Cwin=false;
            else if(Cfirst && a[i]%2==0) Cwin=true;
            else if(!Cfirst && a[i]%2) Cwin=true;
            else if(!Cfirst && a[i]%2==0) Cwin=false;
        }
        else{
            if(Cfirst && a[i]%2) Cfirst=true;
            else if(Cfirst && a[i]%2==0) Cfirst=false;
            else if(!Cfirst && a[i]%2) Cfirst=false;
            else if(!Cfirst && a[i]%2==0) Cfirst=true;
        }
    }
    if(Cwin) printf("Constantine\n");
    else printf("Mike\n");
}






此题为经典dp题

题意如下:

给一个长度为n的数列a[ ],给定m,对于1=<i<=n,则要求 1=<a[i]<=m,a[i]为整数;

同时,要求数列为严格单调递增数列,即对于任意 1=<i<j<=n ,a[i]<a[j];

给定了n和m,问符合条件的数列有多少个,答案需要模上10^9+7


设dp[i][j]表示  数列第 i 个位置 放 j 这个数时的情况数,1<=i<=n,1=<j<=m

先预处理

起初dp[i][j]里所有值均为0

for j: 1 to m

   dp[1][j]=1;

表示第一个位置放 j 这个数时情况数为1

由于严格单调递增性质,得到如下转移方程:

for i: 2 to n

  for j: 1 to m

      for k: 1 to j-1

           dp[i][j]=dp[i][j]+dp[i-1][k];

表示,当i>=2时候,dp[i][j] 由上一层获得,上一层dp[i-1][k] 中满足题意的当且仅当 1<=k<j (因为数列单调递增)

最后统计答案:

for j : 1 to m

    ans=ans+dp[n][j];

(再上面过程适当补上取模操作即可)

代码如下:

//Hello. I'm Peter.
#include<cstdio>
#include<iostream>
#include<sstream>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<functional>
#include<cctype>
#include<ctime>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
using namespace std;
typedef long long ll;
typedef long double ld;
#define peter cout<<"i am peter"<<endl
#define input freopen("data.txt","r",stdin)
#define randin srand((unsigned int)time(NULL))
#define INT (0x3f3f3f3f)*2
#define LL (0x3f3f3f3f3f3f3f3f)*2
#define gsize(a) (int)a.size()
#define len(a) (int)strlen(a)
#define slen(s) (int)s.length()
#define pb(a) push_back(a)
#define clr(a) memset(a,0,sizeof(a))
#define clr_minus1(a) memset(a,-1,sizeof(a))
#define clr_INT(a) memset(a,INT,sizeof(a))
#define clr_true(a) memset(a,true,sizeof(a))
#define clr_false(a) memset(a,false,sizeof(a))
#define clr_queue(q) while(!q.empty()) q.pop()
#define clr_stack(s) while(!s.empty()) s.pop()
#define rep(i, a, b) for (int i = a; i < b; i++)
#define dep(i, a, b) for (int i = a; i > b; i--)
#define repin(i, a, b) for (int i = a; i <= b; i++)
#define depin(i, a, b) for (int i = a; i >= b; i--)
#define pi acos(-1.0)
#define eps 1e-6
#define MOD 1000000007
#define MAXN 2014
#define N 300
#define M
int dp[N][N],n,m,ans;
int main()
{
    freopen("input.txt","r",stdin);
    freopen("output.txt","w",stdout);
    cin>>n>>m;
    repin(j,1,m){
        dp[1][j]=1;
    }
    repin(i,2,n){
        repin(j,1,m){
            rep(k,1,j){
                dp[i][j]+=dp[i-1][k];
                dp[i][j]%=MOD;
            }
        }
    }
    ans=0;
    repin(j,1,m){
        ans+=dp[n][j];
        ans%=MOD;
    }
    cout<<ans<<endl;
}





此题很容易

题意如下:

有n台电脑,每一次每台电脑都可以给k台电脑“充电”(与题意一个意思,换种表述)

一开始只有一台电脑有电,这一次此台电脑给k个电脑充电后,下一次仍然可以

问,最少多少次把n台电脑都充满电?

假设now表示当前有now台电脑有电,起初now=1;

while(now<n)

   now=now+now*k;

   ans=ans+1

主要代码就上面三行,now<n表示还需要给其他电脑充电,已经充过的就没有再充的必要(贪心)

下一次的now由上一次的得来,这一次有now台有电,那么这一次可以给now*k台电脑充电,所以下一次now=now+now*k;此时ans+=1表示充了一次

这个while loop的时间复杂度是O(logn)级别的

因为now+now*k>=2*now (k>=1),所以now是以指数级别增长,跳出循环条件是now<n,是O(logn)级别,具体就不证明解释了

代码如下:

//Hello. I'm Peter.
#include<cstdio>
#include<iostream>
#include<sstream>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<functional>
#include<cctype>
#include<ctime>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
using namespace std;
typedef long long ll;
typedef long double ld;
#define peter cout<<"i am peter"<<endl
#define input freopen("data.txt","r",stdin)
#define randin srand((unsigned int)time(NULL))
#define INT (0x3f3f3f3f)*2
#define LL (0x3f3f3f3f3f3f3f3f)*2
#define gsize(a) (int)a.size()
#define len(a) (int)strlen(a)
#define slen(s) (int)s.length()
#define pb(a) push_back(a)
#define clr(a) memset(a,0,sizeof(a))
#define clr_minus1(a) memset(a,-1,sizeof(a))
#define clr_INT(a) memset(a,INT,sizeof(a))
#define clr_true(a) memset(a,true,sizeof(a))
#define clr_false(a) memset(a,false,sizeof(a))
#define clr_queue(q) while(!q.empty()) q.pop()
#define clr_stack(s) while(!s.empty()) s.pop()
#define rep(i, a, b) for (int i = a; i < b; i++)
#define dep(i, a, b) for (int i = a; i > b; i--)
#define repin(i, a, b) for (int i = a; i <= b; i++)
#define depin(i, a, b) for (int i = a; i >= b; i--)
#define pi acos(-1.0)
#define eps 1e-6
#define MOD 1000000007
#define MAXN 2014
#define N 300
#define M
ll n,k,now,ans;
int main()
{
    freopen("input.txt","r",stdin);
    freopen("output.txt","w",stdout);
    cin>>n>>k;
    ans=0;
    now=1;
    while(now<n){
        now+=now*k;
        ans+=1;
    }
    cout<<ans<<endl;
}







此题好题,用到了两次二进制状态压缩


如上图有20个空格,是一个二进制数,发现,第2,6,7,19,20空为1,其余空为0. 表示选择第2,6,7,19,20题目,其余题目不选择

这样子用二进制表示状态,就可以知道每一个数对应的状态下哪几道题被选择了,是一种枚举的方法

数的下限是0,上限是2^n-1,即查找[0,2^n-1]里的每一个数

然后用适当的方法,查找当前数中哪几个位置为1,适当存储下来(具体过程不描述了,方法多样简单,可以脑补粗来)

那么,我们得到一个vector<int>vt,其中存储的是选择的题目的编号

根据题目,我们知道每一个题目对应哪一个程序(solution,同样的意思,姑且用程序描述)

发现,程序个数不超过60,也可以用二进制表示(2^60<9*10^18,不超过longlong的范围)


如上图,是一个含有60个空的二进制数

与第一副题类似,其表示的是,每个题目是否含有每个程序,发现上面这个二进制数,3,4,5,6...等程序为1,表示含有该程序,其余0表示不含有

那么在输入的时候,就可以维护每个题目程序所表示的二进制数

long long a[i]; //a[i]表示第 i 个题目所含有的程序对应的二进制数

for i: 1 to n

   scanf->k;

   a[i]=0;

   for j: 1 to k

       scanf->t;

      a[i]=a[i] + (1LL<<(t-1));

题目保证k个数都不一样,这样a[i]就可以准确表示第 i 个程序对应的二进制数

现在回到之前枚举[0,2^n-1]里的数的环节

我们通过vector<int>vt已经知道哪些题目被选择了

那么这些题目对应的a[i]全部异或起来,即anst=(a[vt[0]] | a[vt[1]] | ...... | a[vt[len-1]]);  len=vt.size();

ant表示的是所选择的题目对应的所有程序的集合,同样把其看成一个二进制数

题目要求所有程序必须被选择,则anst中m个格子必须全为1,即anst==(1<<m)-1,此时满足题意,就可以动态规划维护答案

代码如下:

//Hello. I'm Peter.
#include<cstdio>
#include<iostream>
#include<sstream>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<functional>
#include<cctype>
#include<ctime>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
using namespace std;
typedef long long ll;
typedef long double ld;
#define peter cout<<"i am peter"<<endl
#define input freopen("data.txt","r",stdin)
#define randin srand((unsigned int)time(NULL))
#define INT (0x3f3f3f3f)*2
#define LL (0x3f3f3f3f3f3f3f3f)*2
#define gsize(a) (int)a.size()
#define len(a) (int)strlen(a)
#define slen(s) (int)s.length()
#define pb(a) push_back(a)
#define clr(a) memset(a,0,sizeof(a))
#define clr_minus1(a) memset(a,-1,sizeof(a))
#define clr_INT(a) memset(a,INT,sizeof(a))
#define clr_true(a) memset(a,true,sizeof(a))
#define clr_false(a) memset(a,false,sizeof(a))
#define clr_queue(q) while(!q.empty()) q.pop()
#define clr_stack(s) while(!s.empty()) s.pop()
#define rep(i, a, b) for (int i = a; i < b; i++)
#define dep(i, a, b) for (int i = a; i > b; i--)
#define repin(i, a, b) for (int i = a; i <= b; i++)
#define depin(i, a, b) for (int i = a; i >= b; i--)
#define pi acos(-1.0)
#define eps 1e-6
#define MOD 1000000007
#define MAXN 100100
#define N 100
#define M
int n,m,k,ans;
int limit1;
ll a[N],llt;
vector<int>ansv;
int main()
{
    freopen("input.txt","r",stdin);
    freopen("output.txt","w",stdout);
    cin>>n>>m;
    rep(i,0,n){
        scanf("%d",&k);
        a[i]=0;
        int t;
        rep(j,0,k){
            scanf("%d",&t);
            a[i]+=(1LL<<(t-1));
        }
    }
    limit1=(1<<n)-1;
    vector<int>ansvt;
    ans=INT;
    int anst;
    repin(i,0,limit1){
        llt=0;
        ansvt.clear();
        anst=0;
        rep(j,0,n){
            if(i&(1<<j)){
                llt|=a[j];
                ansvt.pb(j+1);
                anst+=1;
            }
        }
        if(llt==(1LL<<m)-1 && anst<ans){
            ans=anst;
            ansv=ansvt;
        }
    }
    sort(ansv.begin(),ansv.end());
    printf("%d\n",ans);
    int lenv=gsize(ansv);
    bool first=true;
    rep(i,0,lenv){
        if(first) first=false;
        else printf(" ");
        printf("%d",ansv[i]);
    }
    printf("\n");
}









此题用一个结构体排序,后统计一下就好了。不解释了。是签到题

代码如下:

//Hello. I'm Peter.
#include<cstdio>
#include<iostream>
#include<sstream>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<functional>
#include<cctype>
#include<ctime>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
using namespace std;
typedef long long ll;
typedef long double ld;
#define peter cout<<"i am peter"<<endl
#define input freopen("data.txt","r",stdin)
#define randin srand((unsigned int)time(NULL))
#define INT (0x3f3f3f3f)*2
#define LL (0x3f3f3f3f3f3f3f3f)*2
#define gsize(a) (int)a.size()
#define len(a) (int)strlen(a)
#define slen(s) (int)s.length()
#define pb(a) push_back(a)
#define clr(a) memset(a,0,sizeof(a))
#define clr_minus1(a) memset(a,-1,sizeof(a))
#define clr_INT(a) memset(a,INT,sizeof(a))
#define clr_true(a) memset(a,true,sizeof(a))
#define clr_false(a) memset(a,false,sizeof(a))
#define clr_queue(q) while(!q.empty()) q.pop()
#define clr_stack(s) while(!s.empty()) s.pop()
#define rep(i, a, b) for (int i = a; i < b; i++)
#define dep(i, a, b) for (int i = a; i > b; i--)
#define repin(i, a, b) for (int i = a; i <= b; i++)
#define depin(i, a, b) for (int i = a; i >= b; i--)
#define pi acos(-1.0)
#define eps 1e-6
#define MOD 1000000007
#define MAXN 100100
#define N 3000
#define M 30
struct Subject{
    int id;
    int num_class;
    int ans;
}sub[N];
int k,n,now;
bool comp1(const Subject a,const Subject b){
    return a.num_class<b.num_class;
}
bool comp2(const Subject a,const Subject b){
    return a.id<b.id;
}
int main()
{
    freopen("input.txt","r",stdin);
    freopen("output.txt","w",stdout);
    cin>>k>>n;
    repin(i,1,n){
        sub[i].id=i;
        sub[i].ans=0;
        scanf("%d",&sub[i].num_class);
    }
    sort(sub+1,sub+1+n,comp1);
    now=0;
    repin(i,1,n){
        if(now+sub[i].num_class<k){
            sub[i].ans=sub[i].num_class;
            now+=sub[i].num_class;
        }
        else if(now+sub[i].num_class>=k){
            sub[i].ans=k-now;
            break;
        }
    }
    sort(sub+1,sub+1+n,comp2);
    bool first=true;
    repin(i,1,n){
        if(first) first=false;
        else printf(" ");
        printf("%d",sub[i].ans);
    }
    printf("\n");
}





此题算一道好题,我记得这个方法可以叫two pointers吧?就算不行,也类似了,姑且是吧

要求最长符合题意的子串(实际输出对应原串的起始和终止位置),要求子串中字符种类不超过k

设两个pointers,也就是两个变量from, i ,from表示头指针,i 表示当前指针,i-from+1表示当前的子串的长度,s[from]...s[i]表示当前子串

设int num[26],表示'a'-'z'(对应0-25)出现的次数

for i : 0 to len-1

  int t=s[i]-'a';

  num[t]+=1;

  if(num[t]==1) num_dif+=1;

num_dif表示当前s[from]...s[i]这个子串中种类数

为什么num[t]==1就+=1呢?因为num[t]==1,说明之前num[t]==0,说明当前串之前没有s[i]这个字符,现在有了,所以种类数+=1

接下来

  if(num_dif>k)

      while(num_dif>k)

           int t2=s[from]-'a';

           num[t2]-=1;

   if(num[t2]==0)  num_dif -=1;

           from++;

题目要求种类数不能大于k,所以当num_dif>k的时候,当前子串已经不符合题意,就需要将from往右移动,即from++;

移动过程,需要维护num[]和种类数num_dif,当移动到一个地方满足num_dif<=k时候立刻跳出while loop,这样子就可以保证最优性

接下来统计答案

if(i-from+1>ans)

   ans=i-from+1;

   ansfrom=from;

   ansto=ansto;

最后,ansfrom和ansto就是需要输出的答案

由于子串的连续性,可以保证这样的two pointers方法求出的答案是最优的

具体代码如下:

//Hello. I'm Peter.
#include<cstdio>
#include<iostream>
#include<sstream>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<functional>
#include<cctype>
#include<ctime>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
using namespace std;
typedef long long ll;
typedef long double ld;
#define peter cout<<"i am peter"<<endl
#define input freopen("data.txt","r",stdin)
#define randin srand((unsigned int)time(NULL))
#define INT (0x3f3f3f3f)*2
#define LL (0x3f3f3f3f3f3f3f3f)*2
#define gsize(a) (int)a.size()
#define len(a) (int)strlen(a)
#define slen(s) (int)s.length()
#define pb(a) push_back(a)
#define clr(a) memset(a,0,sizeof(a))
#define clr_minus1(a) memset(a,-1,sizeof(a))
#define clr_INT(a) memset(a,INT,sizeof(a))
#define clr_true(a) memset(a,true,sizeof(a))
#define clr_false(a) memset(a,false,sizeof(a))
#define clr_queue(q) while(!q.empty()) q.pop()
#define clr_stack(s) while(!s.empty()) s.pop()
#define rep(i, a, b) for (int i = a; i < b; i++)
#define dep(i, a, b) for (int i = a; i > b; i--)
#define repin(i, a, b) for (int i = a; i <= b; i++)
#define depin(i, a, b) for (int i = a; i >= b; i--)
#define pi acos(-1.0)
#define eps 1e-6
#define MOD 1000000007
#define MAXN 100100
#define N
#define M 30
int num[M],lens,num_dif,from,ans,ansfrom,ansto,k;
char s[MAXN];
int idx(char c){
    return c-'a';
}
int main()
{
    freopen("input.txt","r",stdin);
    freopen("output.txt","w",stdout);
    cin>>k;
    scanf("%s",s+1);
    lens=len(s+1);
    int ct,ct2;
    num_dif=0;
    from=1;
    ans=0;
    repin(i,1,lens){
        ct=idx(s[i]);
        num[ct]+=1;
        if(num[ct]==1) num_dif+=1;
        if(num_dif>k){
            while(1){
                ct2=idx(s[from]);
                num[ct2]-=1;
                if(num[ct2]==0) num_dif-=1;
                from++;
                if(num_dif<=k) break;
            }
        }
        if(i-from+1>ans){
            ans=i-from+1;
            ansfrom=from;
            ansto=i;
        }
    }
    printf("%d %d\n",ansfrom,ansto);
}





 这题很逗,被题意卡了一下,最后百度才发现 Triskaidekaphobia

的意思是黑色星期五,同时也有13恐惧症的意思

所以,主人公不希望所给的串中出现“13”,题目问最少删除多少个字符保证字符串没有“13”。


题意卡了下,方法也卡了下,不过被我的一个发现给解决了

假设如下串(S1)

3333333333311111113333333111111111333331111331111111333333311111111111

发现一:串开头的3和结尾的1 不会构成“13”

所以不妨把头尾的3和结尾的1给删除,删除后若串空了,说明答案为0

删除后,串变为(S2):

111111133333331111111113333311113311111113333333

那么,我们就可以得到4组“13”连续串

第一组:11111113333333

第二组:11111111133333

第三组:111133

第四组:11111113333333

将每一组存储在一个结构体数组中,存储字符1和3的个数

发现二:答案一定不超过S2中1和3总数的最小值

即ans=min(num1,num3);

发现二是显然的,但不是答案,答案可能会比其更小

举个栗子:

1133333311111133

此时,含有8个1和8个3,但如果删除头两个1和后两个3,串就变为333333111111,不含有“13”符合题意,这样最优答案就是4


那么怎么办呢?百思不得其解时,忽得发现三四

发现三:对于每组连续串,要不删除1,要不删除3,这样子是最优的

发现四:对于第 i 组连续串,若我选择删除3,那么,后面的连续串 i+1...n 选择删除3一定比选择删除1会更好

我们回到之前S2:111111133333331111111113333311113311111113333333

假设第2个连续串选择了删除3,串会变为:11111113333333111111111_11113311111113333333

('_' 只是为了表示之前3字符串所在的位置,实际上不存在'_')

那么,此时,如果第3个连续串我选择删除1,串会变为:11111113333333111111111_3311111113333333

会发现,此时,第2个连续串的1也必须删除,或者第3个连续串的3必须删除

也就是说,要不然第二连续串全部删除了,要不然第三个连续串全部删除了,这与发现三是矛盾的。


由此可以得到一个结论,假设第 i 个连续串我删除了3,后面的连续穿必然也要删除3

于是乎,我们已经得到ans=min(num1,num3);

for i: 1 to n // n表示连续串的个数

   t=从1到 i-1所有连续串1的总数  +  从 i 到 n 所有连续串3的总数;

   ans=min(ans,t); 

至于t怎么求解,前缀和以及后缀和就可以了。

具体代码如下:

//Hello. I'm Peter.
#include<cstdio>
#include<iostream>
#include<sstream>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<functional>
#include<cctype>
#include<ctime>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
using namespace std;
typedef long long ll;
typedef long double ld;
#define peter cout<<"i am peter"<<endl
#define input freopen("data.txt","r",stdin)
#define randin srand((unsigned int)time(NULL))
#define INT (0x3f3f3f3f)*2
#define LL (0x3f3f3f3f3f3f3f3f)*2
#define gsize(a) (int)a.size()
#define len(a) (int)strlen(a)
#define slen(s) (int)s.length()
#define pb(a) push_back(a)
#define clr(a) memset(a,0,sizeof(a))
#define clr_minus1(a) memset(a,-1,sizeof(a))
#define clr_INT(a) memset(a,INT,sizeof(a))
#define clr_true(a) memset(a,true,sizeof(a))
#define clr_false(a) memset(a,false,sizeof(a))
#define clr_queue(q) while(!q.empty()) q.pop()
#define clr_stack(s) while(!s.empty()) s.pop()
#define rep(i, a, b) for (int i = a; i < b; i++)
#define dep(i, a, b) for (int i = a; i > b; i--)
#define repin(i, a, b) for (int i = a; i <= b; i++)
#define depin(i, a, b) for (int i = a; i >= b; i--)
#define pi acos(-1.0)
#define eps 1e-6
#define MOD 1000000007
#define MAXN 100100
#define N 10100
#define M 30
string s;
int ans;
struct Segment{
    int num1,num3;
}seg[N];
int preone[N],suffixthree[N];
int num_seg;
int main()
{
    freopen("input.txt","r",stdin);
    freopen("output.txt","w",stdout);
    cin>>s;
    ans=0;
    int len=slen(s);
    while(len){
        if(s[0]=='3') s.erase(0,1);
        else break;
        len=slen(s);
    }
    while(len){
        if(s[len-1]=='1') s.erase(len-1,1);
        else break;
        len=slen(s);
    }
    len=slen(s);
    if(!len){
        printf("%d\n",0);
        exit(0);
    }
    clr(seg);
    clr(preone);
    clr(suffixthree);
    num_seg=0;
    int last=3;
    rep(i,0,len){
        if(last==3 && s[i]=='1'){
            num_seg+=1;
            seg[num_seg].num1+=1;
            last=1;
        }
        else if(last==1 && s[i]=='3'){
            seg[num_seg].num3+=1;
            last=3;
        }
        else if(last==1 && s[i]=='1') seg[num_seg].num1+=1;
        else if(last==3 && s[i]=='3') seg[num_seg].num3+=1;
    }
    repin(i,1,num_seg){
        preone[i]=preone[i-1]+seg[i].num1;
    }
    depin(i,num_seg,1){
        suffixthree[i]=suffixthree[i+1]+seg[i].num3;
    }
    int num1=0,num3=0;
    rep(i,0,len){
        if(s[i]=='1') num1+=1;
        else num3+=1;
    }
    ans=min(num1,num3);
    int t;
    repin(i,1,num_seg){
        t=preone[i-1]+suffixthree[i];
        ans=min(ans,t);
    }
    cout<<ans<<endl;
}




题意就不讲了,都能看得懂,讲起来有点绕口

发现一:ans1一定是所有ai 或 bi 里的一个数

那么,就可以用lower_bound和upper_bound求出答案,复杂度为O(nlogn)

for i: 1 to num// num表示所有a[i]和b[i]的总数

   p=a[i]或者b[i];

   k1=满足p<a[j]的所有 j 的总数 //可以用upper_bound求出

   k2=满足p>b[j]的所有 j 的总数 //可以用lower_bound求出

   k3=n-(k1+k2);

   // k3表示满足a[j]=<p<=b[j]的所有 j 的总数

   ....再维护答案


再维护答案的代码在这里讲一下,题目说了,p>b[j]的话,捐钱数为0;p<a[j]的话,捐钱数为a[j],所以捐钱总数就为所有 p<a[j] 的 a[j]的总和

在求k1是用upper_bound来求,用upper_bound前提是所求数组要单调,这里是单调递增,那么就可以用前缀和的办法求出所有p<a[j] 的 a[j]的总和

而当 a[j]=<p<=b[j],捐钱数就为 p,则捐钱总数就为 k3*p(如果用int定义的话,注意int到longlong的坑)

这样子不断维护的答案即可

代码如下:

//Hello. I'm Peter.
#include<cstdio>
#include<iostream>
#include<sstream>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<functional>
#include<cctype>
#include<ctime>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
using namespace std;
typedef long long ll;
typedef long double ld;
#define peter cout<<"i am peter"<<endl
#define input freopen("data.txt","r",stdin)
#define randin srand((unsigned int)time(NULL))
#define INT (0x3f3f3f3f)*2
#define LL (0x3f3f3f3f3f3f3f3f)*2
#define gsize(a) (int)a.size()
#define len(a) (int)strlen(a)
#define slen(s) (int)s.length()
#define pb(a) push_back(a)
#define clr(a) memset(a,0,sizeof(a))
#define clr_minus1(a) memset(a,-1,sizeof(a))
#define clr_INT(a) memset(a,INT,sizeof(a))
#define clr_true(a) memset(a,true,sizeof(a))
#define clr_false(a) memset(a,false,sizeof(a))
#define clr_queue(q) while(!q.empty()) q.pop()
#define clr_stack(s) while(!s.empty()) s.pop()
#define rep(i, a, b) for (int i = a; i < b; i++)
#define dep(i, a, b) for (int i = a; i > b; i--)
#define repin(i, a, b) for (int i = a; i <= b; i++)
#define depin(i, a, b) for (int i = a; i >= b; i--)
#define pi acos(-1.0)
#define eps 1e-6
#define MOD 1000000007
#define MAXN
#define N 100100
#define M
int n;
int a[N],b[N],c[N<<1],numc,ans1;
ll suffixa[N],ans2,anst;
int main()
{
    freopen("input.txt","r",stdin);
    freopen("output.txt","w",stdout);
    cin>>n;
    numc=0;
    repin(i,1,n){
        scanf("%d %d",a+i,b+i);
        c[++numc]=a[i];
        c[++numc]=b[i];
    }
    sort(a+1,a+1+n);
    suffixa[n+1]=0;
    depin(i,n,1){
        suffixa[i]=suffixa[i+1]+a[i];
    }
    sort(b+1,b+1+n);
    int k1=1,k2=1,k3=1;
    ans1=INT;
    ans2=0;
    repin(i,1,numc){
        k1=(int)(upper_bound(a+1,a+1+n,c[i])-a);
        k1=n-(k1-1);
        k2=(int)(lower_bound(b+1,b+1+n,c[i])-b);
        k2-=1;
        k3=n-(k1+k2);
        k1=n-k1+1;
        anst=suffixa[k1]+(ll)k3*c[i];
        if(anst>ans2){
            ans2=anst;
            ans1=c[i];
        }
        else if(anst==ans2 && c[i]<ans1){
            ans1=c[i];
        }
    }
    cout<<ans1<<" "<<ans2<<endl;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值