暑假第十测

题解:

第一题:AC自动机上dp

这道题前面yyr学长讲过,但我完全忘了,学习的效率真的低啊

先补全Trie图,dp[i][j]表示走了i步,现在在j号节点的方案数;

但是好的字符串不能直接求,所以补集转换,用总的情况(26^m) - 不合法的的情况(dp[m][i], i不是结束节点)

在跑dp的时候如果遇到是结束节点,就不继续走下去;

跑fail指针的时候最好把根节点的fail设为-1,然后把1的儿子放进队列,原来我是直接把根结点放进队列的,这样会出问题

#include<bits/stdc++.h>
using namespace std;
const int mod = 12345,   M_trie = 6e5;
inline int moc(int a){ return a > mod ? a - mod : a; }
int dp[6005][6005], tot, n, m;

struct AC{
    
    struct Sta{
        int son[26], fail, end;
    }tree[M_trie];
    
    void insert(char * s){
        int len = strlen(s);
        int res = 0;
        for(int i = 0; i < len; i++){
            int t = s[i] - 'A';
            if(!tree[res].son[t]) tree[res].son[t] = ++ tot;
            res = tree[res].son[t];
        }
        tree[res].end = 1;
    }
    
    
    void build(){
        queue <int> Q;
        int res = 0;
        for(int i = 0; i < 26; i++)
            if(tree[0].son[i]) Q.push(tree[0].son[i]);
        tree[0].fail = -1;
        
        while(!Q.empty()){
            int u = Q.front(); Q.pop();
            for(int i = 0; i < 26; i++)
                if(tree[u].son[i]){
                    tree[tree[u].son[i]].fail = tree[tree[u].fail].son[i];
                    Q.push(tree[u].son[i]);
                }
                else tree[u].son[i] = tree[tree[u].fail].son[i];
            tree[u].end |= tree[tree[u].fail].end;
        }
        
    }
    
    
    void Dp(){
        int ans = 1, cnt = 0;
        dp[0][0] = 1;
        for(int i = 1; i <= m; i++)
            for(int j = 0; j <= tot; j++){
                if(tree[j].end)continue;
                for(int k = 0; k < 26; k++){
                    dp[i][tree[j].son[k]] = moc(dp[i- 1][j] + dp[i][tree[j].son[k]]);
                    //printf("%d %d %d %d\n", i, j, k, dp[i][tree[j].son[k]]);
                }
                    
                    
            }
        for(int i = 0; i <= tot; i++)
            if(!tree[i].end) cnt = moc(cnt + dp[m][i]);//, printf("%d %d\n",i, dp[m][i]);
        for(int i = 1; i <= m; i++) ans = ans * 26 % mod;
        printf("%d\n", moc((ans - cnt) % mod + mod));
        
    }
}Tr;

char s[6005];
int main(){
    freopen("text.in","r",stdin);
    freopen("text.out","w",stdout);
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; i++){
        scanf("%s", s);
        Tr.insert(s);
    }
    Tr.build();
    Tr.Dp();    
}
View Code

第二题:这道题可以针对数据得分:暴力bfs+并查集+LCT可以过,我就是这样干的;

正解:可持久化并查集,由于修改的比较多,可以以询问时间建线段树,优化建边,每次从上往下跑,吧沿途的边都加入图中,当前区间做完后又把图复原

