HDU 3364

Lanterns

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 1472    Accepted Submission(s): 581


Problem Description
Alice has received a beautiful present from Bob. The present contains n lanterns and m switches. Each switch controls some lanterns and pushing the switch will change the state of all lanterns it controls from off to on or from on to off. A lantern may be controlled by many switches. At the beginning, all the lanterns are off. 

Alice wants to change the state of the lanterns to some specific configurations and she knows that pushing a switch more than once is pointless. Help Alice to find out the number of ways she can achieve the goal. Two ways are different if and only if the sets (including the empty set) of the switches been pushed are different.
 

Input
The first line contains an integer T (T<=5) indicating the number of test cases.
The first line of each test case contains an integer n (1<=n<=50) and m (1<=m<=50).
Then m lines follow. Each line contains an integer k (k<=n) indicating the number of lanterns this switch controls.
Then k integers follow between 1 and n inclusive indicating the lantern controlled by this switch.
The next line contains an integer Q (1<=Q<=1000) represent the number of queries of this test case.
Q lines follows. Each line contains n integers and the i-th integer indicating that the state (1 for on and 0 for off) of the i-th lantern of this query.
 

Output
For each test case, print the case number in the first line. Then output one line containing the answer for each query.
Please follow the format of the sample output.
 

Sample Input
2 3 2 2 1 2 2 1 3 2 0 1 1 1 1 1 3 3 0 0 0 2 0 0 0 1 0 0
 

Sample Output
Case 1: 1 0 Case 2: 8 0
 
題意:有n個燈和m個開關,每個開關控制數個燈的狀態改變,給出k條詢問,問使燈的狀態變爲詢問中的狀態有多少種發法。
思路:同餘高斯消元法,模板題,將每個開關控制每個燈列成行列式,最終狀態是結果列,同餘高斯消元,如果無解就是0,否則結果就是1<<(自由變元的個數),爲了學習各種高斯消元的模板。將一整套的高斯消元模板弄來了,包括非同餘的,以供相互學習。
#include<stdio.h>
#include<algorithm>
#include<iostream>
#include<string.h>
#include<math.h>
using namespace std;

const int maxn = 110;
const int mod = 2;

int a[maxn][maxn];//增廣矩陣
int b[maxn][maxn];
int x[maxn];//解集
bool free_x[maxn];//標記是否是不確定的變元

int gcd(int a,int b)
{
    return a == 0?b:gcd(b%a,a);
}
int lcm(int a,int b)
{
    return a*b/gcd(a,b);//先除後乘防溢出
}

