2017_SDNU_ACM-ICPC_Provincial_Team_Selection_Round_1【--完结--】

山东省第八届acm大学程序设计竞赛山师选拔赛第一场

(声明:标题是自己取的,如果有语法错误的话与他人无关)


-----------------------------------------------------------------------------------分割线-----------------------------------------------------------------------------------


Problem_A(大数判定2幂数)

题意:给定一个数,判断它是否是2的n次方(0<n<2^1000)

按二进制考虑的话,如果n&(n-1)==0,则这个数就是2的n次方,等于1就不是。

AC代码(JAVA版):

import java.math.BigInteger;
import java.util.*;
public class Main {
	public static void main(String[] args)
	{
		Scanner scanner = new Scanner(System.in);
		int T;
		BigInteger N;
		BigInteger ZERO = new BigInteger("0");
		BigInteger ONE = new BigInteger("1");
		
		T = scanner.nextInt();
		for(int i = 0; i < T; ++i)
		{
			N = scanner.nextBigInteger();
			BigInteger M = N.subtract(ONE);
			if(N.and(M).compareTo(ZERO) == 0) 
				System.out.println("Yes");
			else 
				System.out.println("No");
		}
	}

}

Problem_B(离散化裸题)

有些数据本身很大,自身无法作为数组的下标保存对应的属性。如果这时只是需要这堆数据的相对属性时,那么就可以对其进行离散化。所谓离散化就是指当数据只与它们之间的相对大小有关,而与具体是多少无关时可以用到的一种方法(?
举个例子来说,假设有4个数:1234567、123456789、12345678、123456排序后是123456<1234567<12345678<123456789(只考虑他们相对大小可以想为1<2<3<4),那么这四个数可以表示成:2、4、3、1。
对数据进行离散化如果用上STL会很棒棒哦(思路:先排序,再去重,然后索引元素离散化后对应的值,详见代码)。
AC代码:
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int N = 2002;
bool flag[N][N];
long long x[N], y[N];
long long xx[N], yy[N];
int main()
{
    int n;
    scanf("%d",&n);
    memset(flag, false, sizeof(flag));
    for(int i = 1; i <= n; ++i)
    {
        scanf("%lld%lld%lld%lld",&xx[i],&yy[i],&xx[i+n],&yy[i+n]);
        //输入的同时对数据进行离散化
        x[2*i] = xx[i+n];
        y[2*i] = yy[i+n];
        x[2*i-1] = xx[i];
        y[2*i-1] = yy[i];
    }
    //排序去重
    sort(x+1, x+1+2*n);
    sort(y+1, y+1+2*n);
    unique(x+1, x+1+2*n);
    unique(y+1, y+1+2*n);

    //索引元素离散化后对应的值
    for(int i = 1; i <= 2*n; ++i)
    {
        xx[i] = upper_bound(x+1, x+1+2*n, xx[i]) - (x+1);
        yy[i] = upper_bound(y+1, y+1+2*n, yy[i]) - (y+1);
    }

    for(int k = 1; k <= n; ++k)
        for(int i = xx[k]+1; i <= xx[k+n]; ++i)    //从xx[k]+1开始
            for(int j = yy[k]+1; j <= yy[k+n]; ++j)//从yy[k]+1开始
                flag[i][j] = true;

    long long ans = 0;
    for(int i = 2; i <= 2*n; ++i)
        for(int j = 2; j <= 2*n; ++j)
            if(flag[i][j])
                ans += (x[i]-x[i-1])*(y[j]-y[j-1]);//是y[j]-y[j-1]

    cout << ans << endl;
    return 0;
}


Problem_C(最长回文子串)

题意:给定一序列,输出其最长回文子序列的长度。

思路:没有思路,Manacher模板一套带走。

AC代码:

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int MAX = 1e6+5;
char MA[MAX*2];
int MP[MAX*2];
void Manacher(char s[], int len)
{
    int l = 0;
    MA[l++] = '$';
    MA[l++] = '#';
    for(int i = 0; i < len; ++i)
    {
        MA[l++] = s[i];
        MA[l++] = '#';
    }
    MA[l] = 0;
    int mx = 0, id = 0;
    for(int i = 0; i < l; ++i)
    {
        MP[i] = mx > i ? min(MP[2*id-i], mx-i) : 1;
        while(MA[i+MP[i]] == MA[i-MP[i]]) MP[i]++;
        if(i+MP[i]>mx)
        {
            mx = i + MP[i];
            id = i;
        }
    }
}
char s[MAX];
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%s",s);
        int len = strlen(s);
        Manacher(s, len);
        int ans = 0;
        for(int i = 0; i < 2*len+2; ++i)
            ans = max(ans, MP[i]-1);
        cout << ans << endl;
    }
    return 0;
}


