1. 程式人生 > 實用技巧 >CSUST 4001-你真的會加法嗎?(不進位加法-字典樹)

CSUST 4001-你真的會加法嗎?(不進位加法-字典樹)

題目連結:http://acm.csust.edu.cn/problem/4001
CSDN食用連結:https://blog.csdn.net/qq_43906000/article/details/107721754
Description

眾所周知,LJ精通 \(1+1\)\(1 + 2\), 這天他遇到一個簡單的加法題,但這個加法有一個特殊的性質,它是不進位加法,

比如當是10進位制時 \(987 + 643 = 520\) ,當一位大於 \(10\) 的時候我們我們對其模 \(10\) ,取餘數作為這位的值,\(k\) 進位制數同理。

現在給你 \(n\) 個數 \((1 \leq n \leq 1e5)\),並且每個數最多隻有 \(10\)

位,然後給定一個 \(k (2 \leq k \leq 10)\) ,代表所有數都是 \(k\) 進位制數,

接下來有 \(q\) 次詢問 \((1 \leq q \leq 1e5)\) ,每次詢問給你一個長度不超過 \(10\)\(k\) 進位制數,你需要在 \(n\) 個數中找到一個數和它進行

不進位加法時所得到的值最大,輸出這個最大值。佳爺覺得這題太水了,就出給同學們做了。

Input
第一行兩個正整數 \(n (1 \leq n \leq 1e5)\), \(k (2 \leq k \leq 10)\)

第二行 \(n\) 個整數,每個整數最多隻有 \(10\) 位。

第三行一個整數 \(q\)

\((1 \leq q \leq 1e5)\)
接下來有 \(q\) 行,代表 \(q\) 次詢問,每次給你一個位數不超過 \(10\) 的整數。

Output
輸出有 \(q\) 行,每行對應一個詢問的所求的最大值

Sample Input 2
4 10
998
997
886
885
4
991
998
119
190

Sample Output 2
889
886
995
976

emmm,比賽的時候似乎想到了字典樹。。。不過好像就出現了一秒就被我拋了。。。然後我就一直用二分艹。。。。

QAQ要是我能堅定一下我的字典樹的話這題也就過了。。。

如果能夠看出是字典樹的話,我們就知道,字典樹的操作只有兩種,一個是insert,一個是find,那麼毫無疑問我們當然是直接將n個字串全部插入到字典樹裡面的,不過為了後面的計算,我們要將每個數字補足10位,這樣的話等會找的時候才能好找。

接下來找的話我們先預處理出每個數字在k進位制下的需要匹配的最大數的排名,我們爬鏈找就是按照這個排名順序來找的,如下所示:

ll find(char *s,int rt,int k) {
	ll ans=0;
	int ch[15];
	int nb=0;
	for(int i=0; s[i]; i++) {
		int x=s[i]-'0';
		for (int j=0; j<10; j++){//列舉排名
			if (trie[rt][ranks[x][j]]) {
				ch[nb++]=ranks[x][j]+x;
				rt=trie[rt][ranks[x][j]];
				break;
			}
		}
	}
	for (int i=0; i<10; i++){
		ans=ans*10+(ch[i]%k);
	}
	return ans;
}
********
scanf ("%d%d",&n,&k);
for (int i=0; i<k; i++) {
	for (int j=0; j<k; j++) {
		ranks[i][j]=(k-1-i-j+k)%k;
	}
}

於是,此題便結束了。。。。QAQ

以下是AC程式碼:

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

typedef long long ll;
const int mac=2e6+10;

int trie[mac][12],mark[mac];
int ranks[12][12],tot=0;

void insert(char *s,int rt) {
	for(int i=0; s[i]; i++) {
		int x=s[i]-'0';
		if(trie[rt][x]==0) {   //現在插入的字母在之前同一節點處未出現過
			trie[rt][x]=++tot;   //字母插入一個新的位置
		}
		rt=trie[rt][x];   //為下個字母的插入做準備
	}
	mark[rt]=1;      //對該節點標記為結束(代表再次鏈上以此字母為結尾的存在)
}

ll find(char *s,int rt,int k) {
	ll ans=0;
	int ch[15];
	int nb=0;
	for(int i=0; s[i]; i++) {
		int x=s[i]-'0';
		for (int j=0; j<10; j++){
			if (trie[rt][ranks[x][j]]) {
				ch[nb++]=ranks[x][j]+x;
				rt=trie[rt][ranks[x][j]];
				break;
			}
		}
	}
	for (int i=0; i<10; i++){
		ans=ans*10+(ch[i]%k);
	}
	return ans;
}

char s[15],s1[15];

int main(int argc, char const *argv[])
{
	int n,k;
	scanf ("%d%d",&n,&k);
	for (int i=0; i<k; i++){
		for (int j=0; j<k; j++){
			ranks[i][j]=(k-1-i-j+k)%k;
		}
	}
	for (int i=1; i<=n; i++){
		scanf ("%s",s);
		int len=strlen(s);
		if (len<10) 
			for (int j=0; j<10-len; j++)
				s1[j]='0';
		for (int j=10-len,cnt=0; j<10; j++,cnt++) s1[j]=s[cnt];
		insert(s1,0);
	}
	int q;
	scanf ("%d",&q);
	while (q--){
		scanf ("%s",s);
		int len=strlen(s);
		if (len<10) 
			for (int j=0; j<10-len; j++)
				s1[j]='0';
		for (int j=10-len,cnt=0; j<10; j++,cnt++) s1[j]=s[cnt];
		ll ans=find(s1,0,k);
		printf("%lld\n",ans);
	}
	return 0;
}