2013年HDU多校聯合第三場解題報告

1007:The Unsolvable Problem

這道題很簡單,是簽到題。題意就是:給定一個數n,有兩個數a,b。使a+b=n。求所有(a,b)組合中a和b最小公倍數的最大值。

思路:1.如果n是奇數的話,那麼結果爲n/2*(n/2+1);2.如果n爲偶數:(1).n/2爲偶數:結果爲(n/2-1)*(n/2+1);(2).n/2爲奇數:結果爲(n/2-2)*(n/2+2);

code:

#include <iostream>
#include <stdio.h>
#include <cmath>
#include <algorithm>
#include <string>
#include <cstring>
#include <queue>
#include <stack>
#include <iomanip>
#include <map>
using namespace std;

int main()
{
    long long t , n ;
    scanf("%I64d" , &t) ;
    while(t --)
    {
        long long sum = 0 ;
        scanf("%I64d" , &n) ;
        if(n == 2)
            printf("1\n") ;
        else
        {
            if(n % 2 == 0)
            {
                n /= 2 ;
                if(n % 2 == 0)
                    sum = (n - 1) * (n + 1) ;
                else
                    sum = (n - 2) * (n + 2) ;
            }
            else
            {
                n /= 2 ;
                sum = n * (n + 1) ;
            }
            printf("%I64d\n" , sum) ;
        }
    }
    return 0;
}


1008:Pieces

這道題是僅次於上一題,但是看了結題報告和標程,還不不太明白,好像是用狀態DP做的,以後重點看一下DP吧~~~

code:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <string>
#include <cstring>
#include <vector>
#include <cassert>
using namespace std;

typedef long long int64;
const int MAX_N = 16, INF = ~0U >> 2;
int n;
int dp[1 << MAX_N];
bool can[1 << MAX_N];

char s[MAX_N + 1];
void work() {
    scanf("%s", s);
    n = strlen(s);

    for (int set = 0; set < (1 << n); ++set) {
        char cur[MAX_N];
        int cnt = 0;
        for (int i = 0; i < n; ++i) {
            if (set >> i & 1)
                cur[cnt++] = s[i];
        }
        bool check = true;
        for (int l = 0, r = cnt - 1; l < r; ++l, --r) {
            if (cur[l] != cur[r]) {
                check = false;
                break;
            }
        }
        can[set] = check;
    }

    dp[0] = 0;
    for (int set = 1; set < (1 << n); ++set) {
        dp[set] = INF;
        for (int subset = set; subset > 0; (--subset) &= set)
            if (can[subset]) {
                dp[set] = min(dp[set], dp[set - subset] + 1);
            }
    }

    cout << dp[(1 << n) - 1] << endl;
}

int main() {
    int T;
    cin >> T;
    while (T--) {
        work();
    }
}

1010:No Pain No Game

這道題一開始就想到暴力求解,但是後來一看數據範圍是50000,果斷TLE了,知道看到結題報告之前也沒想到怎麼做。原來用到了樹狀數組來維護。

思路:求解一個區間的gcd的最大值。把每個數的因子都求出來,然後找到最大值,保證是至少兩個數的約數。然後用樹狀數組。那麼求樹狀數組的方法是什麼麼呢~~用到lowbit(x)函數,就是來計算2^k,其中k是x表示成二進制的末尾0 的個數。code:

#include <iostream>
#include <stdio.h>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <vector>
#include <map>
#include <set>
#include <climits>
#define MAX 50010
using namespace std;
int c[MAX] ;
int n ;
int lowbit(int x)//求2^k
{
    return x & (-x) ;
}
void add(int i , int value)//建立樹狀數組
{
    while(i <= n)
    {
        c[i] = max(c[i] , value) ;
        i += lowbit(i) ;
    }
}
int Max(int i)修改樹狀數組獲得最大值
{
    int s = 0 ;
    while(i > 0)
    {
        s = max(s , c[i]) ;
        i -= lowbit(i) ;
    }
    return s ;
}
int a[MAX] , b[MAX] , ans[MAX] ;
struct Node
{
    int l , r ;
    int index ;
}node[MAX];//建立區間節點
bool cmp(Node a , Node b)
{
    return a.l > b.l ;
}
int main()
{
    int t , m , l , r ;
    scanf("%d" , &t) ;
    while(t --)
    {
        scanf("%d" , &n) ;
        int i ;
        for(i = 1 ; i <= n ; i ++)
        {
            scanf("%d" , &a[i]) ;
        }
        scanf("%d" , &m) ;
        for(i = 0 ; i < m ; i ++)
        {
            scanf("%d%d" , &node[i].l , &node[i].r) ;
            node[i].index = i ;
        }
        sort(node , node + m , cmp) ;
        i = n ;
        int j = 0 ;
        memset(b , 0 , sizeof(a)) ;
        memset(c , 0 , sizeof(c)) ;
        while(j < m)
        {
            while(i > 0 && i > node[i].l)
            {
                for(int k = 1 ; k * k <= a[i] ; k ++ )
                {
                    if(a[i] % k == 0)
                    {
                        if(b[k] != 0)
                        {
                            add(b[k] , k) ;
                        }
                        b[k] = i ;
                        if(k != (a[i] / k))
                        {
                            if(b[a[i] / k] != 0)
                            {
                                add(b[a[i] / k] , a[i] / k) ;
                            }
                            b[a[i] / k] ;
                        }
                    }
                }
                i -- ;
            }
            while(j < m && node[j].l > i)
            {
                ans[node[j].index] = Max(node[j].r) ;
                j ++ ;
            }
        }
        for(i = 0 ; i < m ; i ++)
        {
            printf("%d\n" , ans[i]) ;
        }
    }
    return 0;
}


1011:Sad Love Story

這是一道求最近點對的問題:一個平面上有n個點,開始平明上沒有點,逐個向平面上放點。每次放點都有一個兩點間的最短距離。求放n個點之後最短距離的和是多少。

一開始如果是直接暴力的話會TLE所以採用分治法。

思路:解題過程中採用分治法,從中間向兩邊,過程當中用到了多重集合和迭代器,即multiset和iterator。多重集合就是集合元素可以是重複的。多重集合中lower_bound(P)函數返回值是一個有序的多重集合,元素P的位置。begin()函數返回第一個元素的位置,end()函數返回最後一個元素的位置。然後insert()函數是插入一個元素後自動進行排序。

code:

#include <iostream>
#include <stdio.h>
#include <cmath>
#include <algorithm>
#include <string>
#include <cstring>
#include <queue>
#include <stack>
#include <iomanip>
#include <map>
#include <climits>
#include <set>
#include <vector>
#define N 1LL << 60
#define MAX (int)(5e5+10)
using namespace std;
int m[MAX] , u[MAX] ;
void read(int n , int p[])
{
    int a , b , c ;
    scanf("%d%d%d" , &a , &b , &c) ;
    long long cur = 0 ;
    for(int  i = 0 ; i < n ; ++i)
    {
        cur = (cur * a + b) % c ;
        p[i] = cur ;
    }
}
struct point
{
    int x , y ;
    bool operator<(const point&o)const
    {
        return x < o.x ;
    }
};
void work()
{
    long long sum = 0 ;
    int n ;
    int i , j ;
    scanf("%d" , &n) ;
    read(n , m) ;
    read(n , u) ;
    multiset<point>ps ;
    long long ans = N ;
    for(i = 0 ; i < n ; ++i)
    {
        point p ;
        p.x = m[i] , p.y = u[i] ;
        if(i > 0)
        {//分治法
            multiset<point>::iterator it = ps.lower_bound(p) , e ;
            for(e = it ; e != ps.end() ; ++ e)
            {
                long long dx = e->x - p.x ;
                if(dx * dx >= ans)
                    break ;
                long long dy = e->y - p.y ;
                ans = min(ans , dx * dx + dy * dy) ;
            }
            for(e = it ; e != ps.begin() ; )
            {
                -- e ;
                long long dx = e->x - p.x ;
                if(dx * dx >= ans)
                    break ;
                long long dy = e->y - p.y ;
                ans = min(ans , dx * dx + dy * dy) ;
            }
            sum += ans ;
        }
        ps.insert(p) ;
    }
    printf("%I64d\n" , sum) ;
}
int main()
{
    int t ;
    scanf("%d" , &t) ;
    while(t --)
    {
        work() ;
    }
    return 0;
}

這些題還是很好的,會的東西太少了~~還要學很多啊~~好多東西都是聽過但是不太瞭解,或者根本不會寫,加油加油~~~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章