Problem_D(多重背包)

题意:有n个面值为A1,A2,..An,数量为C1,C2,..Cn的n个硬币,问他们之间互相组合能凑出多少种总面额小于m的面值

(可参照POJ的男人八题之Coins

AC代码:

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int MAX = 100000 + 5;
int A[105], C[105];
int DP[MAX];  //面额为A[i]的硬币在DP[i]位置用过的个数
bool vis[MAX];//能凑成的面额
int main()
{
    int N, M;
    while(scanf("%d%d",&N,&M) && (N||M))
    {
        memset(vis, false, sizeof(vis));
        for(int i = 1; i <= N; ++i)
            scanf("%d",&A[i]);
        for(int i = 1; i <= N; ++i)
            scanf("%d",&C[i]);
        vis[0] = true;
        for(int i = 1; i <= N; ++i)
        {
            memset(DP, 0, sizeof(DP));
            for(int j = 1; j <= M; ++j)
            {
                //没有凑成过并且现在硬币的面额比要凑的面额大
                if(vis[j] || j < A[i]) continue;
                if(vis[j-A[i]] && DP[j-A[i]]<C[i])
                {
                    vis[j] = true;
                    //cout << j << "  ";
                    DP[j] = DP[j-A[i]] + 1;
                }
            }
        }
        //cout << endl;
        int ans = 0;
        for(int i = 1; i <= M; ++i)
            if(vis[i]) ans++;
        cout << ans << endl;
    }
    return 0;
}


Problem_E(思维 签到)

题意:给定n个数,从中抽取3个,将它们分成4组,问能否使每组的和相同。能的话输出抽取的三个数的位置,不能就输出I am done.
思路:设置i,j,k三个指针,一开始放在2,4,6这三个位置,然后不断判断分成的四组数据和是否相同,不同就找出其中最小的那一组,让他后面的指针往后移动。还有些小细节,比如k指针到头了,四组的值都相同了等等,自己处理一下就好了。
AC代码:
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int MAX = 1005;
int a[MAX];
int MIN(int a, int b, int c, int d)
{
    if(a <= b && a <= c && a <= d) return 1;
    if(b <= a && b <= c && b <= d) return 2;
    if(c <= a && c <= b && c <= d) return 3;
    if(d <= a && d <= b && d <= c) return 4;
    return 0;
}
int main()
{
    int n;
    int x, y, z;
    int ans1, ans2, ans3, ans4;
    while(~scanf("%d",&n))
    {
        for(int i = 0; i < n; ++i)
            scanf("%d",&a[i]);
        bool flag = 1;
        if(n < 7) flag = 0;
        if(flag)
        {
            ans1 = ans2 = ans3 = ans4 = 0;
            x = 1; y = 3; z = 5;
            ans1 = a[0]; ans2 = a[2]; ans3 = a[4];
            for(int i = 6; i < n; ++i)
                ans4 += a[i];
            while(flag)
            {
                if(ans1 == ans2 && ans1 == ans3 && ans1 == ans4) break;
                int ans = MIN(ans1, ans2, ans3, ans4);
                switch(ans)
                {
                    case 0:
                    case 4: flag = 0; break;
                    case 1: x++;
                    case 2: y++;
                    case 3: z++; break;
                }
                ans1 = ans2 = ans3 = ans4 = 0;
                for(int i = 0; i < x; ++i) ans1 += a[i];
                for(int i = x+1; i < y; ++i) ans2 += a[i];
                for(int i = y+1; i < z; ++i) ans3 += a[i];
                for(int i = z+1; i < n; ++i) ans4 += a[i];
            }
        }
        if(flag) cout << x+1 << " " << y+1 << " " << z+1 << endl;
        else cout << "I am done." << endl;
    }
    return 0;
}


Problem_F(八进制减法)

题意:八进制减法

AC代码(C++--模拟)

/*************
Author:E6ther
*************/
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
int main()
{
    int t;
    while(~scanf("%d",&t))
    {
        getchar();
        for(int j=0;j<t;++j)
        {

            char a[105],b[105];
            int len1=0,len2=0;
            bool flag=1;
            memset(a,0,sizeof(a));
            memset(b,0,sizeof(b));
            char c;
            while((c=getchar())!=' '&&c!='\n')
            {
                a[len1]=c-'0';
                len1++;
            }
            a[len1]=0;
            while((c=getchar())!=' '&&c!='\n')
            {
                b[len2]=c-'0';
                len2++;
            }
            b[len2]=0;

            /*for(int i=0;i<len1;++i)
            {
                printf("%d",a[i]);
            }cout<<endl;
            for(int i=0;i<len2;++i)
            {
                printf("%d",b[i]);
            }cout<<endl;*/

            //printf("%s %s\n",a,b);
            if(len1>len2)
            {
                flag=1;
            }
            else if(len1<len2)
            {
                flag=0;
            }
            else
            {
                for(int i=0;i<len1;++i)
                {
                    if(a[i]>b[i])
                    {
                        flag=1;
                        break;
                    }
                    else if(a[i]<b[i])
                    {
                        flag=0;
                        break;
                    }
                }
            }
            if(flag)
            {
                for(int i=len2-1,j=len1-1;i>=0;--i,--j)
                {
                    if(a[j]>=b[i])
                    {
                        a[j]=a[j]-b[i];
                    }
                    else
                    {
                        a[j]=a[j]+8-b[i];
                        if(a[j-1])
                            a[j-1]--;
                        else
                        {
                            int flag2=0;
                            for(int x=j-2;x>=0;--x)
                            {
                                if(a[x])
                                {
                                    a[x]--;
                                    flag2=1;
                                }
                                a[x+1]=7;
                                if(flag2) break;
                            }
                        }
                    }
                }
            }
            else
            {
                for(int i=len1-1,j=len2-1;i>=0;--i,--j)
                {
                    if(a[i]<=b[j])
                    {
                        b[j]=b[j]-a[i];
                    }
                    else
                    {
                        b[j]=b[j]+8-a[i];
                        if(b[j-1])
                        b[j-1]--;
                        else
                        {
                            int flag2=0;
                            for(int x=j-2;x>=0;--x)
                            {
                                if(b[x])
                                {
                                    b[x]--;
                                    flag2=1;
                                }
                                b[x+1]=7;
                                if(flag2) break;
                            }
                        }
                    }
                }
            }
            bool flag1=0;
            if(flag)
            {
                for(int i=0;i<len1;++i)
                {
                    if(a[i]) flag1=1;
                    if(flag1)
                    printf("%d",a[i]);
                }
                if(!flag1) cout<<"0";
            }
            else
            {
                cout<<"-";
                for(int i=0;i<len2;++i)
                {
                    if(b[i]) flag1=1;
                    if(flag1)
                    printf("%d",b[i]);
                }
            }
            cout<<endl;
        }
    }
    return 0;
}


AC代码(JAVA):

import java.math.BigInteger;
import java.util.*;
public class Main {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		int T;
		Scanner scanner = new Scanner(System.in);
		T = scanner.nextInt();
		BigInteger N, M, X, Y;
		
		for(int i = 0; i < T; ++i)
		{
			N = scanner.nextBigInteger();
			M = scanner.nextBigInteger();
			X = new BigInteger(N.toString(),8);
			Y = new BigInteger(M.toString(),8);
			X = X.subtract(Y);
			System.out.println(X.toString(8));
		}
	}

}