// 高斯消元法解方程組(Gauss-Jordan elimination).(-2表示有浮點數解,但無整數解,
//-1表示無解,0表示唯一解,大於0表示無窮解,並返回自由變元的個數)
//有equ個方程,var個變元。增廣矩陣行數爲equ,分別爲0到equ-1,列數爲var+1,分別爲0到var.
int Gauss(int equ,int var)
{
    int i,j,k;
    int max_r;// 當前這列絕對值最大的行.
    int col;//當前處理的列
    int ta,tb;
    int LCM;
    int temp;
    int free_x_num;
    int free_index;

//    for(int i=0;i<=var;i++)
//    {
//        x[i]=0;
//        free_x[i]=true;
//    }

    //轉換爲階梯陣.
    col=0; // 當前處理的列
    for(k = 0;k < equ && col < var;k++,col++)
    {// 枚舉當前處理的行.
// 找到該col列元素絕對值最大的那行與第k行交換.(爲了在除法時減小誤差)
        max_r=k;
        for(i=k+1;i<equ;i++)
        {
            if(abs(a[i][col])>abs(a[max_r][col])) max_r=i;
        }
        if(max_r!=k)
        {// 與第k行交換.
            for(j=k;j<var+1;j++) swap(a[k][j],a[max_r][j]);
        }
        if(a[k][col]==0)
        {// 說明該col列第k行以下全是0了,則處理當前行的下一列.
            k--;
            continue;
        }
        for(i=k+1;i<equ;i++)
        {// 枚舉要刪去的行.
            if(a[i][col])
            {
                LCM = lcm(abs(a[i][col]),abs(a[k][col]));
                ta = LCM/abs(a[i][col]);
                tb = LCM/abs(a[k][col]);
                if(a[i][col]*a[k][col]<0)tb=-tb;//異號的情況是相加
                for(j=col;j<var+1;j++)
                {
                    a[i][j] = ((a[i][j]*ta-a[k][j]*tb)%mod+mod)%mod;//如果不是同模取餘則改爲a[i][j] = (a[i][j]*ta-a[k][j]*tb;
                }
            }
        }
    }
    // 1. 無解的情況: 化簡的增廣陣中存在(0, 0, ..., a)這樣的行(a != 0).
    for (i = k; i < equ; i++)
    { // 對於無窮解來說,如果要判斷哪些是自由變元,那麼初等行變換中的交換就會影響,則要記錄交換.
        if (a[i][col]) return -1;
    }
    // 2. 無窮解的情況: 在var * (var + 1)的增廣陣中出現(0, 0, ..., 0)這樣的行,即說明沒有形成嚴格的上三角陣.
    // 且出現的行數即爲自由變元的個數.
    if (k < var)
    {
        //首先,自由變元有var - k個,即不確定的變元至少有var - k個.
//        for (i = k - 1; i >= 0; i--)
//        {
//            // 第i行一定不會是(0, 0, ..., 0)的情況,因爲這樣的行是在第k行到第equ行.
//            // 同樣,第i行一定不會是(0, 0, ..., a), a != 0的情況,這樣的無解的.
//            free_x_num = 0; // 用於判斷該行中的不確定的變元的個數,如果超過1個,則無法求解,它們仍然爲不確定的變元.
//            for (j = 0; j < var; j++)
//            {
//                if (a[i][j] != 0 && free_x[j]) free_x_num++, free_index = j;
//            }
//            if (free_x_num > 1) continue; // 無法求解出確定的變元.
//            // 說明就只有一個不確定的變元free_index,那麼可以求解出該變元,且該變元是確定的.
//            temp = a[i][var];
//            for (j = 0; j < var; j++)
//            {
//                if (a[i][j] != 0 && j != free_index) temp -= a[i][j] * x[j];
//            }
//            x[free_index] = temp / a[i][free_index]; // 求出該變元.
//            free_x[free_index] = 0; // 該變元是確定的.
//        }
        return var - k; // 自由變元有var - k個.
    }
    // 3. 唯一解的情況: 在var * (var + 1)的增廣陣中形成嚴格的上三角陣.
    // 計算出Xn-1, Xn-2 ... X0.
    //需要解得時候就用下面的循環來解。
//    for (i = var - 1; i >= 0; i--)
//    {
//        temp = a[i][var];
//        for (j = i + 1; j < var; j++)
//        {
//            if (a[i][j] != 0) temp = ((temp - a[i][j]*x[j])%mod+mod)%mod;//temp -= a[i][j] * x[j];
//        }
//        while(temp % a[i][i] != 0) temp+=mod;//如果不是同模取餘,則temp % a[i][i] != 0時產生浮點數解
//        x[i] = (temp / a[i][i])%mod;
//         if(x[i]<0)x[i]+=mod;
//    }
    return 0;
}
int main()
{
    int i, j;
    int n,m,var;
    int t,kase = 1;
    scanf("%d",&t);
    while (t--)
    {
        memset(b,0,sizeof(b));
        scanf("%d%d", &n, &var);
        for(int i = 0;i < var;i++)
        {
            int num;
            scanf("%d",&num);
            for(int j = 0;j < num;j++)
            {
                int nn;
                scanf("%d",&nn);
                b[nn-1][i] = 1;
            }
        }
        scanf("%d",&m);
        printf("Case %d:\n",kase++);
        while(m--)
        {
            memcpy(a,b,sizeof(b));
            for(int i = 0;i < n;i++)
            {
                scanf("%d",&a[i][var]);
            }
            int ans = Gauss(n,var);
            if(ans == -1)
                printf("0\n");
            else if(ans == 0)
                printf("1\n");
            else
                printf("%I64d\n",1LL<<ans);
        }
    }
    return 0;
}



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