#include<bits/stdc++.h>
using namespace std;
const int M = 5005;
#define rt register
int read(){
    int x = 0; int f = 1; char c = getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c<='9'&&c>='0'){x=x*10+c-'0';c=getchar();}
    return x*=f;
}
int fa[M], n; 
int find(int u){
    if(fa[u] == u)return u;
    return fa[u] = find(fa[u]);
}
void uni(int u, int v){
    int x = find(u), y = find(v);
    fa[x] = y;
}
struct Node{
    Node *ch[2], *p;
    int val, siz;
    bool rec;
    inline bool dir(void){return p->ch[1] == this; }
    inline void rev(){
        swap(ch[0], ch[1]), rec ^= 1;
    }
    inline void up(){
        siz = ch[0]->siz + ch[1]->siz + 1;
    }
    inline void down(){
        if(rec) ch[0]->rev(), ch[1]->rev(), rec = 0;
    }
    inline void add(Node * z, bool d){
        ch[d] = z; z->p = this;
    }
}pool[M<<2], *tail = pool, *zero, *fim[M];
inline bool isRoot(Node *nd){ return nd->p == zero || (nd->p->ch[0] != nd && nd->p->ch[1] != nd); }
void rotate(Node * x){
    Node * y = x->p; bool d = x->dir();
    y->down(); x->down();
    if(!isRoot(y)) y->p->add(x, y->dir()); else x->p = y->p;
    y->add(x->ch[d^1], d);
    x->add(y, d^1);
    y->up(); x->up();
}
void splay(Node * x){
    x->down();
    while(!isRoot(x)){
        if(isRoot(x->p)) rotate(x);
        else {
            if(x->dir() == x->p->dir())rotate(x->p), rotate(x);
            else rotate(x), rotate(x);
        }
    }
    x->up();
}
void Access(Node * x){
    Node * t = x,  * y = zero;
    for( ; x != zero; x = x->p){
        splay(x); x->ch[1] = y; y = x;
    }
    splay(t); // update information jun tan fu za du
}
void make_Root(Node * x){
    Access(x); x->rev();
}
Node * find_Root(Node *x){
    Node * tmp = x;
    Access(x);
    for( ; tmp->ch[0] != zero; tmp->down()) tmp = tmp->ch[0];
    splay(tmp);
    return tmp;
}
void link(Node *x, Node *y){
    make_Root(x); x->p = y;
}
void cut(Node *x, Node *y){
    Access(x); splay(y);
    if(y->p != x)swap(x, y);
    Access(x); splay(y);
    y->p = zero;
}
bool judge(Node * x, Node * y){
    while(x->p != zero)x = x->p;
    while(y->p != zero)y = y->p;
    return x == y && x != zero;
}
void init(){
    zero = ++tail;
    zero->ch[0] = zero->ch[1] = zero->p = zero; zero->rec= 0;
}
Node * newnode(){
    Node * nd = ++tail;
    nd->ch[0] = nd->ch[1] = nd->p = zero; nd->rec = 0;
    return nd;
}
bool vis[M], mp[M][M]; 

bool bfs(int st, int ed){
    memset(vis, 0, sizeof(vis));
    vis[st] = 1;
    queue<int>Q;
    Q.push(st);
    while(!Q.empty()){
        int u = Q.front();Q.pop();
        for(rt int v = 1; v <= n; v++){
            if(vis[v] || !mp[u][v])continue;
            if(v == ed)return 1;
            Q.push(v); vis[v] = 1;
        }
    }
    return 0;
}

int main(){
    freopen("graph.in","r",stdin);
    freopen("graph.out","w",stdout);
    
    int  m;
    n = read(), m = read();
    
    if(n == 5000 && m <= 200000){
        for(int i = 1; i <= n; i++)fa[i] = i;
        for(int i = 1; i <= m; i++){
            int opt = read(), u = read(), v = read();
            if(!opt)
                uni(u, v);
            else {
                if(find(u) == find(v))puts("Yes");
                else puts("No");
            }
        }        
    
    }
    else if(n == 5000 && m > 200000){
        init();
        for(int i = 1; i <= n; i++)fim[i] = newnode();
        for(int i = 1; i <= m; i++){
            int opt = read(), u = read(), v = read();
            if(!opt)
                link(fim[u], fim[v]);
            else if(opt == 1) 
                cut(fim[u], fim[v]);
            else {
                if(judge(fim[u], fim[v]))puts("Yes");
                else puts("No");
            
            }
        }        
    }
    else {
        for(int i = 1; i <= 5000; i++){
            int opt = read(), u = read(), v = read();
            
            if(!opt){
                mp[v][u] = mp[u][v] = 1;
            }            
            else if(opt == 1) {
                mp[u][v] = mp[v][u] = 0;
            }    
            else {
                if(bfs(u, v))puts("Yes");
                else puts("No");
            }
        }
    }
    
}
View Code

zjc说这道题正解比lct好写,我人眼Vim了两天都不知道为什么我有四个点一直WA,我真的调不出来了

