The 2017 ACM-ICPC Asia Hong Kong Regional Contest 一些题解

D - Card collection
送温暖的签到题,裸的最短路。点数很少,只要Floyd就可以
E - Base Station Sites
签到题,最大值最小明显是二分问题,直接二分最终的位置,每次判断一下能不能放完就行
G - Optimal Coin Change
比较简单的dp,首先我们利用完全背包,维护一个最少的使用硬币的个数,在此基础上,我们维护一个答案数组c[i][j] 代表钱数为i的时候,第j种硬币最少i的使用个数,每次转移的时候我们将答案向新的答案更新

#include<bits/stdc++.h>
#include<cstring>
#define ll long long
using namespace std;
#define INF 0x3f3f3f3f
int a[20];
int dp[2010];
int c[2010][20];
int main()
{
    //freopen("input.txt","r",stdin);
    int n,v;
    while(~scanf("%d%d",&v,&n))
    {
        for(int i=1;i<=n;++i)
            scanf("%d",&a[i]);
        memset(dp,INF,sizeof(dp));
        memset(c,0,sizeof(c));
        dp[0]=0;
        for(int i=1;i<=n;++i)
            for(int j=0;j<=v;++j)
            if(j+a[i]<=v)
            {
                if(dp[j+a[i]]>=dp[j]+1)
                {
                    dp[j+a[i]]=dp[j]+1;
                   // dp[i][i]++;
                   for(int k=1;k<=n;++k)
                   {
                       c[j+a[i]][k]=c[j][k];
                   }
                   c[j+a[i]][i]++;
                }
             //   dp[j+a[i]]=min(dp[j]+1,dp[j+a[i]]);

            }
        if(dp[v]==INF)
            puts("-1");
        else
        {
            for(int i=1;i<n;++i)
               printf("%d ",c[v][i]);
            printf("%d\n",c[v][n]);
        }
    }   
    
    return 0;
}

F - Nearby Bicycles
水题,直接暴力就行,最好不要用sqrt,直接longlong不开方判断就行
I - Count the Even Integers
java大数+找规律。首先我们不用将杨辉三角全部打出来,只要将奇偶表示成01,然后按照异或规则打个表,会发现打出来一个很漂亮的图形。其中大三角和小三角互相组合,我们预处理小的三角的答案个数,然后递归一下去计算


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

	
	static BigInteger a[] = new BigInteger[201];
	static BigInteger p[] = new BigInteger[201];
	static BigInteger TWO = new BigInteger("2");
	static BigInteger TRI = new BigInteger("3");
	public static  BigInteger fun(BigInteger n)
	{
		int x = 0;
		for(int i = 0;i<201;i++)
		{
			if(n.compareTo(p[i]) < 0) 
			{
				x = i-1;break;
			}
		}
		//System.out.print(x);
		BigInteger t = n.subtract(p[x]);
		if(t.compareTo(BigInteger.ZERO) == 0) return a[x];
		BigInteger res = t.multiply(p[x].add(p[x]).subtract(BigInteger.ONE).subtract(t)).divide(TWO);
		return a[x].add(fun(t).multiply(TWO)).add(res);
	}
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		p[0] = BigInteger.ONE;

		for(int i = 1;i<200;i++)
		{
			p[i] = p[i-1].multiply(TWO);
		}
		a[0] = a[1] = BigInteger.ZERO;
		for(int i = 2;i<200;i++)
		{
			BigInteger t = p[i-1].multiply(p[i-1].subtract(BigInteger.ONE)).divide(TWO);
			a[i] = a[i-1].multiply(TRI).add(t);
		}
		Scanner sc = new Scanner(System.in);
		BigInteger n;
		while(sc.hasNext())
		{
			n = sc.nextBigInteger();
			System.out.println(fun(n.add(BigInteger.ONE)));
		}
		
		
	}

}

B - Black and White
是一个线段树求矩形交的改一改做法,我们记个标记代表这个点有没有被反转。反转了一定要将区间值同时反转

#include <bits/stdc++.h>
#define ll long long
#define pr pair<int,int>
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define next fuck
using namespace std;
const int MAXN = 1e5 + 5;
#define ls rt<<1
#define rs rt<<1|1
#define lson l,mid,ls
#define rson mid+1,r,rs
int cnt[MAXN],sum[MAXN],len[MAXN];
struct Seg
{
    int l,r,h,d;
    Seg(){}
    Seg(int _l,int _r,int _h,int _d) {l = _l,r = _r,h = _h,d = _d;}
    bool operator < (const Seg &rhs) const
    {
        return h < rhs.h;
    }
}a[MAXN];
void update_rev(int rt)
{
    cnt[rt] ^= 1;
    sum[rt] = len[rt] - sum[rt];
}
void push_down(int rt)
{
    if(cnt[rt])
    {
        update_rev(ls);
        update_rev(rs);
        cnt[rt] = 0;
    }
}
void update(int L,int R,int c,int l,int r,int rt)
{
    if(L <=l && r <= R)
    {
        update_rev(rt);
        return ;
    }
    push_down(rt);
    int mid = (l + r) >> 1;
    if(L <= mid) update(L,R,c,lson);
    if(R > mid) update(L,R,c,rson);
    sum[rt] = sum[ls] + sum[rs];
}
void build(int l,int r,int rt)
{
    cnt[rt] = sum[rt] = 0;
    len[rt] = r - l + 1;
    if(l == r) return ;
    int mid =(l + r)>> 1;
    build(lson);
    build(rson);
}

