现在的位置: 首页 > 综合 > 正文

HDU 4669 Mutiples on a circle (环形数列DP)byPlato

2018年04月23日 ⁄ 综合 ⁄ 共 2653字 ⁄ 字号 评论关闭
HDU 4669 Mutiples on a circle (环形数列DP)byPlato
 
题意: 一个环形数列,可以把相邻的一段合并为一个数字。求其中有多少段能被k整除。(数列长度5W,k200)
 
题解的做法还没有看懂。。。下面是我自己的做法
先不考虑环的情况,线性数列下,很容易发现DP的做法:
f[i][j] -> f[i+1][ (j*len + a[i])%k ]
f[i][j] 是以第i个元素结尾,余数为j的方法数。
 
接下来考虑拆环,一般的拆环方法:双倍延长,分类讨论。
这里用的是分类讨论,
1. 1-n两个元素没有连在一起,这就是上面DP讨论的情况。
2. 1-n两个元素连在一起。也就是那段一定跨越1-n两个元素。
第2种情况朴素的做法是枚举两个指针O(n^2),然后采用一些预处理和区间查找值的技巧,可以转化在O(n*k)的复杂度内处理。具体看代码吧。
 
比赛的时候,把取模的运算规则搞错了,出了一些数据,但是都太弱了,WA到死。
(当时想当然的以为 ab%p = (a%p * (len[b%p])%p +b%p)%p 正确的是ab%p = (a%p * (len[b]%p) + b%p)%p   )
还用随机函数生成了一些数据,但都没有查出错误。一是出了数据手动验证正确性也很慢,二是数据的代表性不够。看来出数据也不一定是万能的,除非数据够强并且有验证的方法。
后面了解了下取模的运算规则,修改了下,在经过一些手残脑残的错误之后,终于AC了。
 

#include
<
cstdio>
#include
<
iostream>
#include
<
fstream>
#include
<
cstring>
#include
<
string>
#define OP(s) cout<<#s<<"="<<s<<"
"
;
#define PP(s) cout<<#s<<"="<<s<<endl;
using
namespace
std;
typedef
long
long LL;
const
int
LN =
50010
,LM =
210
;

inline
int
getlen(int x)
{
    if (x
<
10) return
10;
    if (x
<
100) return
100;
    if (x
<
1000) return
1000;
    return
10000
;
}

int main()
{
    #ifndef ONLINE_JUDGE
        freopen("test.txt","r",stdin);
    #endif

    int n,k;
    while (~scanf("%d %d",&n,&k))
    {
        static
int
a[LN],len[LN];
        memset(len,0,sizeof(len));
        for (int i
=1 ;i
<
= n;i++)
        {
            scanf("%d",a+i);
            len[i] = getlen(a[i]);
        }

        LL ans = 0;
        static LL f[2][LM];
        bool now
=
1;
        memset(f,0,sizeof(f));
        for (int i
= 2;i
<
= n;i++)
        {
            for (int j
= 0;j
<
k;j++) if (f[!now][j])
            {
                f[now][(j*len[i]+a[i])%k]
+= f[!now][j];  ///! 手残1: (j*10 + a[i])%k
            }
            f[now][a[i]%k]  +=
1;
            ans += f[now][0];

            now =
!
now;
            memset(f[now],0,sizeof(f[now]));
        }

        static
int
seg1[LN],seg2[LN],num[LM],slen[LN];
        seg1[0] =
0;
        memset(num,0,sizeof(num));
        int totlen
=
1;
        for (int i
= 1;i
<
= n;i++)
        {
            totlen = (totlen*len[i])%k;
            slen[i] = totlen;
            seg1[i] = (seg1[i-1]*len[i]+a[i])%k;
            if (seg1[i]
==
0) ++ans;  //if (seg1[i] == 0 && i != n) ++ans;
            for (int j
= 0;j
<
k;j++)
                if ( (j*(totlen%k)
+ seg1[i])%k
== 0)
                    ++num[j];
        }

        seg2[n] = a[n]%k;
///! 手残2:seg2[n+1] = 0;
        totlen = 1;
        for (int i
= n-1;i
>=
1
;i--)
        {
            totlen = (totlen*len[i+1])%k;   ///!手残脑残3:totlen
= a[n],totlen = (totlen*len[i])%k

            seg2[i] = (a[i]*totlen
+ seg2[i+1])%k;
        }

        for (int i
= n;i >
1
;i--)
        {
            int x
=
seg2[i]%k;
            for (int j
= 0;j
<
k;j++)
                if ( (j*(slen[i]%k)
+ seg1[i])%k
== 0)
                    --num[j];
            ans += num[x];   ///! 手残脑残4: ans+= num[seg2[i]]
        }

        cout<<ans<<endl;
    }

    return
0
;
}

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

抱歉!评论已关闭.