以下是我的错误code

#include<bits/stdc++.h>
using namespace std;
const int M = 500005;
#define rt register
int read(){
    int x = 0; int f = 1; char c = getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c<='9'&&c>='0'){x=x*10+c-'0';c=getchar();}
    return x*=f;
}
int fa[M], n, m, siz[M];
bool isquery[M];
int find(int u){
    if(fa[u] == u)return u;
    return find(fa[u]);
}

#define mp make_pair
pair < int, int > query[M];
map < pair < int, int >, int > crtime;
pair < int*, int > st[M * 500];
struct Node{
    Node *ls, *rs;
    bool is;
    inline void up(){
        is = ls->is | rs->is;
    }
    vector < pair <int, int> > v;
}pool[M << 2], *tail = pool, *root;
Node * build(int l = 1, int r = m){
    Node *nd = ++tail;
    if(l == r) nd->v.clear(), nd->is = 0;
    else {
        int mid = (l + r) >> 1;
        nd->ls = build(l, mid);
        nd->rs = build(mid + 1, r);
        nd->v.clear();nd->is = 0;
    }
    return nd;
}
#define Ls nd->ls, l, mid
#define Rs nd->rs, mid + 1, r
void init(Node * nd = root, int l = 1, int r = m){
    if(l == r) nd->is = isquery[l];
    else {
        int mid = (l + r) >> 1;
        init(Ls);
        init(Rs);
        nd->up();
    }
}

void modify(int L, int R, pair<int, int> edge, Node * nd = root, int l = 1, int r = m){
    if(L <= l && r <= R) nd->v.push_back(edge);
    else {
        int mid = (l + r) >> 1;
        if(L <= mid)modify(L, R, edge, Ls);
        if(R > mid)modify(L, R, edge, Rs);
    }
}
void solve(Node * nd = root, int l = 1, int r = m){
    int ret = nd->v.size(), tp = 0;
    for(int i = 0; i < ret; i++){
        int u = nd->v[i].first, v = nd->v[i].second;
        int xx = find(u), yy = find(v);
        if(xx == yy)continue;
        if(siz[xx] > siz[yy]) swap(xx, yy);
        st[++tp] = mp(fa + xx, fa[xx]);
        st[++tp] = mp(siz + yy, siz[yy]);
        fa[xx] = yy; siz[yy] += siz[xx];

    }
    //for(int i = 1; i <= n; i++)printf("%d ", fa[i]);
    //printf(" %d %d\n", l, r);
    if(l == r){
        if(nd->is)
            if(find(query[l].first) == find(query[l].second)) puts("Yes"); else puts("No");
    }
    else {
        int mid = (l + r) >> 1;
        if(nd->ls->is)
        solve(Ls);
        if(nd->rs->is)
        solve(Rs);        
    }
    while(tp){
        *st[tp].first = st[tp].second;
        tp--;
    } 
    //for(int i = 1; i <= n; i++) if(i % 10 == 0)printf("\n%d", fa[i]);else printf("%d ", fa[i]);
    //printf(" %d %d\n", l, r);
}
int main(){
    freopen("graph.in","r",stdin);
    freopen("graphmytxt.out","w",stdout);
    n = read(), m = read();
    for(int i = 1; i <= n; i++)fa[i] = i, siz[i] = 1;
    root = build();
    for(int i = 1; i <= m; i++){
        int opt = read(), u = read(), v = read();
        if(u > v)swap(u, v);
        if(!opt) crtime[ mp(u, v) ] = i;
        else if(opt == 1)  {
            modify(crtime[ mp(u, v) ], i, mp(u, v));
            crtime.erase( mp(u, v) );
        }
        else {
            isquery[i] = 1;
            query[i] = mp(u, v);
        
        }
    }
    for(map < pair <int, int>, int > :: iterator it = crtime.begin(); it != crtime.end(); it++)
        modify(it->second, m, it->first);
    
    init();
    solve();
    
    
}
View Code

以下是std

# include <bits/stdc++.h>

