The 15th Zhejiang Provincial Collegiate Programming Contest 题解

A题题目链接

题意:给一个数组请问这个数组是否符合先升序后降序,是输出Yes否则输出No。

思路:直接遍历数组判断

C++代码:

#include
using namespace std;
const int maxn = 100010;

int n,a[maxn];

int main()
{
    int T; scanf ( "%d" , &T );
    while ( T-- )
    {
        scanf ( "%d" , &n );
        for ( int i=1 ; i<=n ; i++ )
            scanf ( "%d" , &a[i] );
        bool ok1 = false,ok2 = true;
        if ( a[1]>=a[2] )
        {
            ok1 = false;
            ok2 = true;
        }
        else
        {
            for ( int i=3 ; i<=n ; i++ )
            {
                if ( a[i]==a[i-1] )
                {
                    ok1 = false;
                    ok2 = true;
                    break;
                }
                else if ( a[i]a[i-1] )
                {
                    if ( ok1==true )
                        ok2 = false;
                }
            }
        }
        if ( ok1&&ok2 )
            printf ( "Yes\n" );
        else
            printf ( "No\n" );
    }
    return 0;
}

B题题目链接

题意:给两个数组D和S,可以给数组D的每一位加上K,请问加上K后D数组和S数组对应位置数字相同的最大值是多少。

思路:直接做差,然后求做差后得到的结果数组的众数。

C++代码:

#include
using namespace std;
const int maxn = 100010;

int n,a[maxn],b[maxn],c[maxn];

int main()
{
    int T; scanf ( "%d" , &T );
    while ( T-- )
    {
        scanf ( "%d" , &n );
        for ( int i=1 ; i<=n ; i++ )
            scanf ( "%d" , &a[i] );
        for ( int i=1 ; i<=n ; i++ )
            scanf ( "%d" , &b[i] );
        for ( int i=1 ; i<=n ; i++ )
            c[i] = b[i]-a[i];
        sort ( c+1 , c+n+1 );
        int ans = 0;
        for ( int i=1 ; i<=n ; i++ )
        {
            int l=i,r=i;
            while ( r+1<=n&&c[r+1]==c[l] ) r++;
            ans = max ( ans , r-l+1 );
            i=r;
        }
        printf ( "%d\n" , ans );
    }
    return 0;
}

C题题目链接

题目背景:将印有数字1-12的牌各4张均分到12堆,每堆各4张。从第一堆开始取牌,取到数字几就到第几堆继续取牌。直到想去某堆取牌但是那堆牌却为空停止。

题意:在保证游戏未结束的情况下,给出前n步操作 ,要求输出当游戏结束时各堆为空的概率为多少(分数表示)。

思路:首先有个结论:最终游戏结束一定是想去第一堆取牌而第一堆没牌可取。(证明:每种数字牌只有4张,且除了第一堆外其他堆都有4张牌,即除了第一堆外其他堆都有足够的牌提供给那4次取牌。)假设进行n次抽牌后1剩余x张,2剩余y张,那么2被抽空的概率为x/(x+y)。(证明:如果将每张牌都看成独立的,那么每张牌被抽的概率相同。那么y张牌先于x张牌抽空的概率为x/(x+y))

C++代码:

#include
using namespace std;

int n,a[110];
char str[10];

int GCD( int a , int b )
{
    if ( b==0 ) return a;
    else return GCD( b , a%b );
}

int main()
{
    int T; scanf ( "%d" , &T );
    while ( T-- )
    {
        scanf ( "%d" , &n );
        for ( int i=1 ; i<=12 ; i++ )
            a[i] = 4;
        for ( int i=1 ; i<=n ; i++ )
        {
            scanf ( "%s" , str );
            if ( str[0]=='A' ) a[1]--;
            else if ( str[0]=='J' ) a[11]--;
            else if ( str[0]=='Q' ) a[12]--;
            else if ( str[1]!='\0' ) a[10]--;
            else a[str[0]-'0']--;
        }
        printf( "1" );
        for ( int i=2 ; i<=12 ; i++ )
        {
            int x = a[1];
            int y = a[1]+a[i];
            if ( x==y ) printf( " 1" );
            else if ( x==0 ) printf ( " 0" );
            else
            {
                int gcd = GCD( x , y );
                printf( " %d/%d" , x/gcd , y/gcd );
            }
        }
        printf( "\n" );
    }
    return 0;
}

D题题目链接

题意:给一个仅包含左右括号且长度为n的字符串,每个字符有对应的权值。可以通过交换相邻的左右括号来获得权值乘积的分数,求最终可以获得的分数最大值。

