The 2016 ACM-ICPC Asia Dalian Regional Contest 部分题解

A - Wrestling Match

二分图判定。

#include<bits/stdc++.h>
using namespace std ;
int main()
{
    std::ios::sync_with_stdio(false) , cin.tie(0) ;
    int n , m , x , y ;
    while(cin >> n >> m >> x >> y)
    {
        vector<vector<int>> g(n + 1) ;
        vector<int> can(n + 1 , false) ;
        while(m --)
        {
            int u , v ;
            cin >> u >> v ;
            g[u].push_back(v) ;
            g[v].push_back(u) ;
            can[u] = true ;
            can[v] = true ;
        }
        vector<int> a(x + 1) ;
        vector<int> b(y + 1) ;
        for(int i = 1 ; i <= x ; i ++)  cin >> a[i] ;
        for(int i = 1 ; i <= y ; i ++)  cin >> b[i] ;
        vector<int> vis(n + 1 , 0) ;
        function<bool(int , int)> ok = [&](int u , int c)
        {
            queue<int> q ;
            vis[u] = c ;
            q.push(u) ;
            while(!q.empty())
            {
                int now = q.front() ;
                q.pop() ;
                for(auto v : g[now])
                {
                    //cout << "??? " << v << '\n' ;
                    if(vis[v] == 0)
                    {
                        vis[v] = 3 - vis[now] ;
                        q.push(v) ;
                    }
                    else
                    {
                        if(vis[v] != 3 - vis[now])  
                        {
                            //cout << "^^^ " << u << ' ' << v << '\n' ;
                            return false ;
                        }
                    }
                }
            }
            return true ;
        } ;
        bool flag = true ;
        for(int i = 1 ; i <= x ; i ++)
            if(vis[a[i]] == 0)  flag &= ok(a[i] , 1) ;
        for(int i = 1 ; i <= y ; i ++)
            if(vis[b[i]] == 0)  flag &= ok(b[i] , 2) ;
        for(int i = 1 ; i <= x ; i ++)  if(vis[a[i]] != 1)  flag = false ;
        for(int i = 1 ; i <= y ; i ++)  if(vis[b[i]] != 2)  flag = false ; 
        for(int i = 1 ; i <= n ; i ++)
            if(vis[i] == 0 && can[i])  flag &= ok(i , 1) ;
        //for(int i = 1 ; i <= n ; i ++)  cout << i << ' ' << vis[i] << '\n' ;
        for(int i = 1 ; i <= n ; i ++)  if(vis[i] == 0)  flag = false ;
        if(flag)  cout << "YES\n" ;
        else  cout << "NO\n" ;
    }
    return 0 ;
}

 

B - Regular Number 

可选字符匹配问题,shift_and算法模板题

 

C - Game of Taking Stones

威佐夫博弈。a_k=\left \lfloor \frac{k*(\sqrt{5}+1)}{2} \right \rfloor,b_k=a_k+k是必败态。

需要上一个java

import java.math.BigDecimal ;
import java.math.BigInteger ;
import java.util.Scanner ;

public class Main
{
    public static void main(String[] args) 
    {
		Scanner cin = new Scanner(System.in) ;
		BigDecimal l = BigDecimal.valueOf(2) ;
		BigDecimal r = BigDecimal.valueOf(3) ;
		for(int i = 0 ; i < 500 ; i ++)
		{
		    BigDecimal mid = l.add(r).divide(BigDecimal.valueOf(2)) ;
		    if(mid.multiply(mid).compareTo(BigDecimal.valueOf(5)) < 0)  l = mid ;
		    else  r = mid ;
		}
		while(cin.hasNext())
		{
		    BigDecimal a = cin.nextBigDecimal() ;
		    BigDecimal b = cin.nextBigDecimal() ;
		    int x = a.compareTo(b) ;
		    if(x > 0)
		    {
		        BigDecimal c = a ;
		        a = b ;
		        b = c ;
		    }
		    BigDecimal k = b.subtract(a) ;
		    BigDecimal ans = l.add(BigDecimal.valueOf(1)).multiply(k).divide(BigDecimal.valueOf(2)) ;
		    ans = ans.setScale(0 , BigDecimal.ROUND_FLOOR) ;
		    if(ans.add(k).compareTo(b) == 0)  System.out.println("0") ;
		    else  System.out.println("1") ;
		}
	}
}

 