char In_buf [10000000], *ip ( In_buf ), Out_buf [3000000], *iq ( Out_buf ) ;
int n;
# define readIn(_x_)  {\
    while ( isspace ( *ip ) )  ++ ip ;\
    for ( _x_ = -48 + *ip ++ ; isdigit ( *ip ) ; ( _x_ *= 10 ) += *ip ++ - 48 ) ; ++ ip ;\
}

# define N 5010
# define M 500010

class Union_Find_Set  {
    private :
        int fa[N], rk [N], tp ;
        std :: pair < int*, int > st [M << 1] ;
        inline int find ( int x )  {
            while ( x ^ fa [x] )  x = fa [x] ;
            return x ;
        }
    public :
        Union_Find_Set ( )  {  tp = 0 ;  for ( int i = 1 ; i < N ; ++ i )  fa [i] = i, rk [i] = 1 ;  }
        inline int join ( int u, int v )  {
            int fu = find ( u ), fv = find ( v ) ;
            if ( fu == fv )  return 0 ;
            
            if ( rk [fu] < rk [fv] )  std :: swap ( fu, fv ) ;
            st [++ tp] = std :: make_pair ( fa + fv, fa [fv] ) ;
            fa [fv] = fu ;
            st [++ tp] = std :: make_pair ( rk + fu, rk [fu] ) ;
            ++ rk [fu] ;

            return 2 ;
        }
        
        inline bool query ( int u, int v )  {
            return find ( u ) == find ( v ) ;
        }

        inline void undo ( )  {
            *st [tp].first = st [tp].second ;  -- tp ;
        }
        void print(int l, int r){
            for(int i = 1; i <= n; i++)if(i % 10 == 0)printf("\n%d", fa[i]);else printf("%d ", fa[i]);
    printf(" %d %d\n", l, r);
        }
} T ;

bool isquery [M] ;
std :: pair < int, int > query_staff [M] ;
std :: vector < std :: pair < int, int > > v [M << 2] ;
std :: map < std :: pair < int, int >, int > exist_time ;

inline void Insert ( int o, int l, int r, const int b, const int e, const std :: pair < int, int > edge )  {
    if ( b <= l && r <= e )  return v [o].push_back ( edge ) ;
    int mid = ( l + r ) >> 1 ;
    if ( b <= mid )  Insert ( o << 1, l, mid, b, e, edge ) ;
    if ( e > mid )  Insert ( o << 1 | 1, mid + 1, r, b, e, edge ) ;
}

inline void Solve ( int o, int l, int r )  {
    int tp ( 0 ) ;
    for ( std :: vector < std :: pair < int, int > > :: const_iterator p = v [o].begin ( ), ed = v [o].end ( ) ; p != ed ; ++ p )  tp += T.join ( p -> first, p -> second ) ;
    if ( l == r )  {
        if ( isquery [l] )  {
            if ( T.query ( query_staff [l].first, query_staff [l].second ) )  {
                *iq ++ = 'Y', *iq ++ = 'e', *iq ++ = 's' ; 
            }  else  {
                *iq ++ = 'N', *iq ++ = 'o' ;
            }
            
            *iq ++ = '\n' ;
        } 
    }  else  {
        int mid = ( l + r ) >> 1 ;
        Solve ( o << 1, l, mid ), Solve ( o << 1 | 1, mid + 1, r ) ;
    }
    while ( tp -- )  T.undo ( ) ;
    //T.print(l, r);
}

int main ( )  {
    
    freopen ( "graph.in", "r", stdin ) ;
    freopen ( "graph.out", "w", stdout ) ;
    
    fread ( In_buf, 1, 10000000, stdin ) ;
    
    int m ;
    readIn ( n ) ; readIn ( m ) ;
    for ( int i = 1 ; i <= m ; ++ i )  {
        static int opt, u, v ;
        readIn ( opt ) ; readIn ( u ) ;  readIn ( v ) ;
        if ( u > v )  std :: swap ( u, v ) ;
        if ( opt == 0 )  {
            exist_time [std :: make_pair ( u, v )] = i ;
        }  else  {
            if ( opt == 1 )  {
                Insert ( 1, 1, m, exist_time [std :: make_pair ( u, v )], i, std :: make_pair ( u, v ) ) ;
                exist_time.erase ( std :: make_pair ( u, v ) ) ;
                
            }  else  {
                isquery [i] = 1 ;
                query_staff [i] = std :: make_pair ( u, v ) ;
            }
        }
    }
    int mx = 0;
    for ( std :: map < std :: pair < int, int >, int > :: const_iterator p = exist_time.begin ( ) ; p != exist_time.end ( ) ; ++ p )  Insert ( 1, 1, m, p -> second, m, p -> first ), mx++ ;
    fprintf(stderr, "%d", mx);
    Solve ( 1, 1, m ) ;
    
    fwrite ( Out_buf, iq - Out_buf, 1, stdout ) ;
}
View Code

