結果

問題 No.174 カードゲーム(Hard)
ユーザー qwewe
提出日時 2025-05-14 13:06:32
言語 C++17
(gcc 13.3.0 + boost 1.87.0)
結果
TLE  
実行時間 -
コード長 7,950 bytes
コンパイル時間 1,100 ms
コンパイル使用メモリ 120,396 KB
実行使用メモリ 90,656 KB
最終ジャッジ日時 2025-05-14 13:07:45
合計ジャッジ時間 4,523 ms
ジャッジサーバーID
(参考情報)
judge4 / judge1
このコードへのチャレンジ
(要ログイン)
ファイルパターン 結果
other AC * 2 TLE * 1 -- * 9
権限があれば一括ダウンロードができます

ソースコード

diff #

#include <iostream>
#include <vector>
#include <numeric>
#include <algorithm>
#include <map> 
#include <unordered_map> // Use unordered_map for potentially better average performance than map
#include <iomanip>
#include <cmath> // For basic math functions if needed

// Platform-specific optimizations for bit manipulation intrinsics
// Check compiler and include intrinsics if available
#if defined(__GNUC__) || defined(__clang__)
// GCC or Clang: use built-in functions __builtin_ctz and __builtin_popcount
#define custom_ctz(x) __builtin_ctz(x)
#define custom_popcount(x) __builtin_popcount(x)
#elif defined(_MSC_VER)
// MSVC: use intrinsics _BitScanForward and __popcnt
#include <intrin.h>
// Define custom_ctz equivalent for MSVC
inline unsigned long msvc_bsf_ulong(unsigned long mask) { 
    unsigned long index;
    // _BitScanForward returns non-zero if a set bit is found and sets index.
    // Returns zero if mask is zero.
    if (_BitScanForward(&index, mask)) return index;
    // The behavior of __builtin_ctz(0) is undefined. 
    // In this code, ctz is called inside loops `while (mask > 0)`, ensuring mask is non-zero.
    // So we don't need to worry about mask=0 case here.
    // If it were possible, we might return a special value like -1 or handle it explicitly.
    return 0; // Default return, should not be reached for non-zero mask
}
#define custom_ctz(x) (int)msvc_bsf_ulong((unsigned long)(x))
// Define custom_popcount equivalent for MSVC
#define custom_popcount(x) (int)__popcnt((unsigned int)(x))
#else
// Fallback implementations if no specific intrinsics are detected/available
// This implementation of ctz finds the index of the least significant bit
int fallback_ctz(int mask) {
    if (mask == 0) return 0; // Define behavior for 0 mask, though likely unused here
    int count = 0;
    // Check bits from right to left (0-indexed)
    while ((mask & 1) == 0) {
        mask >>= 1;
        count++;
        // Add safety break for potentially large integers, though int is usually 32 bits
         if (count >= 32) return -1; // Indicate error or handle appropriately
    }
    return count;
}
// This implementation of popcount counts the number of set bits
int fallback_popcount(int mask) {
    int count = 0;
    while (mask > 0) {
        mask &= (mask - 1); // This clears the least significant set bit
        count++;
    }
    return count;
}
#define custom_ctz(x) fallback_ctz(x)
#define custom_popcount(x) fallback_popcount(x)
#endif


using namespace std;

int N; // Number of cards per player
double PA, PB; // Probabilities for A and B to play their smallest card
vector<int> A_sorted, B_sorted; // Sorted lists of initial cards for A and B

// Using unordered_map for memoization. Key is a combination of two masks.
// Key type is long long to accommodate up to 40 bits (20 for each player).
unordered_map<long long, double> memo;