D - A Simple Math Problem

把题目中式子的gcd消除后解一元二次方程即可。

#include<bits/stdc++.h>
using namespace std ;
#define ll long long 
ll sq(ll x)
{
    ll t = sqrt(x) ;
    for(ll i = t - 1 ; i <= t + 1 ; i ++)
        if(i * i == x)  return i ;
    return 0 ;
}
int main()
{
    std::ios::sync_with_stdio(false) , cin.tie(0) ;
    ll a , b ;
    while(cin >> a >> b)
    {
        ll d = __gcd(a , b) ;
        b *= d ;
        //cout << "^^^ " << a * a << ' ' << 4 * b << '\n' ;
        if(a * a == 4 * b)
        {
            //if(a % 2 == 0)  cout << "ok\n" ;
            if(a % 2 == 0)  cout << a / 2 << ' ' << a / 2 << '\n' ;
            else  cout << "No Solution\n" ;
        }
        else if(a * a >= 4 * b)
        {
            ll tmp = sq(a * a - 4 * b) ;
            //cout << "??? " << tmp << '\n' ;
            if(tmp == 0)  cout << "No Solution\n" ;
            else
            {
                if((a + tmp) % 2 == 0 && (a - tmp) % 2 == 0)  
                    cout << (a - tmp) / 2 << ' ' << (a + tmp) / 2 << '\n' ;
                else  cout << "No Solution\n" ;
            }
        }
        else  cout << "No Solution\n" ;
    }
    return 0 ;
}

 

E - Aninteresting game 

本题有两种询问

1.本质就是lowbit前缀和,结论如下:sum[1:$2^n$] = n*$2^{n-1}$+$2^n$ \ sum[6] = sum[1:2] + sum[1:4]

2.问1-n中有多少个数x满足,[x-lowbit(x)+1,x]这个区间包含了询问的数。手动模拟后可知除了x本身外,从lowbit开始往高的每个0换成1,然后低位换成0的那个数才符合

 

F - Detachment 

打表可以发现最优一定是拆成2、3、4、5、6这样连续的数字,二分找到最大的p满足\sum_{i=2}^pi \leq x,再设q=x-\sum_{i=2}^p i,若q<p,那就让p-q+1~p都+1,若q=p,那就拆成3、4、5...p、p+2。

 

G - Garden of Eden

dp[i][j]表示以i为路径的一个端点,且路径的另一个端点在以i为根的子树内,且路径颜色集合的状压状态是j的路径数。

暴力合并复杂度太高,考虑剪枝。

s|t=2^k-1预处理出满足要求的(s,t)二元组。

又因为dp[i][]不是很满的,只有这个状态方案数大于0再去匹配。

也可以用点分治去冲,不过我感觉复杂度都差不多。

本题可以做到O(32n)的复杂度,要支持插入一个10位的二进制数,询问和一个10位的二进制数或运算=1023的数的数量,现在插入是O(1)的,询问是O(1023)的,如果我们把数字拆成低5位和高5位,可以使插入和询问的复杂度均为O(32)