思路:首先有个结论:对于一个左括号它可以和后面的所有右括号进行交换。于是乎就可以通过DP求解该问题了dp[i][j]表示在i这个位置的左括号匹配大于等于j个右括号可以得到的最大值。

C++代码:

#include
using namespace std;
typedef long long LL;
const int maxn = 1010;

int n;
char s[maxn];
LL a[maxn];
LL b[maxn];
LL dp[maxn][maxn];

int main()
{
    int T; scanf ( "%d" , &T );
    while ( T-- )
    {
        scanf ( "%d%s" , &n , s );
        for ( int i=0 ; i=0 ; i-- )
        {
            if ( s[i]=='(' )
            {
                for ( int j=r-1 ; j>i ; j-- )
                    b[q++] = a[j];
                dp[i][0] = dp[r][0];
                LL tmp = 0;
                for ( int k=1,j=q-1 ; k<=q ; k++,j-- )
                {
                    tmp =  tmp + a[i]*b[j];
                    dp[i][k] = tmp + dp[r][max(0,(k-(r-i-1)))];
                }
                for ( int k=q-1 ; k>=0 ; k-- )
                    dp[i][k] = max ( dp[i][k] , dp[i][k+1] );
                r = i;
                ans = max ( ans , dp[i][0] );
            }
        }
        printf ( "%lld\n" , ans );
    }
    return 0;
}

E题题目链接

题意:有一个长度为n的序列f[],f[i]表示以第i个数为结尾的最长上升序列长度为f[i]。还给出了各个位置上的数据范围l[i],r[i]要求该位置上的数应该在此范围内。求任意一个可行解。

