首页 > 其他分享 >NOIP模拟4.5

NOIP模拟4.5

时间:2022-11-20 20:23:41浏览次数:73  
标签:4.5 ch NOIP int long maxn hd && 模拟

给自己搬了4个T并起到了自嗨的效果(:

啊不是吧我连自己搬得“模拟赛”都改不完题!?

 

A. 【BZOJ3012】First!

对每一个字符串分别考虑,先假设它是最小的,需要满足不能有另一个串是他的前缀,并且把它和所有字符串逐位比较,如果出现不相同的那么当前串的对应字符一定更小然后break,最终建出来图不能有环。

有向图判断环的方法:1.tarjan找到的强连通分量个数==总点数;2.拓扑排序后所有点的入度都减为0.

暴力建图会TTT,正确的建图方式是先建一棵Trie树,对同一层的点连边,恰好满足遇到不同及时结束。

toposort
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 3e4 + 2;
const int M = 3e5 + 5;

int n, ans;
bool ok[maxn];
string s[maxn];
queue<int> q;

inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
        if(ch == '-')
        {
            f = -1;
        }
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        x = (x << 1) + (x << 3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

struct Trie
{
    int tot, ch[M][26], in[26], e[26][26];
    bool ed[M];
    inline void insert(string x)
    {
        int u = 1, len = x.size();
        for(int i=0; i<len; i++)
        {
            int v = x[i]-'a';
            if(!ch[u][v]) ch[u][v] = ++tot;
            u = ch[u][v];
        }
        ed[u] = 1;
    }
    inline void toposort()
    {
        while(!q.empty()) q.pop();
        for(int i=0; i<26; i++) if(!in[i]) q.push(i);
        while(!q.empty())
        {
            int u = q.front(); q.pop();
            for(int v=0; v<26; v++)
            {
                if(e[u][v])
                {
                    in[v]--;
                    if(!in[v]) q.push(v);
                }
            }
        }
    }
    inline bool check(string x)
    {
        fill(in, in+26, 0);
        memset(e, 0, sizeof(e));
        int u = 1, len = x.size();
        for(int i=0; i<len; i++)
        {
            if(ed[u]) return 0;
            int v = x[i]-'a';
            for(int j=0; j<26; j++)
            {
                if(j != v && ch[u][j] && !e[v][j])
                {
                    e[v][j] = 1; in[j]++;
                }
            }
            u = ch[u][v];
        }
        toposort();
        for(int i=0; i<26; i++) if(in[i]) return 0;
        return 1;
    }
}tr;

int main()
{
    n = read(); tr.tot = 1;
    for(int i=1; i<=n; i++)
    {
        cin >> s[i];
        tr.insert(s[i]);
    }
    for(int i=1; i<=n; i++)
    {
        if(tr.check(s[i]))
        {
            ans++; ok[i] = 1;
        }
    }
    printf("%d\n", ans);
    for(int i=1; i<=n; i++)
    {
        if(ok[i]) cout << s[i] << endl;
    }

    return 0;
}
tarjan
 #include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 3e4 + 2;
const int M = 3e5 + 5;

int n, ans, num, cnt;
bool ok[maxn], vis[26];
string s[maxn];
stack<int> stk;

inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
        if(ch == '-')
        {
            f = -1;
        }
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        x = (x << 1) + (x << 3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

struct Trie
{
    int tot, ch[M][26], e[26][26], dfn_c, low[26], dfn[26];
    bool ed[M], ext[26];
    inline void insert(string x)
    {
        int u = 1, len = x.size();
        for(int i=0; i<len; i++)
        {
            int v = x[i]-'a';
            if(!ch[u][v]) ch[u][v] = ++tot;
            u = ch[u][v];
        }
        ed[u] = 1;
    }
    inline void tarjan(int x)
    {
        low[x] = dfn[x] = ++dfn_c;
        vis[x] = 1;
        stk.push(x);
        for(int v=0; v<26; v++)
        {
            if(!e[x][v]) continue;
            if(!dfn[v])
            {
                tarjan(v);
                low[x] = min(low[x], low[v]);
            }
            else if(vis[v])
            {
                low[x] = min(low[x], dfn[v]);
            }
        }
        if(low[x] == dfn[x])
        {
            cnt++;
            int y;
            //printf("cnt = %d\n", cnt);
            do 
            {
                y = stk.top(); stk.pop();
                //printf("%d \n", y);
                vis[y] = 0;
            }while(y != x);
        }
    }
    inline bool check(string x)
    {
        fill(ext, ext+26, 0);
        fill(dfn, dfn+26, 0); dfn_c = 0;
        fill(low, low+26, 0);
        fill(vis, vis+26, 0);
        memset(e, 0, sizeof(e));
        int u = 1, len = x.size();
        for(int i=0; i<len; i++)
        {
            if(ed[u]) return 0;
            int v = x[i]-'a';
            for(int j=0; j<26; j++)
            {
                if(j != v && ch[u][j] && !e[v][j])
                {
                    e[v][j] = 1;
                    ext[v] = ext[j] = 1;
                    //printf("add %c %c\n", v+'a', j+'a');
                    //printf("ext[%d] = ext[%d] = 1\n", v, j);
                }
            }
            u = ch[u][v];
        }
        num = cnt = 0;
        for(int i=0; i<26; i++) if(ext[i]) num++;
        for(int i=0; i<26; i++)
        {
            if(ext[i] && !dfn[i]) tarjan(i);
        }
        //printf("cmp: %d %d\n", num, cnt);
        if(num != cnt) return 0;
        return 1;
    }
}tr;

int main()
{
    n = read(); tr.tot = 1;
    for(int i=1; i<=n; i++)
    {
        cin >> s[i];
        tr.insert(s[i]);
    }
    for(int i=1; i<=n; i++)
    {
        if(tr.check(s[i]))
        {
            ans++; ok[i] = 1;
        }
    }
    printf("%d\n", ans);
    for(int i=1; i<=n; i++)
    {
        if(ok[i]) cout << s[i] << endl;
    }

    return 0;
}

 

B. 庆典

有向图每一次询问查询既满足s可达 i 又满足 i 可达t的 i 的数量,每个询问会加入k<=2条临时的边。所以就有一种暴力的写法是分别从s和t开始两次dfs染色(t走反图),找到公共点的数量。

但是没有用到一个重要的性质,指向同一个点的两点之间一定有边相连,分析m=n-1的数据发现每个点的入度最多是1,因为不存在环所以至少有一个点没有入度,如果有两个点没有入度的话他们永远无法相遇,所以这是一棵真正的树,s的可达范围就是它的子树,t的(反图)可达范围就是它到根的距离,k=0可以直接dep相减。So I have 36 only.

关于有向图的连通性,先缩点没有影响,得到的有向无环图一定会有一个没有入度的点,这个点可以到达所有点。

引用一下吧:

所以现在这棵树的形态就像这样:(引用自OUYE2020)

统计每个点可达范围的时候,这些“跨代”的红色边对“子树”和“到根的链”这两种范围都没有用,用拓扑排序把它们去掉(具体就是每个点只留下使它入度减为0的最后一条边),就变成了和部分分一样的树。

考虑有加边的情况:新的边可能使s拓展了一个新的子树,也可能使t获得了一条新的到根的反路,对子树和到根的链都只记录端点,对属于同一棵子树或同一条反向链的端点去重,本来端点就不可能到达的边不加入,由于k<=2需要存储的内容很少。

我没有图就只好再截一个了:

判断在子树内就用dfn进出的值比较大小就好了,我本来特判树上k=1的数据打算通过多次求LCA来判断这个结果写挂了……

还了解了一下vector区间删除第一个地址是起点,第二个是终点的下一个。

code是鹤的
 #include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 3e5 + 4;

int n, m, Q, k, root, f[maxn][22], bl[maxn], u1, v1, u2, v2;
int dfn[maxn], dfn_c, low[maxn], hd[maxn], tl[maxn], IN, fa[maxn];
bool vis[maxn];
vector<int> a, b, G1[maxn], G2[maxn], G[maxn];
int dp[maxn], dep[maxn], du[maxn], siz[maxn], num;
queue<int> q;
stack<int> stk;

inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
        if(ch == '-')
        {
            f = -1;
        }
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        x = (x << 1) + (x << 3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

void tarjan(int x)
{
    low[x] = dfn[x] = ++dfn_c; stk.push(x); vis[x] = 1;
    for(int v : G1[x])
    {
        if(!dfn[v]) tarjan(v), low[x] = min(low[x], low[v]);
        else if(vis[v]) low[x] = min(low[x], dfn[v]);
    }
    if(dfn[x] == low[x])
    {
        num++; int y;
        do 
        {
            y = stk.top(); stk.pop();
            bl[y] = num; vis[y] = 0;
        }while(y != x);
    }
}

void topo()
{
    for(int i=1; i<=num; i++) if(!du[i]) root = i, q.push(i);
    while(!q.empty())
    {
        int x = q.front(); q.pop();
        for(int v : G2[x])
        {
            du[v]--;
            if(!du[v]) q.push(v), fa[v] = x, G[x].push_back(v);
        }
    }
}

void dfs(int x)
{
    hd[x] = ++IN; f[x][0] = fa[x];
    dp[x] = dp[fa[x]] + siz[x], dep[x] = dep[fa[x]] + 1;
    for(int i=1; i<20; i++) f[x][i] = f[f[x][i-1]][i-1];
    for(int v : G[x]) dfs(v);
    tl[x] = IN;
}

int lca(int u, int v)
{
    if(dep[u] < dep[v]) swap(u, v);
    for(int i=19; i>=0; i--) if(dep[f[u][i]] >= dep[v]) u = f[u][i];
    if(u == v) return u;
    for(int i=19; i>=0; i--)
    {
        if(f[u][i] != f[v][i]) u = f[u][i], v = f[v][i];
    }
    return f[u][0];
}

void addedge(int u, int v)
{
    bool ok = 0;
    for(int i=0; i<(int)a.size(); i++)
    {
        if(hd[u]>=hd[a[i]]&&hd[u]<=tl[a[i]]) ok = 1;
    }
    if(ok)
    {
        int h = 0;
        for(int i=0; i<(int)a.size()&&h>=0; i++)
        {
            if(hd[v]>=hd[a[i]]&&hd[v]<=tl[a[i]]) h = -1;
            if(h>=0&&hd[a[i]]>hd[v]&&hd[a[i]]<=tl[v]) h = 1, a[i] = v;
        }
        for(int i=0; i<(int)a.size(); i++)
        {
            bool ok = 1;
            for(int j=0; j<i; j++) if(a[j] == a[i]) ok = 0;
            if(!ok) a.erase(a.begin()+i, a.begin()+i+1), i--;
        }
        if(h == 0) a.push_back(v);
    }
    ok = 0;
    for(int i=0; i<(int)b.size(); i++)
    {
        if(hd[b[i]]>=hd[v]&&hd[b[i]]<=tl[v]) ok = 1;
    }
    if(ok)
    {
        int h = 0;
        for(int i=0; i<(int)b.size()&&h==0; i++)
        {
            if(hd[b[i]]>=hd[u]&&hd[b[i]]<=tl[u]) h = -1;
            if(h==0&&hd[u]>hd[b[i]]&&hd[u]<=tl[b[i]]) h = 1, b[i] = u;
        }
        if(h == 0) b.push_back(u);
    }
}

int main()
{
    n = read(), m = read(), Q = read(), k = read();
    for(int i=1; i<=m; i++)
    {
        int u = read(), v = read();
        G1[u].push_back(v);
    }
    for(int i=1; i<=n; i++) if(!dfn[i]) tarjan(i);
    for(int x=1; x<=n; x++)
    {
        siz[bl[x]]++;
        for(int v : G1[x])
        {
            if(bl[v] != bl[x])
            {
                G2[bl[x]].push_back(bl[v]); du[bl[v]]++;
            }
        }
    }
    topo(); dfs(root);
    while(Q--)
    {
        int s = read(), t = read();
        a.clear(), b.clear(); u1 = v1 = u2 = v2 = 0;
        a.push_back(bl[s]), b.push_back(bl[t]);
        if(k > 0) u1 = bl[read()], v1 = bl[read()];
        if(k > 1) u2 = bl[read()], v2 = bl[read()];
        if(u1 == v1) u1 = v1 = 0;
        if(u2 == v2) u2 = v2 = 0;
        if(u1) addedge(u1, v1);
        if(u2) addedge(u2, v2);
        if(u1) addedge(u1, v1);
        int ans = 0;
        for(int i=0; i<(int)a.size(); i++)
        {
            for(int j=0; j<(int)b.size(); j++)
            {
                int c = a[i], d = b[j];
                if(hd[d]>=hd[c]&&hd[d]<=tl[c])
                {
                    int lc = fa[c], glc;
                    for(int l=0; l<j; l++)
                    {
                        glc = lca(d, b[l]);
                        if(dep[glc] > dep[lc]) lc = glc;
                    }
                    ans += dp[d]-dp[lc];
                }
            }
        }
        printf("%d\n", ans);
    }

    return 0;
}

 

C.字符串问题

数据点4用hash判断前缀得到 10 pts。

code 10%
 #include <bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
const int maxn = 2e5 + 5;

int T, na, nb, L, R, m;
ull jc[maxn], h[maxn];
char s[maxn];

inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
        if(ch == '-')
        {
            f = -1;
        }
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        x = (x << 1) + (x << 3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

int main()
{
    T = read();
    jc[0] = 1;
    for(int i=1; i<=2e5; i++)
    {
        jc[i] = jc[i-1] * 131;
    }
    while(T--)
    {
        scanf("%s", s+1);
        int len = strlen(s+1);
        for(int i=1; i<=len; i++)
        {
            h[i] = h[i-1] * 131 + s[i] - 'A';
        }
        na = read();
        assert(na == 1);//针对4
        L = read(), R = read();
        nb = read();
        bool flag = 0;
        for(int i=1; i<=nb; i++)
        {
            int l = read(), r = read();
            if(flag) continue;
            if(h[r]-h[l-1]*jc[r-l+1] == h[L+r-l]-h[L-1]*jc[r-l+1])
            {
                flag = 1;
            }
        }
        m = read();
        for(int i=1; i<=m; i++) read(), read();
        if(flag) printf("-1\n");
        else printf("%d\n", R-L+1);
    }

    return 0;
}

 

D.选课

重名的题好多啊,差点以为是那个树形dp的……

二进制枚举 15%
 //小C你热爱学习??热爱学分吧……
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 25;
const ll inf = 0x3f3f3f3f3f3f3f3f;

int m, T, n[maxn], s[maxn], w[maxn][maxn], c[maxn][maxn], ch[maxn], gt;
int p, del[maxn][maxn], add[maxn][maxn], id[maxn][maxn], tot;
typedef pair<int, int> pii;
pii pos[maxn];
bool nt[maxn][maxn];
ll sum[maxn], res, sum2, ans=inf;

inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
        if(ch == '-')
        {
            f = -1;
        }
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        x = (x << 1) + (x << 3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

void check(int num)
{
    res = 0; sum2 = 0; gt = 0;
    for(int i=1; i<=m; i++) sum[i] = 0;
    for(int i=1; i<=tot; i++)
    {
        if(num & (1<<i-1)) ch[++gt] = i;
    }
    for(int i=1; i<=gt; i++)
    {
        pii now = pos[ch[i]];
        sum[now.first] += w[now.first][now.second];
        sum2 += w[now.first][now.second];
        res += c[now.first][now.second];
    }
    if(sum2 < T) return;
    for(int i=1; i<=m; i++)
    {
        if(sum[i] < s[i]) return;
    }
    for(int i=1; i<=gt; i++)
    {
        for(int j=1; j<i; j++)
        {
            res -= del[ch[i]][ch[j]];
            res += add[ch[i]][ch[j]];
            if(nt[ch[i]][ch[j]]) return;
        }
    }
    ans = min(ans, res);
}

int main()
{
    m = read(); T = read();
    for(int i=1; i<=m; i++)
    {
        n[i] = read(), s[i] = read();
        for(int j=1; j<=n[i]; j++)
        {
            w[i][j] = read(), c[i][j] = read();
            id[i][j] = ++tot;
            pos[tot] = pii(i, j);
        }
    }
    p = read();
    for(int i=1; i<=p; i++)
    {
        int opt = read();
        if(opt == 1)
        {
            int x1 = read(), y1 = read(), x2 = read(), y2 = read(), c = read();
            del[id[x1][y1]][id[x2][y2]] = del[id[x2][y2]][id[x1][y1]] = c;
        }
        else if(opt == 2)
        {
            int x1 = read(), y1 = read(), x2 = read(), y2 = read(), c = read();
            add[id[x1][y1]][id[x2][y2]] = add[id[x2][y2]][id[x1][y1]] = c;
        }
        else 
        {
            int x1 = read(), y1 = read(), x2 = read(), y2 = read();
            nt[id[x1][y1]][id[x2][y2]] = nt[id[x2][y2]][id[x1][y1]] = 1;
        }
    }
    int mx = 1 << tot;
    for(int i=0; i<mx; i++)
    {
        check(i);
    }
    printf("%lld\n", ans==inf?-1:ans);

    return 0;
}

 

标签:4.5,ch,NOIP,int,long,maxn,hd,&&,模拟
From: https://www.cnblogs.com/Catherine2006/p/16909414.html

相关文章