zoukankan      html  css  js  c++  java
  • 20171122校内训练

    各种特判啊。

    首先,我们可以发现,每个人看到的帽子种类数要么是帽子总种类数,要么是这个数-1。

    所以,如果q[i]有大于两种或两种q[i]的差大于1,那么肯定无解。

    我们不妨记比较小的那个q[i]为num1,出现次数为cnt1,比较大的那个数q[i]为num2,出现次数为cnt2

    若只有出现一种数,则num2=cnt2=0。

    只出现一种数:首先,num1如果=n-1,有解(n顶帽子颜色互异)。我们发现,如果出现的帽子颜色每种至少有两个,那么也可以满足题意,但是此时num1最大只能是n/2。

    若出现两种数:如果大的那种数出现次数为一,肯定无解。然后q是小的那个数的人的帽子颜色必须互异且q是大的那个数的人的帽子颜色每种至少有两个。

    q是小的那个数的人的帽子颜色必须互异:这里对答案的贡献为cnt1-1(在q是小的那个数的人的视角(他自己头上的帽子颜色不算))

    q是大的那个数的人的帽子颜色每种至少有两个:这里对答案的贡献最小为1,最大为cnt2/2。

    所以num1必须介于cnt1-1+1与cnt1-1+cnt2/2之间才有解

    #include<iostream>
    #include<cstdio>
    using namespace std;
    int main()
    {
        freopen("water.in","r",stdin);freopen("water.out","w",stdout);
        int T;scanf("%d",&T);
        while(T--)
        {
            int n,cnt1=0,cnt2=0,num1=0,num2=0;
            scanf("%d",&n);bool ok=0;
            for(int i=1;i<=n;i++)
            {
                int a;scanf("%d",&a);
                if(a==num1)cnt1++;
                else if(a==num2)cnt2++;
                else if(num1==0)num1=a,cnt1++;
                else if(num2==0)num2=a,cnt2++;
                else ok=1;
            }
            if(ok){puts("No");continue;}
            if(num1>num2&&num2!=0)swap(cnt1,cnt2),swap(num1,num2);
            if(num2-num1>=2){puts("No");continue;}
            if(num2==0)
            {
                if(num1==n-1){puts("Yes");continue;}
                if(num1<=n/2){puts("Yes");continue;}
                puts("No");continue;
            }
            else
            {
                if(cnt2==1){puts("No");continue;}
                if(num1>=cnt1&&num1<=cnt1+cnt2/2-1){puts("Yes");continue;}
                puts("No");continue;
            }
        }
        return 0;
    }
    View Code

     

    我们考虑DP

    首先我们可以发现,每个涂色方案满不满足条件,主要看的是红,绿,蓝三种颜色的最后一次出现位置。所以我们的状态可以设成这样:dp[i][j][k][l]表示前i个数(即询问确定到i(询问不考虑r>i的区间)),红色最后一次出现的位置j,绿色最后一次出现的位置k,蓝色最后一次出现的位置l。

    但这样复杂度显然是O(n^4)的,考虑优化。

    我们可以发现,一定有一种颜色的最后出现位置为i,且询问对颜色种类数没有做过多的要求(即只要有这么多种就好了,而不是这里面具体出现了哪几种颜色),所以我们考虑这么表示状态:f[i][j][k]表示确定到i,三种颜色最后一次出现的位置分别是i,j,k(j>k),显然i,j,k互不相等。但是,我们规定,dp[i][j][0]表示三种颜色其中两种颜色最后一次出现的位置分别是i,j,没有出现过第三种颜色。dp[i][0][0]表示只有出现一种颜色。

    转移的话呢,显然,我们判断dp[i][j][k]是由什么东西转移来的显然不太好判断(至少时间复杂度高),这时候我们就思考,dp[i][j][k]可以转移出什么。把黄圈看成蓝圈。红圈表示红色最后一次出现位置,绿圈表示绿色最后一次出现位置,黄圈表示蓝色最后一次出现位置。然后第i+1个位置有可能填红绿黄三种颜色之一,即后三幅图的状态都可以从第一幅图转移过来。于是有如下状态转移方程:

    dp[i-1][j][k](j是绿圈位置,k是红圈位置,i-1是黄圈位置)

    dp[i][j][k](j是绿圈位置,k是红圈位置,i是黄圈位置)

    dp[i][i-1][k](i-1是黄圈位置,k是红圈位置,i是绿圈位置)

     dp[i][i-1][j](i-1是黄圈位置,j是绿圈位置,i是红圈位置)

    但是我们还没判这些状态合不合法呢。我们看看所有i==询问的右端点的询问,对于每种状态dp[i][j][k],看一下这个区间中是不是恰好有那么多种数,如果不是,dp[i][j][k]=0。

    初始化dp[1][0][0]=3。特殊的转移dp[i-1][0][0]可以转移到dp[i][0][0],dp[i][i-1][0],dp[i][i-1][0](这里没有多写一遍,就是可以转移到两次dp[i][i-1][0])

    时间复杂度O(n^3),但是,由于这题空间复杂度8MB,所以我们把i这维滚动

    #include<iostream>
    #include<vector>
    #include<cstdio>
    using namespace std;
    const int mod=1e9+7;
    int dp[2][301][301];
    typedef pair<int,int> P;
    vector<P> R[301];
    int main()
    {
        int n,m;scanf("%d%d",&n,&m);int ok=0;
        for(int i=1;i<=m;i++){int l,r,x;scanf("%d%d%d",&l,&r,&x);R[r].push_back(P(l,x));if(l==r&&x>1)ok=1;}
        if(ok){puts("0");return 0;}
        dp[1][0][0]=3;
        for(int i=2;i<=n;i++)
        {
            int o=i&1;
            for(int j=0;j<i;j++)for(int k=0;k<j;k++)dp[o][j][k]=0;
            dp[o][0][0]=0;
            dp[o][0][0]=(dp[o][0][0]+dp[o^1][0][0])%mod;
            dp[o][i-1][0]=(dp[o][i-1][0]+dp[o^1][0][0])%mod;
            dp[o][i-1][0]=(dp[o][i-1][0]+dp[o^1][0][0])%mod;
            for(int j=0;j<i;j++)
            for(int k=0;k<j;k++)
            {
                dp[o][j][k]=(dp[o][j][k]+dp[o^1][j][k])%mod;
                dp[o][i-1][j]=(dp[o][i-1][j]+dp[o^1][j][k])%mod;
                dp[o][i-1][k]=(dp[o][i-1][k]+dp[o^1][j][k])%mod;
            }
            for(int l=0;l<R[i].size();l++)
            {
                for(int j=0;j<i;j++)
                for(int k=0;k<j;k++)
                {
                    int ok1=0;if(j>=R[i][l].first)ok1=1;
                    int ok2=0;if(k>=R[i][l].first)ok2=1;
                    if(ok1+ok2+1!=R[i][l].second)dp[o][j][k]=0;
                }
                if(R[i][l].second!=1)dp[o][0][0]=0; 
            }
        }
        int ans=0;
        for(int j=0;j<n;j++)
        for(int k=0;k<j;k++)ans=(ans+dp[n&1][j][k])%mod;
        printf("%d",(ans+dp[n&1][0][0])%mod);
        return 0;
    }
    View Code
  • 相关阅读:
    Java多线程总结之线程安全队列Queue
    Android模拟器Genymotion使用详解
    Failed to resolve:com.android.support:appcompat-v7:报错处理
    第一次使用Android Studio时你应该知道的一切配置
    sdk manager 打不开
    Android app设置全屏模式
    Android设备与外接U盘实现数据读取操作
    IIS相关优化
    集群、负载均衡、分布式
    docker启动
  • 原文地址:https://www.cnblogs.com/lher/p/7887216.html
Copyright © 2011-2022 走看看