// Recursive function to calculate expected score with memoization
double dp(int mask_A, int mask_B) {
    // Base case: if no cards are left, expected score is 0
    if (mask_A == 0) {
        return 0.0;
    }
    
    // Create a unique key for the current state (mask_A, mask_B)
    // Shift mask_A left by 20 bits (since N <= 20) and OR with mask_B
    long long state_key = ((long long)mask_A << 20) | mask_B; 
    
    // Check if the result for this state is already memoized
    auto it = memo.find(state_key);
    if (it != memo.end()) {
        return it->second; // Return cached result
    }

    // Calculate the number of cards remaining for each player (k)
    int k = custom_popcount(mask_A); 

    // Find the indices of the minimum value cards currently held by A and B
    // This relies on the cards being sorted initially. The LSB set in the mask corresponds to the minimum available card.
    int min_A_idx = custom_ctz(mask_A); 
    int min_B_idx = custom_ctz(mask_B);

    double expected_value = 0.0; // Accumulator for the expected score from this state

    // Calculate the probability of playing a non-minimum card
    // Handle the case k=1 separately to avoid division by zero
    double prob_A_other = (k <= 1) ? 0.0 : (1.0 - PA) / (double)(k - 1.0);
    double prob_B_other = (k <= 1) ? 0.0 : (1.0 - PB) / (double)(k - 1.0);

    // Iterate through all possible cards A can play using bit manipulation
    // This loop iterates through each set bit in mask_A
    int current_mask_A = mask_A;
    while (current_mask_A > 0) {
        // Get the index 'i' of the least significant set bit (current card A to consider)
        int i = custom_ctz(current_mask_A); 
        int card_A = A_sorted[i]; // Get the actual card value
        double prob_A; // Probability A plays this card
        
        // Determine the probability A plays card_A based on the rules
        if (k == 1) { // If only one card left, must play it
            prob_A = 1.0;
        } else { // Otherwise, depends if it's the minimum card
            prob_A = (i == min_A_idx) ? PA : prob_A_other;
        }

        // Iterate through all possible cards B can play using bit manipulation
        int current_mask_B = mask_B;
        while (current_mask_B > 0) {
            // Get the index 'j' of the least significant set bit (current card B to consider)
            int j = custom_ctz(current_mask_B); 
            int card_B = B_sorted[j]; // Get the actual card value
            double prob_B; // Probability B plays this card

            // Determine the probability B plays card_B based on the rules
             if (k == 1) { // If only one card left, must play it
                 prob_B = 1.0;
             } else { // Otherwise, depends if it's the minimum card
                 prob_B = (j == min_B_idx) ? PB : prob_B_other;
             }

            // Calculate the score A gets in this specific match if A wins (card_A > card_B)
            double current_score = (card_A > card_B) ? (double)(card_A + card_B) : 0.0;
            
            // Determine the masks for the next state by removing the played cards
            // Use XOR to toggle the bit corresponding to the played card's index
            int next_mask_A = mask_A ^ (1 << i);
            int next_mask_B = mask_B ^ (1 << j);
            
            // Recursively call dp for the next state and add the weighted contribution
            // The total expected score is the immediate score + expected future score
            expected_value += prob_A * prob_B * (current_score + dp(next_mask_A, next_mask_B));

            // Clear the least significant set bit in current_mask_B to move to the next card for B
            current_mask_B &= current_mask_B - 1; 
        }
        // Clear the least significant set bit in current_mask_A to move to the next card for A
        current_mask_A &= current_mask_A - 1; 
    }

    // Store the computed expected value in the memoization table before returning
    return memo[state_key] = expected_value;
}


int main() {
    // Use fast I/O operations
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    // Read input values N, PA, PB
    cin >> N >> PA >> PB;

    // Read card values for players A and B
    vector<int> A_cards(N), B_cards(N);
    for (int i = 0; i < N; ++i) cin >> A_cards[i];
    for (int i = 0; i < N; ++i) cin >> B_cards[i];

    // Create sorted copies of the card lists
    A_sorted = A_cards;
    B_sorted = B_cards;
    sort(A_sorted.begin(), A_sorted.end());
    sort(B_sorted.begin(), B_sorted.end());
    
    // Calculate the initial mask where all bits are set (representing all cards available)
    // (1 << N) - 1 creates a mask with N set bits: 11...1 (N times)
    int initial_mask = (1 << N) - 1;
    
    // Call the recursive DP function starting from the initial state (all cards available)
    double result = dp(initial_mask, initial_mask);

    // Output the final result with the required precision
    cout << fixed << setprecision(15) << result << endl;

    return 0;
}
0