#include<bits/stdc++.h>
using namespace std ;
vector<int> h[12][1025] ;
int main()
{
    std::ios::sync_with_stdio(false) , cin.tie(0) ;
    int n , k ;
    for(int p = 1 ; p <= 10 ; p ++)
        for(int i = 0 ; i < (1 << p) ; i ++) 
            for(int j = 0 ; j < (1 << p) ; j ++)
                if((i | j) == (1 << p) - 1)
                    h[p][i].push_back(j) ;   
    while(cin >> n >> k)
    {
        vector<int> c(n + 1) ;
        for(int i = 1 ; i <= n ; i ++)  cin >> c[i] , c[i] -- ;
        vector<vector<int>> g(n + 1) ;
        for(int i = 1 ; i <= n - 1 ; i ++)
        {
            int u , v ;
            cin >> u >> v ;
            g[u].push_back(v) ;
            g[v].push_back(u) ;
        }
        if(k == 1)
        {
            cout << 1ll * n * n << '\n' ;
            continue ;
        }
        vector<vector<int>> dp(n + 1) ;
        for(int i = 0 ; i <= n ; i ++)  dp[i].resize((1 << k) , 0) ;
        long long ans = 0 ;
        int up = (1 << k) ;
        function<void(int , int)> dfs = [&](int fa , int u)
        {
            dp[u][(1 << c[u])] = 1 ;
            for(auto v : g[u])
            {
                if(v == fa)  continue ;
                dfs(u , v) ;
                for(int i = 0 ; i < up ; i ++)
                {
                    if(dp[v][i] == 0)  continue ;
                    for(auto t : h[k][i])
                    {
                        //cout << u << ' ' << t << ' ' << dp[u][t] << '\n' ;
                        //cout << v << ' ' << i << ' ' << dp[v][i] << '\n' ;
                        ans += 1ll * dp[u][t] * dp[v][i] ;
                    }
                }
                for(int i = 0 ; i < up ; i ++)
                {
                    int col = (i | (1 << c[u])) ;
                    dp[u][col] += dp[v][i] ;
                }
            }
        } ;
        dfs(1 , 1) ;
        //cout << ans << '\n' ;
        //for(int i = 1 ; i <= n ; i ++)  ans += dp[i][(1 << k) - 1] ;
        cout << ans * 2 << '\n' ;
    }
    return 0 ;
}

 

H - To begin or not to begin 

dp[i]表示当前有i个黑球和1个红球且面对这个局面可以赢的概率。

转移方程:dp[i]=\frac{1}{i+1}+\frac{i}{i+1}*(1-dp[i-1])

#include<bits/stdc++.h>
using namespace std ;
int main()
{
    std::ios::sync_with_stdio(false) , cin.tie(0) ;
    int k ;
    while(cin >> k)
    {
        k ++ ;
        vector<double> dp(k + 1 , 0.0) ;
        function<double(int)> dfs = [&](int i)
        {
            if(i == 1)
            {
                dp[i] = 1.0 ;
                return dp[i] ;
            }
            dp[i] = 1.0 / i + 1.0 * (i - 1) / i * (1.0 - dfs(i - 1)) ;
            return dp[i] ;
        } ;
        dfs(k) ;
        if(fabs(dp[k] - 0.5) < 1e-6)  cout << "0\n" ;
        else if(dp[k] > 0.5)  cout << "1\n" ;
        else  cout << "2\n" ;
    }
    return 0 ;
}

 

I - Convex

温暖的签到题。

#include<bits/stdc++.h>
using namespace std ;
const double pi = acos(-1.0) ;
int main()
{
    std::ios::sync_with_stdio(false) , cin.tie(0) ;
    int n , d ;
    while(cin >> n >> d)
    {
        vector<int> a(n) ;
        for(int i = 0 ; i < n ; i ++)  cin >> a[i] ;
        double ans = 0 ;
        for(int i = 0 ; i < n ; i ++)
            ans += 0.5 * d * d * sin(a[i] / 180.0 * pi) ;
        cout << fixed << setprecision(3) << ans << '\n' ;
    }
    return 0 ;
}

 

J - Find Small A

温暖的签到题。

#include<bits/stdc++.h>
using namespace std ;
int main()
{
    std::ios::sync_with_stdio(false) , cin.tie(0) ;
    int n ;
    while(cin >> n)
    {
        int ans = 0 ;
        while(n --)
        {
            unsigned x ;
            cin >> x ;
            while(x > 0)
            {
                if(x % 256 == 97)  ans ++ ;
                x /= 256 ;
            }
        }
        cout << ans << '\n' ;
    }
    return 0 ;
}

 

K - Guess the number

留坑。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值