Problem_G(模拟?)

题意:给你n个数,输出对他们进行第一次快速排序后的结果。不知道快速排序的话,自己去搜(其实不知道也没关系,题目里已经给出了排序的方式了,按照它的来就是了)。

AC代码:

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int MAX = 10005;
int a[MAX];
void solve(int n)
{
    int tem = a[0];
    int l = 0, r = n;
    while(l <= r)
    {
        for(--r; l <= r; --r)
        {
            if(a[r] < tem)
            {
                a[l] = a[r];
                break;
            }
        }
        for(++l; l <= r; ++l)
        {
            if(a[l] > tem)
            {
                a[r] = a[l];
                break;
            }
        }
    }
    a[--l] = tem;
}
int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        memset(a, 0, sizeof(a));
        for(int i = 0; i < n; ++i)
            scanf("%d",&a[i]);
        solve(n);
        for(int i = 0; i < n; ++i)
        {
            if(i) cout << " ";
            cout << a[i];
        }
        cout << endl;
    }
    return 0;
}


Problem_H(大素数判定)

题意:给你n个数,输出他们中素数的个数。要用到Miller-rabin算法,套个模板就好了。

AC代码:

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <ctime>
using namespace std;
const int S = 8;//随机算法判定次数,一般8~10次就够了

//计算ret = (a*b)%c  a, b, c < 2^63
long long mult_mod(long long a, long long b, long long c)
{
    a %= c;
    b %= c;
    long long ret = 0;
    long long tem = a;
    while(b)
    {
        if(b & 1)
        {
            ret += tem;
            if(ret > c) ret -= c;//直接取模慢很多
        }
        tem <<= 1;
        if(tem > c) tem -= c;
        b >>= 1;
    }
    return ret;
}

