AtCoder Grand Contest 047 部分题解
本题解包含 \(A,B\) 以及 \(E\) 的前 \(800\) 分。
大家都知道直接模拟是肯定不行的。感觉很难做?
然后我们要注意“小数点后最多\(9\)位”这个条件,同时乘上 \(10^9\) ,它们就都变成了整数。
可以发现,如果其中两个数原是 \(a,b\),现是 \(a',b'\),\(a \times b\) 为整数,则 \(a' \times b'\) 能被 \(10^{18}\) 整除。对每个数,记一下因数 \(2\) 和 \(5\) 的数量,拿一个桶存下来,最后枚举 \(2\) 和 \(5\) 的数量,统计一下就好了。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define mit map<int,int>::iterator
#define sit set<int>::iterator
#define itrm(g,x) for(mit g=x.begin();g!=x.end();g++)
#define itrs(g,x) for(sit g=x.begin();g!=x.end();g++)
#define ltype int
#define rep(i,j,k) for(ltype(i)=(j);(i)<=(k);(i)++)
#define rap(i,j,k) for(ltype(i)=(j);(i)<(k);(i)++)
#define per(i,j,k) for(ltype(i)=(j);(i)>=(k);(i)--)
#define pii pair<int,int>
#define fi first
#define se second
#define mpr make_pair
#define pb push_back
#define fastio ios::sync_with_stdio(false)
const int inf=0x3f3f3f3f,mod=1000000007;
const double pi=3.1415926535897932,eps=1e-6;
void chmax(int &x,int y){if(x < y) x = y;}
void chmin(int &x,int y){if(x > y) x = y;}
int n;ll pw[20],num[200005],ans;
string s[200005];
ll t[105][105];
int main()
{
pw[0] = 1;rep(i,1,18) pw[i] = pw[i - 1] * 10;
fastio;
cin>>n;
rep(i,1,n) {
cin>>s[i];
int pos = -1;
rap(j,0,s[i].size()) if(s[i][j] == '.') pos = j;
if(pos == -1) pos = s[i].size();
rap(j,0,pos) num[i] += (s[i][j] - '0') * pw[pos + 8 - j];
rap(j,pos+1,s[i].size()) num[i] += (s[i][j] - '0') * pw[9 - (j - pos)];
//cout<<num[i]<<endl;
}
rep(i,1,n) {
int num2 = 0,num5 = 0;
while(num[i] % 2 == 0) num2++,num[i] /= 2;
while(num[i] % 5 == 0) num5++,num[i] /= 5;
if(num2 >= 9 && num5 >= 9) ans--;
t[num2][num5]++;
}
rep(two,0,64) rep(fiv,0,64) {
int nedtwo = max(18 - two, 0);
int nedfiv = max(18 - fiv, 0);
rep(k,nedtwo,64) rep(l,nedfiv,64) ans += t[two][fiv] * t[k][l];
}
cout<<ans / 2;
return 0;
}
容易发现,一个串 \(S\) 能被另外一个串 \(T\) 形成,当且仅当:\(T\) 除去第一个字符后,是 \(S\) 的后缀,且 \(T\) 的第一个字符在 \(S\) 的前面部分出现了。
这样很容易想到,把所有串反转后插到一棵 \(Trie\) 里。
实现起来还是有点困难的。
可以令 \(suf_i\) 表示第 \(i\) 个节点下面单词结尾的个数,\(suf2_{i,j}\) 表示从第 \(i\) 个节点往下走,走到一个单词结尾 \(w\) ,路径上至少经过一个字符 \(j\) 的 \(w\) 的数量。
对于每个串,设长度为 \(len\) ,先走 \(len - 1\) 步,走不到就一定不合法(其实不用判断,一开始就把所有的串插入进来了,所以一定能走到),然后现在走到了结点 \(cur\) ,对 \(cur\) 的每个儿子\(son\),把答案增加 \(suf2_{son,lastchar}\)。最后去掉重复计算的。
注意不能直接 \(suf2_{cur,lastchar}\),不然这组数据会挂掉:
2
ewq
eewq
"ewq" 会把 "eewq"统计进去,去重处理之后,会输出 \(2\)。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define mit map<int,int>::iterator
#define sit set<int>::iterator
#define itrm(g,x) for(mit g=x.begin();g!=x.end();g++)
#define itrs(g,x) for(sit g=x.begin();g!=x.end();g++)
#define ltype int
#define rep(i,j,k) for(ltype(i)=(j);(i)<=(k);(i)++)
#define rap(i,j,k) for(ltype(i)=(j);(i)<(k);(i)++)
#define per(i,j,k) for(ltype(i)=(j);(i)>=(k);(i)--)
#define pii pair<int,int>
#define fi first
#define se second
#define mpr make_pair
#define pb push_back
#define fastio ios::sync_with_stdio(false)
const int inf=0x3f3f3f3f,mod=1000000007;
const double pi=3.1415926535897932,eps=1e-6;
void chmax(int &x,int y){if(x < y) x = y;}
void chmin(int &x,int y){if(x > y) x = y;}
int n;ll ans;
string s[200005];
int son[1000005][27],tag[1000005];
char typ[1000005];
int ex[1000005][27],suf1[1000005],suf2[1000005][27];/*ex 是无用数组*/
int num;
//root is 0
void ins(string s){
int cur = 0;
rap(i,0,s.size()){
if(!son[cur][s[i] - 'a']) {
son[cur][s[i] - 'a'] = ++num;
typ[num] = s[i];
cur = num;
}
else cur = son[cur][s[i] - 'a'];
}
tag[cur]++;
}
void dfs(int x){
ex[x][typ[x] - 'a'] ++;//无用语句
suf1[x] += tag[x];
rep(i,0,25){
if(son[x][i]) {
dfs(son[x][i]);
suf1[x] += suf1[son[x][i]];
rep(j,0,25) {ex[x][j] += ex[son[x][i]][j]; suf2[x][j] += suf2[son[x][i]][j];}
}
}
rep(ch,0,25) {
if(typ[x] == ch + 'a') suf2[x][ch] = suf1[x];
}
//cout<<x<<' '<<typ[x]<<' '<<suf2[x][2]<<'\n';
}
int main()
{
fastio;
cin>>n;
rep(i,1,n) {cin>>s[i];reverse(s[i].begin(),s[i].end());}
rep(i,1,n) ins(s[i]);
dfs(0);
rep(i,1,n) {
//walk
int nwlen = (int)s[i].size() - 1;
int cur = 0;
bool flg = 0;
rap(j,0,nwlen){
int to = son[cur][s[i][j] - 'a'];
if(!to) {flg = 1;break;}
cur = to;
}
if(flg) continue;
//cout<<i<<' '<<cur<<'\n';
char lst = s[i].back();
rep(ch,0,25) if(son[cur][ch]) ans += suf2[son[cur][ch]][lst - 'a'];
//cout<<lst<<' '<<suf2[cur][lst - 'a']<<'\n';
}
ans -= n;
cout<<ans<<'\n';
return 0;
}
本人思路有点奇怪,希望大家能看懂。。。
为了解释清晰一点,令 \(N = 200000\)。
\(A,B \leq 10\) 看起来比较好搞。
我们想办法把 \(a_{N - 1}\) 搞成 \(1\) 备用。
考虑把 \(a_2\) 加上 \(A\) 个 \(B\) ,这样就是 \(A \times B\) 了
于是在 \(a_3\) 中存上 \(B\) 的副本,然后把 \(a_0\) 加上 \(B\) ,变成 \(A + B\) ,这样只要循环\(10\)次,每次当 \(a_0 < a_1\) 时,把 \(a_2\) 加上 \(B\),然后把\(a_1\) 加上 \(1\) ,就达成了目标。
但是直接"加上 \(B\)"也不好做,因为没有"if"。
然后只能把 \(B\) 拆成 \(B\) 个 \(1\) 加在一起了。又要套一个\(10\)次的循环,每次判断是不是要加这个 \(1\)
那么这个 \(1\) 什么时候可以加呢?当然是
a[1] < a[0] && j - 1 < a[3](j 为循环变量)
用一个位置 \(a_{4}\) 来储存 \(j\),重头戏是如何处理与运算
我们用 \(a_{N - 2}\) 来储存 \(a_1 \leq a_0\), \(a_{N - 3}\) 来储存 \(a_{4} \leq a_3\) ,\(a_{N - 4}\) 来储存 \(a_{N - 2} + a_{N - 3}\)。只要 \(1 < a_{N - 4}\) 成立,那么上式为真。这样就可以做了。
还有一个小问题:\(A,B\) 有可能等于 \(0\),如何确保 \(a_{N - 1}\) 等于 \(1\) ?
如果 \(A\) 与 \(B\) 至少一个大于 \(0\),令 \(a_{N - 1} = (A > 0) || (B > 0)\) 即可(“与”会了,“或”应该也会了)(注:我一开始傻了,好像直接判断 \(A+B > 0\) 就 ok 了...)
否则,无论怎样操作,都不可能搞出任何一个大于 \(0\) 的数,算出的答案一定是 \(0\),我们也就不用管了。
程序中出现了各种各样奇怪的下标,有些特殊的做了注释,其他的自行转换一下吧。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define mit map<int,int>::iterator
#define sit set<int>::iterator
#define itrm(g,x) for(mit g=x.begin();g!=x.end();g++)
#define itrs(g,x) for(sit g=x.begin();g!=x.end();g++)
#define ltype int
#define rep(i,j,k) for(ltype(i)=(j);(i)<=(k);(i)++)
#define rap(i,j,k) for(ltype(i)=(j);(i)<(k);(i)++)
#define per(i,j,k) for(ltype(i)=(j);(i)>=(k);(i)--)
#define pii pair<int,int>
#define fi first
#define se second
#define mpr make_pair
#define pb push_back
#define fastio ios::sync_with_stdio(false)
const int inf=0x3f3f3f3f,mod=1000000007;
const double pi=3.1415926535897932,eps=1e-6;
void chmax(int &x,int y){if(x < y) x = y;}
void chmin(int &x,int y){if(x > y) x = y;}
string buf[44444];
int top;
void Puts(string s){buf[++top] = s;}
int main()
{
//freopen("code.txt","w",stdout);
//注意这四行 a[2] 是当作 0 来用的
Puts("< 2 0 199999");
Puts("< 2 1 747");
Puts("+ 199999 747 748");
Puts("< 2 748 199999");//199999 先存 A>0, 747 748 这两个下标分别用来存 B>0 和 (A>0) + (B>0),再把 ((A>0) + (B>0)) > 0 存回 199999
Puts("+ 3 1 3");//a[3] = B
Puts("+ 0 1 0");//a[0] += B
rep(i,1,10) {
Puts("< 1 0 199998");//a[1] < a[0]
Puts("+ 234 123 4");//清空
rep(j,1,10){
Puts("< 4 3 199997");//j - 1 < a[3]
Puts("+ 199998 199997 199996");
Puts("< 199999 199996 199995");
Puts("+ 2 199995 2");//统计答案
Puts("+ 4 199999 4");//循环变量增加
}
Puts("+ 1 199999 1");//循环变量增加
}
printf("%d\n",top);
rep(i,1,top) printf("%s\n",buf[i].c_str());
}