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;
}
# 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;
}