思路:发现题目中有不等式可用,所以我们采用差分约束来解决此题。定义dis[]数组表示该点的最终答案,即原序列。以0为原点,于是乎会有以下不等式产生。dis[i]-dis[0]<=r[i],dis[i]-dis[0]>=l[i],此外因为题目给的数据范围不尽相同,所以还需要以下不等式dis[last[f[i]]-dis[i]>=0,dis[last[f[i]+1]]-dis[i]>=1。通过以上不等式建图spfa最短路,即可得到答案。

C++代码:

#include
using namespace std;
const int maxn = 100010;
const int maxm = 500010;
const int inf  = 0x7FFFFFFF;

int n,x,last[maxn],tol,head[maxn];
struct edge
{
    int to,cost,next;
}es[maxm];

void init()
{
    tol = 0; memset ( head , -1 , sizeof(head) );
}

void addedge( int u , int v , int w )
{
    es[tol].to = v;
    es[tol].cost = w;
    es[tol].next = head[u];
    head[u] = tol++;
}

int dis[maxn];
int vis[maxn];

void spfa()
{
    for ( int i=1 ; i<=n ; i++ )
        dis[i] = inf,vis[i] = 0;
    queueQ;
    Q.push(0);
    vis[0] = 1;
    dis[0] = 0;
    while ( !Q.empty() )
    {
        int u = Q.front();
        Q.pop();
        vis[u] = 0;
        for ( int i=head[u] ; i!=-1 ; i=es[i].next )
        {
            int v = es[i].to,w = es[i].cost;
            if ( dis[v]>dis[u]+w )
            {
                dis[v] = dis[u]+w;
                if ( !vis[v] )
                {
                    vis[v] = 1;
                    Q.push(v);
                }
            }
        }
    }
}

int main()
{
    int T; scanf ( "%d" , &T );
    while ( T-- )
    {
        init(); scanf ( "%d" , &n );
        memset ( last , 0 , sizeof(last) );
        for ( int i=1 ; i<=n ; i++ )
        {
            scanf ( "%d" , &x );
            if ( last[x] )
                addedge( last[x] , i , 0 );
            if ( last[x-1] )
                addedge( i , last[x-1] , -1 );
            last[x] = i;
        }
        for ( int i=1 ; i<=n ; i++ )
        {
            int l,r; scanf ( "%d%d" , &l , &r );
            addedge( 0 , i ,  r );
            addedge( i , 0 , -l );
        }
        spfa();
        for ( int i=1 ; i<=n ; i++ )
        {
            if ( i!=1 ) printf ( " " );
            printf ( "%d" , dis[i] );
        }
        printf ( "\n" );
    }
    return 0;
}

F题题目连接

题意:对题目所给公式进行求和并对1e9进行取模。

思路:对ai进行排序,然后由于底数最大不超过30所以考虑预处理ai/底数的结果并进行前缀和求和,然后对于询问给定p不断规定左右界进行二分,然后利用前缀和快速求解。

C++代码:

#include
using namespace std;
const int maxn = 500010;
const int mod  = 1e9;

int n,m;
int a[maxn];
int b[35][maxn];


int main()
{
    int T; scanf ( "%d" , &T );
    while ( T-- )
    {
        scanf ( "%d%d" , &n , &m );
        for ( int i=1 ; i<=n ; i++ )
            scanf ( "%d" , &a[i] );
        sort ( a+1 , a+n+1 );
        for ( int i=1 ; i<=30 ; i++ )
        {
            b[i][0] = 0;
            for ( int j=1 ; j<=n ; j++ )
            {
                b[i][j] = a[j]/i;
                b[i][j] = ( b[i][j]+b[i][j-1] )%mod;
            }
        }
        int ans = 0;
        for ( int i=1 ; i<=m ; i++ )
        {
            int x; scanf ( "%d" , &x );
            int L=1,R=x,sum=0,t=1;
            while ( 1 )
            {
                int l = upper_bound( a+1 , a+n+1 , L )-a;
                int r = upper_bound( a+1 , a+n+1 , R )-a;
                sum = ( sum+(b[t][r-1]-b[t][l-1]) )%mod;
                if ( a[n]/L

G题题目链接

题意:

定义两个非空字符串x,y和另外一个非空字符串z,若xz==zy,则称z是符合要求的字符串

给两个字符串s,t,长度分别为n和m,有q组询问每次询问给两个整数x,y,求符合sx,sx+1,。。。,sx+m-1和t要求的且长度不超过y的字符串z的个数

思路:

C++代码:

#include
using namespace std;
typedef long long LL;
const int maxn = 100010;
const int mod1 = 1e9+7;
const int mod2 = 1e9+9;
const int base1 = 131;
const int base2 = 233;

LL p1[maxn],p2[maxn];
LL Hash1[maxn];
LL Hash2[maxn];
int n,m,q,Next[maxn];
char a[maxn],b[maxn];
map,int>mp;

pair GetHash( int l , int r )
{
    LL h1 = ( Hash1[r]-Hash1[l-1]*p1[m]%mod1+mod1 )%mod1;
    LL h2 = ( Hash2[r]-Hash2[l-1]*p2[m]%mod2+mod2 )%mod2;
    return make_pair( h1 , h2 );
}

int main()
{
    p1[0] = 1;
    p2[0] = 1;
    for ( int i=1 ; ih;
        for ( int i=1 ; i<=m ; i++ )
        {
            LL h1 = ( (h.first*base1 )%mod1+(LL)b[i] )%mod1;
            LL h2 = ( (h.second*base2)%mod2+(LL)b[i] )%mod2;
            h = make_pair( h1 , h2 );
        }
        int cnt = 0;
        mp.clear();
        for ( int i=1 ; i<=m ; i++ )
        {
            LL h1 = ( (h.first*base1 )%mod1+(LL)b[i] )%mod1;
            LL h2 = ( (h.second*base2)%mod2+(LL)b[i] )%mod2;
            h = make_pair( (h1-(LL)b[i]*p1[m]%mod1+mod1)%mod1 , (h2-(LL)b[i]*p2[m]%mod2+mod2)%mod2 );
            if ( !mp[h] ) mp[h] = i;
        }
        while( q-- )
        {
            int x; LL y;
            scanf ( "%d%lld" , &x , &y );
            h = GetHash( x , x+m-1 );
            if ( !mp[h] )
                printf ( "0\n" );
            else
                printf ( "%lld\n" , (y-mp[h]+loop)/loop );
        }
    }
    return 0;
}

H题题目链接

题意:

思路:

C++代码:

#include
using namespace std;
const int maxn = 1010;
typedef long long LL;

int tol,head[maxn];
struct edge
{
    int to,next; LL cost;
}es[maxn];

LL sizes[maxn];
LL indeg[maxn];
LL dis[maxn];
LL dp[maxn][2];

void init()
{
    tol = 0;
    memset ( head , -1 , sizeof(head) );
    memset ( sizes , 0 , sizeof(sizes) );
    memset ( indeg , 0 , sizeof(indeg) );
    memset ( dis , 0 , sizeof(dis) );
    memset ( dp , 0 , sizeof(dp) );
}

void addedge( int u , int v , int w )
{
    es[tol].to = v;
    es[tol].cost = w;
    es[tol].next = head[u];
    head[u] = tol++;
}

void dfs( int u , int f )
{
    if ( indeg[u]<=1 )
        sizes[u] = 1;
    else
        sizes[u] = 0;
    LL cnt = 0;
    for ( int i=head[u] ; i!=-1 ; i=es[i].next )
    {
        int v = es[i].to; LL w = es[i].cost;
        if ( v==f ) continue;
        dis[v] = dis[u]+w;
        dfs( v , u );
        LL val;
        sizes[u] += sizes[v];
        if ( dis[v]+dp[v][0]

I题题目链接

题意:4n-4个点,从0开始编号,从(0,0)按逆时针开始逆时针放置,现在要求连接n条直线(n条直线都至少过两点并且不能平行于x轴和y轴),使得n条直线的交点尽量的多。

思路:先连接(0,n),(1,n+1)...(n-2,2*n-2)这n-1条直线,这n-1条直线一定两两相交且斜率不想等,故任意两个交点一定不重合。最后第n条直线,只需非对角线且斜率的绝对值与之前n-1条直线都不相等即可,比如连接(n-1,3*n-2)或(n-1,3*n-4)。特别的,当n==2时需要特判。

C++代码:

#include
using namespace std;

int main()
{
    int T; scanf( "%d" , &T );
    while ( T-- )
    {
        int n; scanf( "%d" , &n );
        if ( n==2 )
            printf( "0 2 1 3\n" );
        else
        {
            for ( int i=0 ; i

J题题目链接

题意:一个长度为n的01串,0表示女生,1表示男生,现在要求将这些女生和男生分到四个组里面分别为G1,G2,G3,G4,其中G1,G2只能为女生G3,G4只能为男生,要求G1+G3的权值等于G2+G4的权值。第i个位置上的人拥有i点权值,输出任意满足要求的解。若无解则输出-1。

思路:首先可以简单的知道只有在n%4==0或n%4==3的情况下,才可能有解。

1.对于偶数情况,即n%4==0的情况:我们只要1,n,3,n-2,。。。分到一组,其余分给另一组即可。

2.对于奇数情况,即n%4==3的情况:我们先将1,n/2分给第一组,n/2+1分给另外一组,其余按偶数情况分开即可。

C++代码:

#include
using namespace std;
const int maxn = 100010;

char s[maxn];

int main()
{
    int T; scanf ( "%d" , &T );
    while ( T-- )
    {
        int n; scanf ( "%d%s" , &n , &s );
        if ( n%4==3 )
        {
            int m = n/2;
            if ( s[0]=='0' )
                printf( "1" );
            else
                printf( "3" );
            for ( int i=1,j=0 ; i

K题题目链接

题意:给出n块不同的麻将,有四种不同种类分别是C:万,B:条,D:筒,W:白板。优先级顺序为C

思路:因为题目已经保证初始局面一定合法且至少有一种满足置换,所以我们可以先通过判断是否有逆序情况来判断是不是只有唯一解。若发现数据无逆序,我们下一步去寻找是否有白板。若无白板则答案为3*m-n+1即没出现过的牌或者头张牌,若有白板则考虑去寻找白板的位置根据白板位置不同有如下情况:

白板在开头:答案为第二块牌的大小-1

白板夹在两牌中间:答案为前后两牌差的大小-1

白板在结尾:答案为3*m-倒数第二块牌的大小

最后注意白板在第二位置上时,答案需要+1

C++代码:

#include
using namespace std;
const int maxn = 100010;

int n,m,x,w;
int a[maxn];

bool ok()
{
    w = 0;
    for ( int i=1 ; i

L题题目链

题意:有n个字符串,每个字符串有对应的权值x,在其中选取m个字符串,要求ans通过计算公式for(i=0;i

思路:贪心,取前m个字符串。

C++代码:

#include
using namespace std;

int n,m;
struct node
{
    char s[20]; int x;
    friend bool operator<( const node&a , const node&b )
    {
        if ( a.x==b.x )
            return strcmp( a.s , b.s )<0;
        else
            return a.x>b.x;
    }
}A[110];

int main()
{
    int T; scanf( "%d" , &T );
    while ( T-- )
    {
        scanf( "%d%d" , &n , &m );
        for ( int i=0 ; i

M题题目链接

题意:有n个数字和一个整数b,问这n个数字中是否存在一个数字(xi+b)%7==0,有输出Yes,无则输出No。

思路:直接判断。

C++代码:

#include
using namespace std;


int main()
{
    int T; scanf( "%d" , &T );
    while ( T-- )
    {
        int n,b,x,f=0;
        scanf( "%d%d" , &n , &b );
        for ( int i=0 ; i

你可能感兴趣的:(套题)