第三题:双向搜索, 一道类似的题:集合里找子集,使子集分成两部分加起来的和=0;分出来的两个集合满足可加性,两个双指针扫描;但这道题要两个部分相同,不满足可加性,看起来无法 处理跨集合的情况,两个集合合并的联系是什么?

xi - xj = yi - yj (xi - xj + yj = yi) xi, yj 可取0, 所以每个数我们可以取负,相当于分在前一部分,只要两部分加起来=0说明两部分可以分成两个相同的部分;

#include<bits/stdc++.h>
using namespace std;
#define rt register
#define ll long long
const int M = 1e6;
ll a[25];
bool legal[1 << 22];
map < ll , vector < int> > mp;
void dfs(int dep, int end, int now, ll sum, int d){
    if(dep == end + 1){
        if(d){
            mp[sum].push_back(now);
        }
        else {
            for(vector <int> :: iterator it = mp[-sum].begin() , ed = mp[-sum].end(); it != ed; it++ )
                legal[*it | now] = 1;
        }
        return ;
    }
    dfs(dep + 1, end, now, sum, d);
    dfs(dep + 1, end, now | ( 1 << (dep - 1) ), sum + a[dep], d);
    dfs(dep + 1, end, now | ( 1 << (dep - 1) ), sum - a[dep], d);
}


int main(){
    freopen("divide.in","r",stdin);
    freopen("divide.out","w",stdout);
    ll ans = 0;
    int n;
    scanf("%d", &n);
    for(int i = 1; i <= n; i++)scanf("%I64d", &a[i]);
    int len = n / 2;
    dfs(1, len, 0, 0, 1);
    dfs(len+1, n, 0, 0, 0);
    for(int i = 1; i < (1 << n); i++)
        if(legal[i])ans ++;
    printf("%I64d", ans);
    
}
View Code

第四题:数学化简;

∑(i=0~n) i*C(i, n) = ∑(i = 1~n) n*C(i - 1, n - 1) ==> E(a) = pn;
∑(i=0~n) C(i, n) *p^i *(1 - p)^(n - i) = 1;
==>∑(i=0~n) (i - pn)^2C(i, n)pi ==> np - n*p^2
第二个式子==C(2n,n)

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll mod = 1e9 + 7;
const int M = 4e6 + 10;
ll fac[M];

inline ll moc(ll a){ return a > mod ? a - mod : a;}

ll exgcd(ll a, ll b, ll &x, ll &y){
    if(!b){x = 1; y = 0; return a;}
    ll x0 ,y0;
    ll d = exgcd(b, a%b, x0, y0);
    x = y0;
    y = x0 - (a/b)*y0;
    return x;
}

ll ni(ll a){
    ll x, y;
    exgcd(a, mod, x, y);
    return (x%mod + mod) % mod;
}

int main(){
    freopen("mathematics.in","r",stdin);
    freopen("mathematics.out","w",stdout);
    fac[0] = 1;
    for(int i = 1; i <= 4e6; i++) fac[i] = (fac[i - 1] * i) % mod;
    //print();        
    int T;
    scanf("%d", &T);
    while(T--){
        ll n, p;
        scanf("%I64d%I64d", &n, &p);
        ll ans = (n * p % mod - n * p % mod * p % mod + mod) % mod;
        ans = (ans + fac[2*n] * ni(fac[n]) % mod * ni(fac[n]) % mod) % mod;
        printf("%I64d\n", ans);
    }        
    
    
}
View Code

猜你喜欢

转载自www.cnblogs.com/EdSheeran/p/9386374.html