2018.5.19——第九届acm山东省省赛部分题解E+G

E:sequence

题目描述

  We define an element  in a sequence "good", if and only if there exists  (1≤j<i) such that .    Given a permutation p of integers from 1 to n. Remove an element from the permutation such that the number of "good" elements is maximized.
输入描述:
The input consists of several test cases. The firstline of the input gives the number of test cases,.For each test case, the first line contains aninteger ,representing the length of the given permutation.The second line contains n integersp1,p2,…,pn ,representing the given permutation p.It's guaranteed that  .
输出描述:
For each test case, output one integer in a singleline, representing the element that should be deleted. If there are severalanswers, output the minimal one.
示例1
输入
2
1
1
5
5 1 2 3 4
输出
1

5

这道题的思路是维护一个到第i个点之前的最小值first[i]和到第i个点之前的次最小值secend[i],然后再循环判断i是否是good数,并进行标志,然后再一重循环,假设这个点被去掉了,会影响几个good值,用cnt函数进行记录,然后再进行判断,如果这个数不是good值并且cnt[i]==0,说明去掉这个数对good值无影响,那么就可以直接找出这样的代表数字最小的进行输出;如果没有这样的数字,那么我们就要着手于good值,因为good值对good数的影响只能是一,或者找非good数,并且他的cnt等于一,这样的数对good值也是一。

所以很多细节方面的需要考虑。比赛的时候我们没有想出来,可能是思维还是需要训练的。这道题就是一道需要考虑很多细节的思维题。

代码:

#include<bits/stdc++.h>
using namespace std;
int first[10000010];
int second[10000010];
int a[10000010];
int flag[10000010];
int cnt[10000010];
const int maxn=0x3f3f3f3f;
int ans;
int main()
{
    int t;
    int n;
    int k;
    scanf("%d",&t);
    while(t--){
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
        }
        int now_min=maxn;
        int last_min=maxn;
        for(int i=1;i<=n;i++){
            first[i]=now_min;
            second[i]=last_min;

            if(a[i]<=now_min){
                last_min=now_min;
                now_min=a[i];
               // flag[i]=1;
            }
            else if(a[i]<last_min){
                last_min=a[i];
            }
        }//维护最大值和次小值,jia标记,bad数标记为1
        for(int i=1;i<=n;i++)
        {//不好数标记为1,好数标记为0
            if(a[i]<=first[i])
                flag[i]=1;
            else
                flag[i]=0;
        }
        for(int i=1;i<=n;i++){
            if(flag[i]==1){//是
                k=i;
                cnt[k]=0;
            }
            else{
                if(a[i]<=second[i])
                    cnt[k]++;
            }
        }
       ans=maxn;
        for(int i=1;i<=n;i++){
            if(flag[i]==1&&cnt[i]==0)
                ans=min(ans,a[i]);
        }
        if(ans==maxn) 
        {
            for(int i=1;i<=n;i++)
            {
              if(flag[i]==0) 
                ans=min(ans,a[i]);
              else if(cnt[i]==1)
                ans=min(ans,a[i]);
            }
        }
        printf("%d\n",ans);
    }
}

G:

games

Alice and Bob are playing a stone game. There are n piles of stones. In each turn, a player can remove some stones from a pile (the number must be positive and not greater than the number of remaining stones in the pile). One player wins if he or she remove the last stone and all piles are empty. Alice plays first.    To make this game even more interesting, they add a new rule: Bob can choose some piles and remove entire of them before the game starts. The number of removed piles is a nonnegative integer, and not greater than a given number d. Note d can be greater than n, and in that case you can remove all of the piles.    Let ans denote the different ways of removing piles such that Bob are able to win the game if both of the players play optimally. Bob wants you to calculate the remainder of ans divided by109+7.
输入描述:
The first line contains an integer T, representingthe number of test cases.For each test cases, the first line are twointegers n and d, which are described above.The second line are n positive integersai,representing the number of stones in each pile.
输出描述:
For each test case, output one integer (modulo109+7.) in asingle line, representing the number of different ways of removing piles thatBob can ensure his victory.
示例1
输入

2
5 2
1 1 2 3 4
6 3
1 2 4 7 1 2
输出

2
5

这道题显然是nim博弈,结论就是异或值之和为0的先手输。所以有了这个结论就可以写了。

比赛时学长写了一发秒过,现在想来好骄傲啊。赛后重现赛自己写,写出了好多bug,WA了好多发。自己定义的longlong数组一直数组越界,好久才改的int。。还有最后mod的时候,结果要再一次mod啊,老是犯这种错误。

就是用dp[i][j][k]表示前i为选j个,异或值为k的种类数目。

状态转移方程为:

dp[i][j][k]=(dp[i-1][j][k^a[i]]%mod+dp[i-1][j-1][k]%mod)%mod;

代码:

#include<bits/stdc++.h>
using namespace std;
int a[1005];
int dp[1005][15][1030];
const int mod=1000000007;
int main()
{
    int t;
    scanf("%d",&t);
    int n,d;
    while(t--)
    {
    scanf("%d%d",&n,&d);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    memset(dp,0,sizeof(dp));
    dp[1][0][a[1]]=1;
    dp[1][1][0]=1;//前i个删除j个异或和为k
    long long ans=0;
    for(int i=2;i<=n;i++){
        for(int j=0;j<=min(n,min(i,d));j++){
                for(int k=0;k<=1024;k++){
        dp[i][j][k]=(dp[i-1][j][k^a[i]]%mod+dp[i-1][j-1][k]%mod)%mod;
                     //cout<<"$$$$$$$$$$"<<dp[i][j][k]<<endl;
                }
        }
    }
    for(int i=0;i<=min(d,n);i++){
        //cout<<dp[n][i][0]<<"*&*&*&*"<<endl;
        ans=(ans%mod+dp[n][i][0]%mod)%mod;
    }
    printf("%lld\n",ans);
        //cout<<ans<<endl;
    }
    return 0;
}

还有F题容斥的题还没有看。

ps:重现赛的时候真的好想念学长大佬啊。感觉闭着眼只用读题的感觉真好。好希望可以成为像他们那么厉害的努力的人。

猜你喜欢

转载自blog.csdn.net/xianpingping/article/details/80377745