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 < 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());
}
posted @ 2020-08-10 15:43  beacon_cwk  阅读(318)  评论(0编辑  收藏  举报