• JZOJ1434灌水(2019.08.05[NOIP提高组]模拟 B 组)


    传送门

    灌水 (Standard IO)

    Time Limits: 1000 ms  Memory Limits: 65536 KB  Detailed Limits  

    Time to Submit: 01:54:23

    Description

      学生都很喜欢灌水,第一天只有Alice给她的每个朋友灌了一次水,从第二天开始,所有学生(包括Alice)将会有规律地去灌水:
      •如果前一天被灌了奇数次的水,他们将会给每个朋友灌一次水;
      •如果前一天被灌了偶数次的水,他们将会给每个朋友灌两次水。
      学生编号为1到N,Alice为1号,学生之间的朋友关系会给出。
      计算H天后一共灌了几次水。

    Input

      输入一行包含两个整数N和H(1<=N<=20,1<=H<=10^9),表示学生数和天数。
      接下来N行,每行包含N个‘0’或‘1’,(A,B)处的字符表示A和B的关系,‘1’表示是朋友关系,‘0’表示不是。注意自己和自己不是朋友关系,输入保证该矩阵是对称的。

    Output

      输出H天后一共灌水的数量。

    Sample Input

    输入1:

    4 1

    0110

    1001

    1001

    0110

    输入2:

    4 2

    0110

    1001

    1001

    0110

    输入3:

    5 3

    01000

    10110

    01000

    01001

    00010

    Sample Output

    输出1:

    2

    输出2:

    14

    输出3:

    26

    Data Constraint

    Hint

    【样例解释】
      样例2中,第一天Alice灌了2次水,第二天学生1和学生4给学生2和学生3都灌了2次水,而学生2和学生3给学生1和学生4各灌水1次,2天一共灌了12次水。
    【数据范围】
      50%的数据 H<=1000。

    一、暴力(50分)

    按照题目说的模拟暴力即可

    #include<bits/stdc++.h>
    using namespace std;
    inline int poread()
    {
        int res = 0;
        char c = 0;
        while(!isdigit(c = getchar()));
        do
        {
            res = res * 10 + c - 48;
        }
        while(isdigit(c = getchar()));
        return res;
    }
    inline int charget()
    {
        char c = 0;
        while(!isdigit(c = getchar()));
        return c - 48;
    }
    const int MAXN = 25;
    int mp[MAXN][MAXN];
    int jo[MAXN];
    long long sum[MAXN];
    int n,k;
    long long ans;
    int main()
    {
    #ifdef lky233
        freopen("testdata.in","r",stdin);
        freopen("testdata.out","w",stdout);
    #endif
        n = poread();
        k = poread();
        for(register int i = 1; i <= n; ++i)
        {
            for(register int j = 1; j <= n; ++j)
            {
                mp[i][j] = charget();
            }
        }
        jo[1] = 1;
        for(register int kk = 1; kk <= k; ++kk)
        {
            memset(sum,0,sizeof(sum));
            for(register int i = 1; i <= n; ++i)
            {
                for(register int j = 1; j <= n; ++j)
                {
                    if(mp[i][j])
                        sum[j] += jo[i];
                }    
            }
            for(register int i = 1; i <= n; ++i)
                jo[i] = sum[i] % 2 == 0 ? 2 : 1;
            for(register int i = 1; i <= n; ++i)
                    ans += sum[i];
            cerr<<ans<<endl;
        }
        cerr<<ans<<endl;
        cout<<ans<<endl;
    }

    O(k^2),五十分。

    二、正解

    分析数据,一共有n(n<=20)个人,对于每个人有灌别人2次或1次两种状态,一共有2^20 = 1048576种状态,而操作有1e9次,显然会出现重复的状态。

    而在这道题中,如果状态重复,一定会陷入循环。(显然)

    那么就可以找到这个循环然后快速计算出答案。

    #pragma GCC optimize("Ofast")
    #pragma GCC target("sse3","sse2","sse")
    //#pragma GCC target("avx","sse4","sse4.1","sse4.2","ssse3")
    //#pragma GCC target("f16c")
    //#pragma GCC target("fma","avx2")
    //#pragma GCC target("xop","fma4")
    #pragma GCC optimize("inline","fast-math","unroll-loops","no-stack-protector")
    #pragma GCC diagnostic error "-fwhole-program"
    #pragma GCC diagnostic error "-fcse-skip-blocks"
    #pragma GCC diagnostic error "-funsafe-loop-optimizations"
    #pragma GCC diagnostic error "-std=c++14"
    
    
    #include<bits/stdc++.h>
    using namespace std;
    const int MAXN = 25;
    int mp[MAXN][MAXN];
    int n,k;
    int m;
    int v[2097160];//2^20 = 2097152;
    long long sum[100000];
    long long sumed[MAXN];
    inline int poread()
    {
        int res = 0;
        char c = 0;
        while(!isdigit(c = getchar()));
        do
        {
            res = res * 10 + c - 48;
        }
        while(isdigit(c = getchar()));
        return res;
    }
    inline int charget()
    {
        char c = 0;
        while(!isdigit(c = getchar()));
        return c - 48;
    }
    int main()
    {
    #ifdef lky233
        freopen("testdata.in","r",stdin);
        freopen("testdata.out","w",stdout);
    #endif
        n = poread();
        k = poread();
        for(register int i = 1; i <= n; ++i)
        {
            for(register int j = 1; j <= n; ++j)
            {
                mp[i][j] = charget();
            }
        }
        int round = 1;
        m = 2;
        for(round; round <= k; ++round)
        {
            memset(sumed,0,sizeof(sumed));
            for(register int i = 1; i <= n; ++i)
            {
                for(register int j = 1; j <= n; ++j)
                {
                    if(mp[i][j])
                    {
                        sumed[j] += (m & (1 << i)) ? 1 : 2;
                    }
                }
            }
            if(round == 1)
            {
                memset(sumed,0,sizeof(sumed));
                for(register int i = 1; i <= n; ++i)
                    sumed[i] += mp[1][i];
            }
    //        cerr<<round<<" "<<m<<" ";
            if(v[m] && v[m] != 1)
            {
                break;
                int ttttt = clock();
                while(clock() - ttttt < 2000);
            }
            v[m] = round;
            m = 0;
            for(register int i = 1; i <= n; ++i)
            {
                if(sumed[i] & 1)
                    m = (m | (1<<i));
            }
            for(register int i = 1; i <= n; ++i)
                sum[round] += sumed[i];
    //        cerr<<sum[round]<<endl;
        }
    //    cerr<<endl;
        round--;
        long long lsum = 0;
        long long ans = 0;
    //    cerr<<v[m]<<" "<<round<<endl;
        //循环区间和 
        for(register int i = v[m]; i <= round; i++)
            lsum += sum[i];
        //区间段 数 
        int tim = (k - v[m]) / (round - v[m] + 1);
    //    cerr<<tim<<endl;
        ans = lsum * tim;
        
        //前面的 
        for(register int i = 1; i <= v[m] - 1; ++i)
            ans += sum[i];
        //剩余的数量 
        tim = k - tim * (round - v[m] + 1) - v[m] + 1;
    //    cerr<<tim<<endl;
        //未循环到的区间 
        for(register int i = v[m]; i <= tim + v[m] - 1; ++i)
            ans += sum[i];
    //    cerr<<ans<<endl;
        cout<<ans<<endl;
    }

    坑点:初始的状态与其他状态不同需要特判。

  • 相关阅读:
    一秒 解决 ERROR 1044 (42000): Access denied for user ''@'localhost' to database 'mysql 问题
    30分钟让你学会 Spring事务管理属性
    判断是否是回文
    Linux自动获取IP地址 重启不会消失
    .The server quit without updating PID file (/var/lib/mysql/pc.pid).
    线程的状态
    linux vim基本操作
    C++ 函数重载和参数的缺省值
    C++ 类中的3种访问权限和继承方式
    C++ 内存管理
  • 原文地址:https://www.cnblogs.com/Shiina-Rikka/p/11305597.html
Copyright © 2020-2023  润新知