int n,k;
int main()
{
    int ca;
    scanf("%d",&ca);
    while(ca--)
    {
        scanf("%d%d",&n,&k);
        int tot = 0;
        for(int i = 1;i<=k;i++)
        {
            int x1,x2,y1,y2;
            scanf("%d%d%d%d",&x1,&x2,&y1,&y2);
            a[tot++] = Seg(x1,x2,y1,1);
            if(y2 < n) a[tot++] = Seg(x1,x2,y2+1,-1);
        }
        sort(a,a+tot);
        build(1,n,1);
        int ans = 0;
        for(int i = 1,j = 0;i<=n;i++)
        {
            while(j < tot && a[j].h == i)
            {
                update(a[j].l,a[j].r,1,1,n,1);
                j++;
            }
            ans += sum[1];
        }
        printf("%d\n",ans);
    }
    return 0;
}

A - Fermat’s Optimization Problem
给出n,z,求 f ( x , y , z ) = ∣ x n − y n − z n ∣ f(x,y,z) = |x^n-y^n-z^n| f(x,y,z)=xnynzn最小值。
感觉可以随机搞一发,但是发现n最大只有10,我们先预处理一下pow(JAVA大数的pow慢的绝望),然后枚举Y,用二分求出来当前Y下最小的X值,注意特判一下1的情况

import java.util.*;
import java.math.BigInteger;

public class Main
{
    static BigInteger pow[][] = new BigInteger[100005][12];
    public static void main(String[]args)
    {

        for(int i = 1;i<=100000;i++)
        {
            pow[i][0] = BigInteger.ONE;
            for(int j = 1;j<=10;j++)
            {
                pow[i][j] = pow[i][j-1].multiply(BigInteger.valueOf((long)i));
            }
        }
        String INF = "1";
        for(int i  =1;i<=60;i++) INF = INF+"0";
        Scanner sc = new Scanner(System.in);
        int ca = sc.nextInt();

        while(ca-- != 0)
        {
            int n = sc.nextInt(),z = sc.nextInt();
            int X = 0,Y = 0;
            BigInteger ans = new BigInteger(INF);
            for(int i = 2;i<z;i++)
            {
                BigInteger num = pow[1][n].add(pow[i][n]).subtract(pow[z][n]);
                if(num.compareTo(BigInteger.ZERO) >= 0)
                {
                    if(num.compareTo(ans) == -1) {
                        ans = num;
                        X = 1;
                        Y = i;
                        //System.out.println(X+" "+Y+" "+num);
                    }
                    continue;
                }
                int l = 1,r = i-1;
                while(l < r)
                {
                    int mid = (l+r+1>> 1);
                    num = pow[mid][n].add(pow[i][n]).subtract(pow[z][n]);
                    if(num.compareTo(BigInteger.ZERO) == -1)
                    {
                        l = mid;
                    }
                    else r = mid-1;
                }
                num = pow[l][n].add(pow[i][n]).subtract(pow[z][n]);
                num = num.multiply(BigInteger.valueOf(-1l));

                if(num.compareTo(ans) == -1)
                {
                    ans = num;
                    X = l;
                    Y = i;

                }

                if(l < i-1)
                {
                    l++;
                    num = pow[l][n].add(pow[i][n]).subtract(pow[z][n]);
                    if(num.compareTo(ans) == -1)
                    {
                        ans = num;
                        X = l;
                        Y = i;
                    }
                }
                //System.out.println(X+" "+Y+" "+num);
            }
            System.out.printf("%d %d ",X,Y);
            System.out.println(ans);
        }
        sc.close();
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ACM-ICPC(国际大学生程序设计竞赛)是一项面向大学生的计算机编程竞赛,涉及算法和数据结构等领域。在比赛中,选手需要解决一系列编程问题,使用合适的算法和数据结构来实现正确和高效的解决方案。 对于整理ACM-ICPC模板,以下是一些建议: 1. 了解比赛要求:首先,你需要了解ACM-ICPC比赛的具体要求和规则。这包括了解比赛所涉及的算法和数据结构,以及题目的类型和难度等。 2. 收集资料:收集与ACM-ICPC相关的资料,包括经典算法和数据结构的实现代码、常见问题的解题思路等。可以参考教材、博客、论文等资源。 3. 整理模板:将收集到的资料整理成模板。可以按照算法和数据结构的分类进行整理,例如排序算法、图算法、字符串算法等。对每个模板,添加必要的注释和示例代码,以便理解和使用。 4. 测试代码:对每个模板编写测试代码,确保它们的正确性和可靠性。可以使用已知的测试用例或自行设计测试用例。 5. 更新与扩充:定期更新和扩充模板,以适应ACM-ICPC比赛中新出现的算法和数据结构。同时,根据自己的经验和理解,对模板进行优化和改进。 6. 练习和复习:在比赛之前,利用整理好的模板进行练习和复习。尝试解决一些经典问题,使用模板中的算法和数据结构进行实现,并进行优化。 希望这些建议对你整理ACM-ICPC模板有所帮助!

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值