1. 程式人生 > 實用技巧 >題解 [HNOI2016]大數

題解 [HNOI2016]大數

題目傳送門

題目大意

給出一個\(n\)個數的字串,有\(m\)次查詢,對於該串的子串\([l,r]\)有多少個子串滿足是固定素數\(p\)的倍數。

思路

其實很簡單,但是一開始想偏了。。。果然還是自己菜啊。。。

我們可以想到統計一下字尾和\(s[i]\),表示\([i,n]\)構成的數,那麼,判斷一個區間\([l,r]\)是不是\(p\)的倍數就等價於:

\[\dfrac{s[l]-s[r+1]}{10^{n-r}}\equiv 0 \pmod p \]

我們發現如果\(\gcd(10,p)=1\)的話,分母就不會產生影響,於是判斷條件就是:

\[s[l]\equiv s[r+1]\pmod p \]

於是對於這種情況我們就可以用莫隊\(\Theta(n\sqrt n)\)開桶記錄答案。

如果\(\gcd(10,p)\not=1\)的話,那麼\(p=2 \operatorname{or} 5\),我們發現這種情況對於區間\([l,r]\)判斷是否的話直接判斷第\(r\)位是不是\(2\operatorname{or}5\)的倍數即可。於是我們可以\(\Theta(n)\)解決這種情況。

果然還是自己菜了啊。。。這都沒有看出來。。。

\(\texttt{Code}\)

#include <bits/stdc++.h>
using namespace std;

#define Int register int
#define ll long long
#define MAXN 200005

template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}

char S[MAXN];
ll sum,ans[MAXN];int n,m,p,un,s[MAXN],tmp[MAXN],bel[MAXN],cnt[MAXN];

namespace Subtask1{
	struct node{
		int l,r,id;
		bool operator < (const node &p)const{return bel[l] != bel[p.l] ? l < p.l : r < p.r;}
	}q[MAXN];
	void cge (int x,int k){sum -= 1ll * cnt[x] * (cnt[x] - 1) / 2,cnt[x] += k,sum += 1ll * cnt[x] * (cnt[x] - 1) / 2;}
	void Work (){
		int siz = sqrt (n);
		for (Int i = n,c = 1;i;-- i,c = 1ll * c * 10 % p) s[i] = (s[i + 1] + 1ll * c * (S[i] - '0') % p) % p,bel[i] = (i - 1) / siz + 1,tmp[i] = s[i];
		sort (tmp + 1,tmp + n + 2);un = unique (tmp + 1,tmp + n + 2) - tmp - 1;for (Int i = 1;i <= n + 1;++ i) s[i] = lower_bound (tmp + 1,tmp + un + 1,s[i]) - tmp;
		read (m);for (Int i = 1;i <= m;++ i) read (q[i].l,q[i].r),q[i].r ++,q[i].id = i;sort (q + 1,q + m + 1);int l = 1,r = 0;
		for (Int i = 1;i <= m;++ i){
			while (l < q[i].l) cge (s[l ++],-1);while (l > q[i].l) cge (s[-- l],1);
			while (r < q[i].r) cge (s[++ r],1);while (r > q[i].r) cge (s[r --],-1);
			ans[q[i].id] = sum;
		}
		for (Int i = 1;i <= m;++ i) write (ans[i]),putchar ('\n');
		return ;
	}
}

namespace Subtask2{
	int snum[MAXN];ll ssum[MAXN];
	void Work(){
		for (Int i = 1;i <= n;++ i){
			snum[i] = ((S[i] - '0') % p == 0);
			ssum[i] = ssum[i - 1] + 1ll * ((S[i] - '0') % p == 0) * i;
		}
		read (m);
		while (m --){
			int l,r;read (l,r);
			write (ssum[r] - ssum[l - 1] - (snum[r] - snum[l - 1]) * (l - 1)),putchar ('\n');
		}
	}
}

signed main(){
	read (p),scanf("%s",S + 1),n = strlen (S + 1);
	if (p == 2 || p == 5) Subtask2::Work ();
	else Subtask1::Work ();
	return 0;
}