1. 程式人生 > 實用技巧 >AtCoder Grand Contest 047 部分題解

AtCoder Grand Contest 047 部分題解

本題解包含 \(A,B\) 以及 \(E\) 的前 \(800\) 分。

AGC047A

大家都知道直接模擬是肯定不行的。感覺很難做?

然後我們要注意“小數點後最多\(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;
}

AGC047B

容易發現,一個串 \(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;
}

AGC047E

本人思路有點奇怪,希望大家能看懂。。。

為了解釋清晰一點,令 \(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());
}