//计算 ret = (a^n) % mod
long long pow_mod(long long a, long long n, long long mod)
{
    long long ret = 1;
    long long tem = a % mod;
    while(n)
    {
        if(n & 1) ret = mult_mod(ret, tem, mod);
        tem = mult_mod(tem, tem, mod);
        n >>= 1;
    }
    return ret;
}

// 通过 a^(n-1)=1(mod n)来判断n是不是素数
// n-1 = x * 2^t 中间使用二次判断
// 是合数返回true,不一定是合数返回false
bool check(long long a, long long n, long long x, long long t)
{
    long long ret = pow_mod(a, x, n);//a^x % n
    long long last = ret;
    for(int i = 1; i <= t; ++i)//进行t次(a^x % n)^2 % n
    {
        ret = mult_mod(ret, ret, n);
        if(ret == 1 && last != 1 && last != n - 1) return true;//合数
        last = ret;
    }
    if(ret != 1) return true;
    return false;//不一定是合数
}

//**************************************************
// Miller_Rabin算法
// 是素数返回true,(可能是伪素数)
// 不是素数返回false
//**************************************************
bool Miller_Rabin(long long n)
{
    if(n < 2) return false;
    if(n == 2) return true;
    if( (n&1) == 0) return false;//偶数
    long long x = n - 1;
    long long t = 0;
    while( (x&1) == 0) //将n分解为x*2^t;
    {
        x >>= 1;
        t++;
    }

    srand( time(NULL));

    for(int i = 0; i < S; ++i)
    {
        long long a = rand()%(n-1) + 1;//产生随机数a(并控制其范围在1 ~ n-1之间)
        if(check(a, n, x, t))//是合数
            return false;
    }
    return true;
}
int main()
{
    int t, ans;
    long long tem;
    scanf("%d",&t);
    ans = 0;
    while(t--)
    {
        scanf("%lld",&tem);
        if(Miller_Rabin(tem)) ans++;
    }
    cout << ans << endl;
    return 0;
}

因为预计这周周六会进行第二场选拔赛,本打算完了后两场一块写,可是那个ZZ说我不写博客直接给他代码,心里不踏实,所以只好牺牲自己的睡眠时间来写着“十分有用”的博客了。   祝你生活愉快。:)

(你还嫌我写的太敷衍?我这暴脾气。


另外,搬机房了,新机房更宽敞,条件也更好点(?,但是并不是很开心啊,虽然只在五楼呆了一学期多点吧,但就这样突然离开还是有些不舍的(大概是因为那里承载了太多的--几乎是目前的大学的全部回忆吧,不去纠结了,太重感情也不是件好事哦,哈哈。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值