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\)
注意不能直接 \(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 \leq \(a_1\) 時,把 \(a_2\) 加上 \(B\),然後把\(a_1\) 加上 \(1\) ,就達成了目標。
但是直接"加上 \(B\)"也不好做,因為沒有"if"。
然後只能把 \(B\) 拆成 \(B\) 個 \(1\) 加在一起了。又要套一個\(10\)次的迴圈,每次判斷是不是要加這個 \(1\)
那麼這個 \(1\) 什麼時候可以加呢?當然是
a[1] <= a[0] && j <= a[3](j 為迴圈變數)
用一個位置 \(a_{4}\) 來儲存 \(j\),重頭戲是如何處理與運算
我們用 \(a_{N - 2}\) 來儲存 \(a_1 <= a_0\), \(a_{N - 3}\) 來儲存 \(a_{4} <= 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);
Puts("< 2 0 199999");
Puts("< 2 1 747");
Puts("+ 199999 747 748");//199999 先存 A>0, 747 748 這兩個下標分別用來存 B>0 和 (A>0) + (B>0),再把 ((A>0) + (B>0)) > 0 存回 199999
Puts("< 2 748 199999");
Puts("+ 3 1 3");
Puts("+ 0 1 0");
rep(i,1,10) {
Puts("< 1 0 199998");
Puts("+ 234 123 4");//清空
rep(j,1,10){
Puts("< 4 3 199997");
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());
}