• 国庆训练


    CERC 2017

    A 模拟

    #include <bits/stdc++.h>
    using namespace std;
    int r, n;
    char s[55][15];
    int main() {
        scanf("%d%d", &r, &n);
        for (int i=1; i<=r+3; ++i) scanf("%s", s[i]);
        int cntl = 0, cntr = 0;
        for (int i=1; i<=r+3; ++i) {
            for (int j=0; j<=10; ++j) if (s[i][j]=='-') {
                if (j<=4) ++cntl;
                if (j>=6) ++cntr;
            }
        }
        char now = 'a';
        while (n--) {
            int cnt1 = 0, cnt2 = 0;
            for (int i=0; i<=10; ++i) { 
                if (s[2][i]=='-') ++cnt1;
                if (s[r/2+3][i]=='-') ++cnt2;
            }
            auto gao = [&](int x) {
                if (s[x][4]=='-'&&(s[x][6]!='-'||cntl>=cntr)) s[x][4]=now++,cntl--;
                else if (s[x][6]=='-') s[x][6]=now++,cntr--;
                else if (s[x][2]=='-'&&(s[x][8]!='-'||cntl>=cntr)) s[x][2]=now++,cntl--;
                else if (s[x][8]=='-') s[x][8]=now++,cntr--;
                else if (s[x][0]=='-'&&(s[x][10]!='-'||cntl>=cntr)) s[x][0]=now++,cntl--;
                else if (s[x][10]=='-') s[x][10]=now++,cntr--;
                else if (s[x][5]=='-') s[x][5]=now++;
                else if (s[x][1]=='-'&&(s[x][9]!='-'||cntl>=cntr)) s[x][1]=now++,cntl--;
                else s[x][9]=now++,cntr--;
            };
            if (cnt1||cnt2) {
                if (cnt1>=cnt2) gao(2);
                else gao(r/2+3);
            }
            else {
                int cnt = -1, pos = 0;
                for (int i=1; i<=r+3; ++i) {
                    int sum = 0;
                    for (int j=0; j<=10; ++j) if (s[i][j]=='-') ++sum;
                    if (sum>cnt) cnt = sum, pos = i;
                    else if (sum==cnt) {
                        if (min({abs(i-1),abs(i-r/2-2),abs(i-r-3)})<min({abs(pos-1),abs(pos-r/2-2),abs(pos-r-3)})) cnt = sum, pos = i;
                    }
                }
                gao(pos);
            }
        }
        for (int i=1; i<=r+3; ++i) puts(s[i]);
    }
    View Code

    预处理出从每行第一列开始, 走$c$步回到哪一行, 这样找循环节可以$O(n+m)$求出移动$k$次位置

    修改操作可以发现只会影响能到达$(x-1,y-1),(x,y-1),(x+1,y-1)$这三个点的行, 这是一段连续的区间, 可以$O(m)$维护

    F

    $nle 2p$时, $r$只能为$0$

    $nle p$时, $r$不为$0$时必须修改位置$p$

    $n<p$时, 枚举修改位置

    #include <bits/stdc++.h>
    using namespace std;
    int qpow(long long a, int n, int p) {
        long long ans = 1;
        for (; n; a=a*a%p,n>>=1) if (n&1) ans=ans*a%p;
        return ans;
    }
    int main() {
        long long n;
        int p, r;
        scanf("%lld%d%d", &n, &p, &r);
        if (n>=2*p) {
            if (r) puts("-1 -1");
            else puts("2 1");
            return 0;
        }
        if (n>=p) {
            if (r==0) {
                if (p==2) {
                    if (n==2) puts("-1 -1");
                    else puts("3 1");
                }
                else puts("2 1");
            }
            else {
                long long now = 1;
                for (int i=1; i<=n; ++i) if (i%p) now = now*i%p;
                for (int i=1; i<p; ++i) {
                    if (now*i%p==r) return printf("%d %d
    ", p, i), 0;
                }
                puts("-1 -1");
            }
            return 0;
        }
        long long now = 1;
        for (int i=1; i<=n; ++i) now = now*i%p;
        now = qpow(now,p-2,p);
        for (int k=2; k<=n; ++k) {
            int v = now*r%p*k%p;
            if (1<=v&&v<k) return printf("%d %d
    ", k, v), 0;
        }
        puts("-1 -1");
    }
    View Code

    G

    如果抽到比当前点期望小的点, 那么一定要走, 否则扔掉票

    所以直接从$n$跑$dijkstra$即可

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 3e5+10;
    int n, m, deg[N], cnt[N];
    bool vis[N];
    double ans[N],sum[N];
    vector<int> g[N];
    int main() {
        scanf("%d%d", &n, &m);
        while (m--) { 
            int u, v;
            scanf("%d%d", &u, &v);
            g[u].push_back(v);
            g[v].push_back(u);
            ++deg[u],++deg[v];
        }
        for (int i=1; i<=n; ++i) ans[i] = 1e20;
        ans[n] = 0;
        priority_queue<pair<double,int>,vector<pair<double,int>>,greater<pair<double,int>>> q;
        q.push({0,n});
        while (q.size()) {
            int x = q.top().second; q.pop();
            if (vis[x]) continue;
            vis[x] = 1;
            for (int y:g[x]) if (!vis[y]) {
                ++cnt[y];
                sum[y] += ans[x];
                double w = (sum[y]+deg[y])/cnt[y];
                q.push({ans[y]=w,y});
            }
        }
        printf("%.20lf
    ", ans[1]);
    }
    View Code

    H

    模拟

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 1e5+10;
    int n, tot, t, sz[N], fa[N];
    char s[N];
    vector<int> g[N];
    string val[N],val2[N];
    map<string,int> mp;
    
    int ID(string s) {
        if (mp.count(s)) return mp[s];
        mp[s] = ++tot;
        val2[tot] = s;
        for (int i=s.size()-1; i>=0; --i) if (s[i]=='/') {
            val[tot] = s.substr(i+1,s.size()-1-i);
            break;
        }
        return tot;
    }
    void dfs(int x) {
        sort(g[x].begin(),g[x].end(),[](int a,int b){return val[a]<val[b];});
        for (int y:g[x]) dfs(y),sz[x]+=sz[y];
    }
    void dfs2(int x) {
        int cnt = 0, mx = 0;
        for (int y:g[x]) if (g[y].size()) {
            ++cnt;
            mx = max(mx, sz[y]);
        }
        if (!cnt) cout<<"  "<<val2[x]<<"/ "<<sz[x]<<'
    ';
        else if (mx<t) cout<<"+ "<<val2[x]<<"/ "<<sz[x]<<'
    ';
        else {
            cout<<"- "<<val2[x]<<"/ "<<sz[x]<<'
    ';
            for (int y:g[x]) if (g[y].size()) dfs2(y);
        }
    }
    int main() {
        ID("");
        scanf("%d", &n);
        while (n--) {
            int u;
            scanf("%s%d", s+1, &u);
            int len = strlen(s+1), pre = 1;
            for (int i=2; i<=len; ++i) {
                int j = i;
                while (j<=len&&s[j]!='/') ++j;
                int x = ID(string(s+1,s+j));
                if (j>len) sz[x] = u;
                if (!fa[x]) fa[x] = pre, g[pre].push_back(x);
                pre = x, i = j;
            }
        }
        scanf("%d", &t);
        dfs(1);
        dfs2(1);
    }
    View Code

    I

    对于位置$p_i,p_{i+1}$, 假设它们之间的数坐标范围在$[l,r]$, 那么就把$i$向$[l,r)$连边

    那么一个询问的答案就是区间能到达的最小位置和最大位置

    可以用倍增优化建图, 然后强连通分量缩点跑拓扑排序

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 1e5+10, LOG = 18;
    int n, m, tot, a[N], pos[N], Log[N], L2[N], R2[N];
    int dfn[N*LOG], low[N*LOG], sccno[N*LOG];
    int s[N*LOG], s_top, clk, scnt;
    int L[N*LOG],R[N*LOG],deg[N*LOG];
    vector<int> g[N*LOG], f[N*LOG];
    queue<int> q;
    struct {
        int mi[N][LOG], ma[N][LOG], id[N][LOG];
        void init(int n, int *a) {
            for (int i=1; i<=n; ++i) { 
                mi[i][0] = ma[i][0] = a[i];
                id[i][0] = i;
            }
            for (int j=1; j<LOG; ++j) for (int i=1; i+(1<<j)-1<=n; ++i) {
                id[i][j] = ++tot;
                g[id[i][j]].push_back(id[i][j-1]);
                g[id[i][j]].push_back(id[i+(1<<j-1)][j-1]);
                mi[i][j] = min(mi[i][j-1], mi[i+(1<<j-1)][j-1]);
                ma[i][j] = max(ma[i][j-1], ma[i+(1<<j-1)][j-1]);
            }
        }
        void init(int n, int *L, int *R) {
            for (int i=1; i<=n; ++i) mi[i][0] = L[i], ma[i][0] = R[i];
            for (int j=1; j<LOG; ++j) for (int i=1; i+(1<<j)-1<=n; ++i) {
                mi[i][j] = min(mi[i][j-1], mi[i+(1<<j-1)][j-1]);
                ma[i][j] = max(ma[i][j-1], ma[i+(1<<j-1)][j-1]);
            }
        }
        void add_edge(int x, int l, int r) {
            int t = Log[r-l+1];
            r -= (1<<t)-1;
            g[x].push_back(id[l][t]);
            g[x].push_back(id[r][t]);
        }
        pair<int,int> rmq(int l, int r) {
            int t = Log[r-l+1];
            r -= (1<<t)-1;
            return {min(mi[l][t],mi[r][t]),max(ma[l][t],ma[r][t])};
        }
    } st;
    void dfs(int x) {
        dfn[s[++s_top]=x]=low[x]=++clk;
        for (int y:g[x]) {
            if (!dfn[y]) dfs(y),low[x]=min(low[x],low[y]);
            else if (!sccno[y]) low[x]=min(low[x],dfn[y]);
        }
        if (low[x]==dfn[x]) for(int u=++scnt; sccno[u=s[s_top--]]=scnt, u!=x; );
    }
    int main() {
        scanf("%d", &n);
        for (int i=1; i<=n; ++i) scanf("%d", &a[i]), pos[a[i]] = i;
        Log[0] = -1;
        for (int i=1; i<=n; ++i) Log[i] = Log[i>>1]+1;
        tot = n;
        st.init(n, pos);
        for (int i=1; i<n; ++i) {
            int lx = a[i], rx = a[i+1], l, r;
            if (lx>rx) swap(lx, rx);
            tie(l,r) = st.rmq(lx, rx);
            if (l!=r-1) st.add_edge(i,l,r-1);
        }
        for (int i=1; i<=tot; ++i) { 
            if (!dfn[i]) dfs(i);
            L[i] = n;
        }
        for (int i=1; i<=tot; ++i) {
            if (i<n) {
                L[sccno[i]] = min(L[sccno[i]], i);
                R[sccno[i]] = i;
            }
            for (int j:g[i]) if (sccno[i]!=sccno[j]) {
                f[sccno[j]].push_back(sccno[i]), ++deg[sccno[i]];
            }
        }
        for (int i=1; i<=scnt; ++i) if (!deg[i]) q.push(i);
        while (q.size()) {
            int x = q.front(); q.pop();
            for (int y:f[x]) {
                L[y] = min(L[y], L[x]);
                R[y] = max(R[y], R[x]);
                if (!--deg[y]) q.push(y);
            }
        }
        for (int i=1; i<n; ++i) L2[i] = L[sccno[i]], R2[i] = R[sccno[i]];
        st.init(n-1,L2,R2);
        scanf("%d", &m);
        while (m--) {
            int x, y;
            scanf("%d%d", &x, &y);
            if (x==y) printf("%d %d
    ", x, y);
            else {
                int l, r;
                tie(l,r) = st.rmq(x, y-1);
                printf("%d %d
    ", l, r+1);
            }
        }
    }
    View Code

    J

    枚举因子暴力dp

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 1e6+10;
    int n, ans[N], id[N], fa[N], sz[N];
    vector<int> g[N];
    void dfs(int x, int f) {
        for (int y:g[x]) if (y!=f) fa[y]=x,dfs(y,x);
        id[++*id] = x;
    }
    int solve(int k) {
        for (int i=1; i<=n; ++i) sz[i] = 1;
        for (int i=1; i<=n; ++i) {
            int x = id[i];
            if (sz[x]>k) return 0;
            if (sz[x]!=k) sz[fa[x]] += sz[x];
        }
        return !sz[0];
    }
    int main() {
        scanf("%d", &n);
        for (int i=1; i<n; ++i) {
            int u, v;
            scanf("%d%d", &u, &v);
            g[u].push_back(v);
            g[v].push_back(u);
        }
        dfs(1,0);
        vector<int> ans;
        for (int i=n-1; i; --i) if (n%i==0&&solve(i)) printf("%d%c", n/i-1, " 
    "[i==1]);
    }
    View Code

    K

    I

    斜着的正方形坐标变换一下 $x=x+y, y = x-y$, 然后二维前缀和统计

    #include <bits/stdc++.h>
    using namespace std;
    const int M = 2510;
    int a[5100][5100],c[5100][5100];
    char b[5100][5100];
    void chkmax(int &a, int b) {a<b?a=b:0;}
    int main() {
        int n;
        scanf("%d", &n);
        while (n--) {
            char op;
            int x,y,z;
            scanf(" %c%d%d%d", &op, &x, &y, &z);
            if (op=='A') {
                z /= 2;
                x += M, y += M+1;
                ++c[x-z][y-z];
                ++c[x+z][y+z];
                --c[x-z][y+z];
                --c[x+z][y-z];
            }
            else {
                z /= 2;
                int tx = x+y, ty = x-y;
                x = tx, y = ty;
                x += M, y += M;
                ++a[x-z][y-z];
                ++a[x+z][y+z];
                --a[x-z][y+z];
                --a[x+z][y-z];
            }
        }
        int ans = 0;
        for (int i=0; i<5100; ++i) {
            for (int j=0; j<5100; ++j) {
                if (a[i][j]) {
                    a[i+1][j] += a[i][j];
                    a[i][j+1] += a[i][j];
                    a[i+1][j+1] -= a[i][j];
                    int x = i-M, y = j-M;
                    if ((x+y)%2==0) {
                        int xx = (x+y)/2+M, yy = (x-y)/2+M;
                        b[xx][yy] |= 1;
                        b[xx][yy+1] |= 4;
                    }
                    else {
                        ++y;
                        int xx = (x+y)/2+M, yy = (x-y)/2+M;
                        b[xx][yy+1] |= 8;
                        b[xx-1][yy+1] |= 2;
                    }
                }
            }
        }
        for (int i=0; i<5100; ++i) {
            for (int j=0; j<5100; ++j) { 
                if (c[i][j]) {
                    c[i+1][j] += c[i][j];
                    c[i][j+1] += c[i][j];
                    c[i+1][j+1] -= c[i][j];
                    ans += 4;
                }
                else {
                    if (b[i][j]&1) ++ans;
                    b[i][j] >>= 1;
                    if (b[i][j]&1) ++ans;
                    b[i][j] >>= 1;
                    if (b[i][j]&1) ++ans;
                    b[i][j] >>= 1;
                    if (b[i][j]&1) ++ans;
                }
            }
        }
        printf("%.2lf
    ", ans/4.+1e-5);
    }
    View Code 

    CERC 2019

    A 哈希模拟

    #include <bits/stdc++.h>
    const int N = 1e6+10;
    const int P1 = 876756319, B1 = 991;
    const int P2 = 799898821, B2 = 2333;
    int n, fac1[N], fac2[N];
    int L1[N], L2[N], R1[N], R2[N];
    char s[N];
    void init() {
        for (int i=1; i<=n; ++i) {
            L1[i] = ((long long)L1[i-1]*B1+s[i]-'a'+1)%P1;
            L2[i] = ((long long)L2[i-1]*B2+s[i]-'a'+1)%P2;
        }
        for (int i=n; i; --i) {
            R1[i] = ((long long)R1[i+1]*B1+s[i]-'a'+1)%P1;
            R2[i] = ((long long)R2[i+1]*B2+s[i]-'a'+1)%P2;
        }
    }
    std::pair<int,int> HashL(int l, int r) {
        int x = (L1[r]-(long long)L1[l-1]*fac1[r-l+1])%P1;
        int y = (L2[r]-(long long)L2[l-1]*fac2[r-l+1])%P2;
        if (x<0) x+=P1; if (y<0) y+=P2;
        return std::pair<int,int>(x,y);
    }
    std::pair<int,int> HashR(int l, int r) {
        int x = (R1[l]-(long long)R1[r+1]*fac1[r-l+1])%P1;
        int y = (R2[l]-(long long)R2[r+1]*fac2[r-l+1])%P2;
        if (x<0) x+=P1; if (y<0) y+=P2;
        return std::pair<int,int>(x,y);
    }
    int main() {
        fac1[0] = fac2[0] = 1;
        for (int i=1; i<N; ++i) {
            fac1[i] = (long long)fac1[i-1]*B1%P1;
            fac2[i] = (long long)fac2[i-1]*B2%P2;
        }
        scanf("%d%s", &n, s+1);
        init();
        if (L1[n]==R1[1]&&L2[n]==R2[1]) return puts("0"),0;
        for (int i=1; i<=n; ++i) {
            int x, y;
            std::tie(x, y) = HashR(1, i);
            int l1 = ((long long)L1[n]*fac1[i]+x)%P1;
            int l2 = ((long long)L2[n]*fac2[i]+y)%P2;
            std::tie(x, y) = HashL(1, i);
            int r1 = (R1[1]+(long long)x*fac1[n])%P1;
            int r2 = (R2[1]+(long long)y*fac2[n])%P2;
            if (l1==r1&&l2==r2) return printf("%d
    ", i), 0;
        }
    }
    View Code

    一个区间的gcd种类数是$O(log A)$的, 先预处理出前缀$gcd$和后缀$gcd$, 再处理出每个数作为最大值时的范围. 最后暴力合并即可, 复杂度是$O(nlog^3 A)$ 

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 111, P = 1e9+7;
    int n, a[N], pre[N], nxt[N];
    vector<pair<int,int> > L[N], R[N];
    int gcd(int x, int y) {return x?gcd(y%x,x):y;}
    int main() { 
        scanf("%d", &n);
        for (int i=1; i<=n; ++i) scanf("%d", a+i);
        for (int i=1; i<=n; ++i) {
            auto v = L[i-1];
            for (int j=0; j<v.size(); ++j) v[j].first = gcd(v[j].first, a[i]);
            v.push_back({a[i],i});
            for (int j=0; j<v.size(); ) {
                int k = j;
                while (k<v.size()&&v[k].first==v[j].first) ++k;
                L[i].push_back(v[j]);
                j = k;
            }
            pre[i] = i-1;
            while (pre[i]&&a[pre[i]]<a[i]) pre[i]=pre[pre[i]];
        }
        for (int i=n; i; --i) {
            auto v = R[i+1];
            for (int j=0; j<v.size(); ++j) v[j].first = gcd(v[j].first, a[i]);
            v.push_back({a[i],i});
            for (int j=0; j<v.size(); ) {
                int k = j;
                while (k<v.size()&&v[k].first==v[j].first) ++k;
                R[i].push_back(v[j]);
                j = k;
            }
            nxt[i] = i+1;
            while (nxt[i]<=n&&a[nxt[i]]<=a[i]) nxt[i]=nxt[nxt[i]];
        }
        int ans = 0;
        for (int i=1; i<=n; ++i) {
            int l = pre[i]+1, r = nxt[i]-1;
            for (int x=0; x<L[i].size(); ++x) for (int y=0; y<R[i].size(); ++y) {
                int l1 = L[i][x].second, r1 = x+1==L[i].size()?i:L[i][x+1].second-1;
                int l2 = y+1==R[i].size()?i:R[i][y+1].second+1, r2 = R[i][y].second;
                if (r1<l||l2>r) continue;
                l1 = max(l1, l), r2 = min(r2, r);
                ans = (ans+(long long)gcd(L[i][x].first,R[i][y].first)*a[i]%P*(r1-l1+1)%P*(r2-l2+1))%P;
            }
        }
        printf("%d
    ", ans);
    }
    View Code 

    C 非叶节点贡献是度数-2

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 3e5+10;
    int main() {
        int n;
        scanf("%d", &n);
        vector<int> deg(n+1);
        for (int i=1; i<n; ++i) {
            int u, v;
            scanf("%d%d", &u, &v);
            ++deg[u], ++deg[v];
        }
        int ans = 0;
        for (int i=1; i<=n; ++i) if (deg[i]>1) ans += deg[i]-2;
        printf("%d
    ", ans);
    }
    View Code

    D

    E 每个点做圆得到与直线相交的线段, 离散化求出被线段覆盖最多的点即可 

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 3e5+10;
    double x[N], y[N];
    int main() {
        int n;
        double r, a, b;
        scanf("%d%lf%lf%lf", &n, &r, &a, &b);
        for (int i=1; i<=n; ++i) scanf("%lf%lf", x+i, y+i);
        if (!b) { 
            swap(a, b);
            for (int i=1; i<=n; ++i) swap(x[i], y[i]);
        }
        vector<pair<double,int> > s;
        for (int i=1; i<=n; ++i) {
            double A = a*a+b*b;
            double B = a*b*x[i]+b*b*y[i];
            double C = b*b*(x[i]*x[i]+y[i]*y[i]-r*r);
            double D = B*B-A*C;
            if (D<-1e-8) continue;
            D = sqrt(D);
            s.push_back({(B-D)/A,-1});
            s.push_back({(B+D)/A,1});
        }
        sort(s.begin(),s.end());
        int ans = 0, now = 0;
        for (auto &t:s) ans = max(ans, now-=t.second);
        printf("%d
    ", ans);
    }
    View Code

    F 分块

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 3e5+10;
    int main() {
        long long n, m, ans = 0;
        cin>>n>>m;
        auto calc = [](long long n) { 
            long long ans = 0;
            for (long long i=1,j; i<=n; i=j+1) {
                j = n/(n/i);
                ans += (j-i+1)*(n/i);
            }
            return ans;
        };
        cout<<calc(m)-calc(n-1)<<endl;
    }
    View Code

    G 从高位到低位贪心

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 2e5+10;
    int n, k, a[N];
    int main() {
        scanf("%d%d", &n, &k);
        for (int i=1; i<=n; ++i) scanf("%d", a+i);
        int ans = 0;
        for (int i=30; i>=0; --i) {
            int cnt = 0;
            for (int j=1; j<=n; ++j) if (a[j]>>i&1) ++cnt;
            if (cnt>=k) {
                cnt = 0;
                for (int j=1; j<=n; ++j) if (a[j]>>i&1) a[++cnt] = a[j];
                n = cnt;
                ans ^= 1<<i;
            }
        }
        printf("%d
    ", ans);
    }
    View Code

    H AC自动机矩阵快速幂

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 111, P = 1e9+7;
    int n, q, cnt;
    char s[N];
    struct AC_automaton {
        int rt, ch[N][26], fa[N], val[N];
        void init() {
            rt = cnt = 1;
            memset(ch[1],0,sizeof ch[1]);
            fa[1] = 0;
        }
        int newnode() {
            ++cnt;
            memset(ch[cnt],0,sizeof ch[cnt]);
            fa[cnt] = 0;
            return cnt;
        }
        void ins(char *s) {
            int now = rt, n = strlen(s);
            for (int i=0; i<n; ++i) {
                int c = s[i]-'a';
                if (!ch[now][c]) ch[now][c]=newnode();
                now = ch[now][c];
            }
            val[now] = 1;
        }
        void build() {
            queue<int> q;
            for (int i=0; i<26; ++i) {
                if (ch[rt][i]) fa[ch[rt][i]]=rt,q.push(ch[rt][i]);
                else ch[rt][i]=rt;
            }
            while (q.size()) {
                int u = q.front(); q.pop();
                val[u] |= val[fa[u]];
                for (int i=0; i<26; ++i) {
                    int &v = ch[u][i];
                    if (v) {
                        fa[v] = ch[fa[u]][i];
                        q.push(v);
                    }
                    else v = ch[fa[u]][i];
                }
            }
        }
    } ac;
    struct Mat {
        int v[N][N];
        Mat() {memset(v, 0, sizeof v);}
        Mat operator * (const Mat& b) const {
            Mat c;
            for (int k=1; k<=cnt; ++k) 
                for (int i=1; i<=cnt; ++i) 
                    for (int j=1; j<=cnt; ++j) 
                        c.v[i][j] = ((long long)v[i][k]*b.v[k][j]+c.v[i][j])%P;
            return c;
        }
        Mat operator ^ (int nn) {
            Mat b, a=*this;
            for (int i=1; i<=cnt; ++i) b.v[i][i] = 1;
            while (nn) {
                if(nn&1) b=b*a;
                nn>>=1,a=a*a;
            }
            return b;
        }
    } g;
    int main() {
        scanf("%d%d", &n, &q);
        ac.init();
        while (q--) {
            scanf("%*d%s", s);
            ac.ins(s);
        }
        ac.build();
        for (int i=1; i<=cnt; ++i) {
            for (int j=0; j<26; ++j) { 
                if (!ac.val[ac.ch[i][j]]) ++g.v[i][ac.ch[i][j]];
            }
        }
        g = g^n;
        int ans = 0;
        for (int i=1; i<=cnt; ++i) ans = (ans+g.v[1][i])%P;
        printf("%d
    ", ans);
    }
    View Code

    I 优先交换$2$元环, 然后$3$元环, 然后$4$元环

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 1e6+10;
    int n,f[500][500];
    char a[N],b[N],s[]={'A','C','G','T'};
    int main() {
        scanf("%s%s", a+1, b+1);
        n = strlen(a+1);
        for (int i=1; i<=n; ++i) if (a[i]!=b[i]) ++f[a[i]][b[i]];
        int ans = 0, t;
        auto gao = [&](char x, char y) {
            t = min(f[x][y],f[y][x]);
            ans += t, f[x][y] -= t, f[y][x] -= t;
        };
        auto gao2 = [&](char x, char y, char z) {
            t = min({f[x][y],f[y][z],f[z][x]});
            ans += 2*t, f[x][y] -= t, f[y][z] -= t, f[z][x] -= t;
        };
        auto gao3 = [&](char x, char y, char z, char w) {
            t = min({f[x][y],f[y][z],f[z][w],f[w][x]});
            ans += 3*t, f[x][y] -= t, f[y][z] -= t, f[z][w] -= t, f[w][x] -= t;
        };
        for (auto x:s) for (auto y:s) gao(x,y);
        for (auto x:s) for (auto y:s) for (auto z:s) gao2(x,y,z);
        for (auto x:s) for (auto y:s) for (auto z:s) for (auto w:s) gao3(x,y,z,w);
        printf("%d
    ", ans);
    }
    View Code

    J 按度数分块即可

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 1e5+10, S = 400;
    int n, e, p, fa[N], a[N], u[N], v[N], deg[N], vis[N];
    vector<int> g[N];
    int Find(int x) {return fa[x]?fa[x]=Find(fa[x]):x;}
    int main() {
        scanf("%d%d%d", &n, &e, &p);
        for (int i=1; i<=e; ++i) {
            scanf("%d%d", u+i, v+i);
            ++deg[u[i]], ++deg[v[i]];
        }
        for (int i=1; i<=e; ++i) {
            if (deg[u[i]]<=S) g[u[i]].push_back(v[i]);
            if (deg[v[i]]<=S) g[v[i]].push_back(u[i]);
            if (deg[u[i]]>S&&deg[v[i]]>S) { 
                g[u[i]].push_back(v[i]);
                g[v[i]].push_back(u[i]);
            }
        }
        while (p--) {
            int m;
            scanf("%d", &m);
            for (int i=1; i<=m; ++i) scanf("%d", a+i), vis[a[i]] = 1;
            int cnt = m;
            for (int i=1; i<=m; ++i) {
                int u = Find(a[i]);
                for (auto &t:g[a[i]]) if (vis[t]) {
                    int v = Find(t);
                    if (u!=v) fa[v]=u,--cnt;
                }
            }
            for (int i=1; i<=m; ++i) fa[a[i]] = vis[a[i]] = 0;
            printf("%d
    ", cnt);
        }
    }
    View Code

    K

    L 分类讨论

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 2e5+10;
    int n, a[N], b[N], vis[N], Log[N], mi[N][19], ma[N][19];
    vector<int> g[N];
    void init() {
        Log[0] = -1;
        for (int i=1; i<=n; ++i) mi[i][0] = ma[i][0] = a[i], Log[i]=Log[i>>1]+1;
        for (int j=1; j<=Log[n]; ++j) for (int i=1;i+(1<<j)-1<=n; ++i) {
            mi[i][j] = min(mi[i][j-1], mi[i+(1<<j-1)][j-1]);
            ma[i][j] = max(ma[i][j-1], ma[i+(1<<j-1)][j-1]);
        }
    }
    pair<int,int> rmq(int l, int r) {
        int t = Log[r-l+1];
        r -= (1<<t)-1;
        return {min(mi[l][t],mi[r][t]),max(ma[l][t],ma[r][t])};
    }
    struct {
        int c[N];
        void add(int x) {
            for (; x<=*b; x+=x&-x) ++c[x];
        }
        int query(int l, int r) {
            int ans = 0;
            for (int x=l-1; x; x^=x&-x) ans -= c[x];
            for (int x=r; x; x^=x&-x) ans += c[x];
            return ans;
        }
    } bit;
    int main() {
        scanf("%d", &n);
        for (int i=1; i<=n; ++i) scanf("%d", a+i), b[i] = a[i];
        sort(b+1, b+1+n);
        *b = unique(b+1, b+1+n)-b-1;
        for (int i=1; i<=n; ++i) { 
            a[i] = lower_bound(b+1, b+1+*b, a[i])-b;
            g[a[i]].push_back(i);
        }
        init();
        for (int i=1; i<=*b; ++i) {
            if (g[i].size()>=3) vis[111] = 1;
            if (g[i].size()>=2) {
                int l, r;
                tie(l, r) = rmq(g[i][0], g[i].back());
                if (l<i) vis[212] = 1;
                if (r>i) vis[121] = 1;
                tie(l, r) = rmq(g[i][1], n);
                if (l<i) vis[221] = 1;
                if (r>i) vis[112] = 1;
                tie(l, r) = rmq(1, g[i][g[i].size()-2]);
                if (l<i) vis[122] = 1;
                if (r>i) vis[211] = 1;
            }
        }
        int x=-1, xx=-1;
        for (int i=1; i<=n; ++i) {
            if (a[i]<xx) vis[321] = 1;
            if (a[i]<x) xx = max(xx, a[i]);
            x = max(x, a[i]);
        }
        x = xx = n+1;
        for (int i=1; i<=n; ++i) {
            if (a[i]>xx) vis[123] = 1;
            if (a[i]>x) xx = min(xx, a[i]);
            x = min(x, a[i]);
        }
        for (int i=1; i<=n; ++i) {
            if (1<i&&i<n) {
                int l, r;
                tie(l, r) = rmq(i+1, n);
                if (r>a[i]+1&&bit.query(a[i]+1,r-1)) vis[213] = 1;
                if (l<a[i]-1&&bit.query(l+1,a[i]-1)) vis[231] = 1;
            }
            bit.add(a[i]);
        }
        memset(bit.c,0,sizeof bit.c);
        for (int i=n; i; --i) {
            if (1<i&&i<n) {
                int l, r;
                tie(l, r) = rmq(1, i-1);
                if (r>a[i]+1&&bit.query(a[i]+1,r-1)) vis[312] = 1;
                if (l<a[i]-1&&bit.query(l+1,a[i]-1)) vis[132] = 1;
            }
            bit.add(a[i]);
        }
        for (int i=111; i<=333; ++i) if (vis[i]) printf("%d
    ", i);
    }
    View Code

    CERC 2018

    A ac自动机处理一下每个位置最长匹配长度, 然后dp的时候要支持区间取最小值, 单点求最值, 直接线段树, 因为区间和单点询问都是单调的, 也可以线性做

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 3e5+10;
    struct AC_automaton {
        int rt, cnt, ch[N][26], fa[N], val[N];
        void init() {
            rt = cnt = 1;
            memset(ch[1],0,sizeof ch[1]);
            fa[1] = 0;
        }
        int newnode() {
            ++cnt;
            memset(ch[cnt],0,sizeof ch[cnt]);
            fa[cnt] = 0;
            return cnt;
        }
        void ins(char *s) {
            int now = rt, n = strlen(s);
            for (int i=0; i<n; ++i) {
                int c = s[i]-'a';
                if (!ch[now][c]) ch[now][c]=newnode();
                now = ch[now][c];
            }
            val[now] = n;
        }
        void build() {
            queue<int> q;
            for (int i=0; i<26; ++i) {
                if (ch[rt][i]) fa[ch[rt][i]]=rt,q.push(ch[rt][i]);
                else ch[rt][i]=rt;
            }
            while (q.size()) {
                int u = q.front(); q.pop();
                val[u] = max(val[u], val[fa[u]]);
                for (int i=0; i<26; ++i) {
                    int &v = ch[u][i];
                    if (v) { 
                        fa[v] = ch[fa[u]][i];
                        q.push(v);
                    }
                    else v = ch[fa[u]][i];
                }
            }
        }
    } ac;
    struct seg {
        int mi[N<<2];
        void build(int o, int l, int r) {
            mi[o] = 1e9; int mid = (l+r)/2;
            if (l!=r) build(o<<1,l,mid),build(o<<1|1,mid+1,r);
        }
        void upd(int o, int l, int r, int ql, int qr, int v) {
            if (ql<=l&&r<=qr) { 
                mi[o] = min(mi[o], v);
                return;
            }
            int mid = (l+r)/2;
            if (mid>=ql) upd(o<<1,l,mid,ql,qr,v);
            if (mid<qr) upd(o<<1|1,mid+1,r,ql,qr,v);
        }
        void query(int o, int l, int r, int x, int &v) {
            v = min(v, mi[o]);
            if (l==r) return;
            int mid = (l+r)/2;
            if (mid>=x) query(o<<1,l,mid,x,v);
            else query(o<<1|1,mid+1,r,x,v);
        }
    } tr;
    
    int n, L, vis[N];
    char s[N],a[N];
    
    int main() {
        scanf("%d%s", &L, s);
        ac.init();
        for (int i=1; i<=L; ++i) {
            scanf("%s", a);
            ac.ins(a);
        }
        ac.build();
        int now = ac.rt, n = strlen(s);
        for (int i=0; i<n; ++i) {
            now = ac.ch[now][s[i]-'a'];
            vis[i] = ac.val[now];
        }
        tr.build(1,0,n);
        tr.upd(1,0,n,n,n,0);
        for (int i=n-1; i>=0; --i) {
            int dp = 1e9;
            tr.query(1,0,n,i+1,dp);
            if (dp==1e9) return puts("-1"), 0;
            if (vis[i]) tr.upd(1,0,n,i-vis[i]+1,i,dp+1);
        }
        int ans = 1e9;
        tr.query(1,0,n,0,ans);
        if (ans==1e9) puts("-1");
        else printf("%d
    ", ans);
    }
    View Code

    B 建$10$个图, 第$i$个图只连权值$<i$的边, 询问只要暴力枚举答案, 判断连通性即可, 可以离线lct或者线段树分治

    C 迭代加深搜索

    #include <bits/stdc++.h>
    using namespace std;
    char s[50];
    int ans, n;
    long long mx;
    int dfs(long long x, int d) {
        if (x==mx) return 1;
        if (!d) return 0;
        for (int i=1; i<=n; ++i) {
            long long y = x|(x>>i);
            if (y>x&&dfs(y,d-1)) return 1;
        }
        return 0;
    }
    int main() {
        scanf("%s", s);
        n = strlen(s);
        long long x = 0;
        for (int i=0; i<n; ++i) { 
            x = x*2+s[i]-'0';
            mx = mx*2+1;
        }
        if (s[0]=='0') return puts("-1"),0;
        for (int i=0; i<=n; ++i) {
            if (dfs(x,i)) return printf("%d
    ", i), 0;
        }
    }
    View Code

    D

    E

    F

    G

    H

    I 暴力

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 1e6+5;
    int f[N];
    int main() {
        for (int i=1; i<N; ++i) 
            for (int j=(N+i-1)/i-1; j>=i+1; --j) 
                for (int k=(N+i*j-1)/(i*j)-1; k>=j+1; --k)
                    ++f[i*j*k];
        for (int i=1; i<N; ++i) f[i] += f[i-1];
        int t;
        scanf("%d", &t);
        while (t--) {
            int n;
            scanf("%d", &n);
            printf("%d
    ", f[n]);
        }
    }
    View Code

    J 分四个方向dp一下

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 1e3+10;
    int n, m, dp[N][N], len[N][N];
    char s[N][N];
    long long ans;
    void solve() {
        for (int i=0; i<n; ++i) for (int j=0; j<m; ++j) {
            if (j&&s[i][j]==s[i][j-1]) len[i][j] = len[i][j-1]+1;
            else len[i][j] = 1;
        }
        for (int i=n-1; i>=0; --i) for (int j=0; j<m; ++j) {
            if (s[i][j]==s[i+1][j]) dp[i][j] = max(1, dp[i+1][j]-1);
            else dp[i][j] = 1;
            while (dp[i][j]<i+1&&j+dp[i][j]<m&&s[i-dp[i][j]][j]==s[i][j]&&len[i-dp[i][j]][j+dp[i][j]]>dp[i][j]) ++dp[i][j];
            ans += dp[i][j]-1;
        }
    }
    int main() {
        scanf("%d%d", &n, &m);
        for (int i=0; i<n; ++i) scanf("%s", s[i]);
        solve();
        for (int i=0; i<n; ++i) reverse(s[i], s[i]+m);
        solve();
        for (int j=0; j<m; ++j) for (int i=0; i<n-i-1; ++i) swap(s[i][j],s[n-i-1][j]);
        solve();
        for (int i=0; i<n; ++i) reverse(s[i], s[i]+m);
        solve();
        printf("%lld
    ", ans);
    }
    View Code

    K

    L

    当$A=B$时, 是平等博弈, 可以得到$x$个石子的$sg$值是$x space modspace (A+1)$

    当$A>B$时, 可以看成参数为$B$的博弈

    如果存在一堆$xge B+1$, 那么先手取$B+1$个石子, 这样可以让$sg$值异或和不变, 先手必胜 

    如果每一堆石子个数都$<B+1$, 转化为平等博弈

    当$A<B$时, 可以看成参数为$A$的博弈

    如果$sg$值异或和为$0$, 先手必败

    否则先手第一步操作必须删掉$ge A+1$的堆并且让$sg$为$0$才能赢, 否则必败

    #include <bits/stdc++.h>
    using namespace std;
    int n, a, b, x[100010];
    int main() {
        scanf("%d%d%d", &n, &a, &b);
        int sg = 0, mi = min(a, b);
        for (int i=1; i<=n; ++i) scanf("%d", x+i), sg ^= x[i]%(mi+1);
        if (a==b) return puts(sg?"Petyr":"Varys"),0;
        if (a>b) {
            if (sg) puts("Petyr");
            else {
                for (int i=1; i<=n; ++i) if (x[i]>=mi+1) {
                    return puts("Petyr"), 0;
                }
                puts("Varys");
            }
            return 0;
        }
        int p = 0;
        for (int i=1; i<=n; ++i) if (x[i]>=mi+1) { 
            if (p) return puts("Varys"), 0;
            p = i;
        }
        if (!p) return puts(sg?"Petyr":"Varys"),0;
        sg ^= x[p]%(a+1);
        for (int i=x[p]-a; i<=a; ++i) if (sg==(x[p]-i)%(a+1)) return puts("Petyr"), 0;
        puts("Varys");
    }
    View Code

    2018 icpc Yokohama

    A 模拟

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 2e5+10;
    int n;
    char s[N],t[N];
    int chk(char *a, char *b) {
        int n = strlen(a+1), m = strlen(b+1);
        for (int i=1; i<=n&&i<=m; ++i) { 
            if (isdigit(a[i])&&isdigit(b[i])) {
                int x = i, y = i;
                while (x<n&&isdigit(a[x+1])) ++x;
                while (y<m&&isdigit(b[y+1])) ++y;
                if (x!=y) return x<y;
                for (int j=i; j<=x; ++j) if (a[j]!=b[j]) return a[j]<b[j];
                i = x;
            }
            else if (a[i]!=b[i]) {
                if (isdigit(a[i])!=isdigit(b[i])) return isdigit(a[i]);
                return a[i]<b[i];
            }
        }
        return n<m;
    }
    int main() {
        scanf("%d%s", &n, s+1);
        while (n--) {
            scanf("%s", t+1);
            if (chk(t,s)) puts("-");
            else puts("+");
        }
    }
    View Code

    B 暴力枚举最后两项, 二分找前面项

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 5010;
    int n, a[N], f[N][N];
    int main() {
        scanf("%d", &n);
        for (int i=1; i<=n; ++i) scanf("%d", a+i);
        sort(a+1,a+1+n);
        for (int i=1; i<=n; ++i) for (int j=i+1; j<=n; ++j) f[j][i] = 2;
        int ans = 2;
        for (int i=1; i<=n; ++i) for (int j=2; j<i; ++j) {
            int x = lower_bound(a+1,a+j,2*a[j]-a[i])-a;
            if (a[x]==2*a[j]-a[i]) { 
                f[i][j] = max(f[i][j], f[j][x]+1);
                ans = max(ans, f[i][j]);
            }
        }
        printf("%d
    ", ans);
    }
    View Code

    C

    D 求字典序最小, 那么就考虑从前往后贪心, 那么需要求出每个后缀的答案检验是否合法

    设${dp}_{i,j}$表示只考虑第一个串后缀$i$和第二个串后缀$j$时的最小值, 可以用序列自动机$O(1)$转移

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 4e3+10;
    int n, m, pre1[N][2], pre2[N][2], nxt1[N][2], nxt2[N][2], dp[N][N];
    int len[N][N];
    char s[N],t[N];
    void chkmin(int &a, int b) {a>b?a=b:0;}
    int main() {
        scanf("%s%s", s+1, t+1);
        n = strlen(s+1);
        m = strlen(t+1);
        nxt1[n+1][0]=nxt1[n+1][1]=n+1;
        nxt2[m+1][0]=nxt2[m+1][1]=m+1;
        for (int i=n; i; --i) {
            nxt1[i][0] = nxt1[i+1][0];
            nxt1[i][1] = nxt1[i+1][1];
            nxt1[i][s[i]-'0'] = i;
        }
        for (int i=m; i; --i) {
            nxt2[i][0] = nxt2[i+1][0];
            nxt2[i][1] = nxt2[i+1][1];
            nxt2[i][t[i]-'0'] = i;
        }
        memset(len,0x3f,sizeof len);
        for (int i=n+1; i; --i) for (int j=m+1; j; --j) for (int k=0; k<=1; ++k) {
            int ni = nxt1[i][k], nj = nxt2[j][k];
            if (ni==n+1&&nj==m+1) chkmin(len[i][j], 1);
            else chkmin(len[i][j], len[min(n+1,ni+1)][min(nj+1,m+1)]+1);
        }
        int x = 0, y = 0, ans = len[1][1];
        for (int i=1; i<=ans; ++i) {
            int tx = nxt1[min(x+1,n+1)][0], ty = nxt2[min(y+1,m+1)][0];
            if (i==ans) {
                if (tx<=n||ty<=m) putchar('1');
                else putchar('0');
            }
            else {
                if (len[min(n+1,tx+1)][min(m+1,ty+1)]<=ans-i) putchar('0'),x=tx,y=ty;
                else putchar('1'),x=nxt1[min(x+1,n+1)][1],y=nxt2[min(y+1,m+1)][1];
            }
        }
        puts("");
    }
    View Code

    E

    F

    G

    考虑每个数的贡献, 要么加入左边递增部分, 贡献是左侧比它大的数的个数, 要么加入右边递减部分, 贡献是右侧比它大的数的个数, 两者取最小即可

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 1e5+10;
    int n, a[N], c[N], f[N];
    int main() {
        scanf("%d", &n);
        for (int i=1; i<=n; ++i) scanf("%d", a+i);
        for (int i=1; i<=n; ++i) {
            for (int x=a[i]+1; x<N; x+=x&-x) f[i] += c[x];
            for (int x=a[i]; x; x-=x&-x) ++c[x];
        }
        memset(c,0,sizeof c);
        long long ans = 0;
        for (int i=n; i; --i) {
            int sum = 0;
            for (int x=a[i]+1; x<N; x+=x&-x) sum += c[x];
            for (int x=a[i]; x; x-=x&-x) ++c[x];
            ans += min(f[i], sum);
        }
        printf("%lld
    ", ans);
    }
    View Code

    H

    I

    J

    每种颜色按$dfs$排序, 用一个$set$维护, 修改颜色看做删点添点, 讨论一下相对其他点位置关系即可

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 1e5+10;
    int n, clk, a[N], dep[N], sz[N], son[N], fa[N];
    int top[N], ans[N], L[N], R[N], id[N];
    vector<int> g[N];
    struct cmp {
        bool operator () (int x, int y) const {return L[x]<L[y];}
    };
    set<int,cmp> s[N];
    void dfs(int x, int f, int d) {
        fa[x] = f, dep[x] = d, sz[x] = 1;
        L[x] = ++clk, id[clk] = x;
        for (int y:g[x]) if (y!=f) {
            dfs(y,x,d+1);
            sz[x] += sz[y];
            if (sz[y]>sz[son[x]]) son[x]=y;
        }
        R[x] = clk;
    }
    void dfs(int x, int tf) {
        top[x] = tf;
        if (son[x]) dfs(son[x],tf);
        for (int y:g[x]) if (!top[y]) dfs(y,y);
    }
    int lca(int x, int y) {
        while (top[x]!=top[y]) {
            if (dep[top[x]]<dep[top[y]]) y=fa[top[y]];
            else x=fa[top[x]];
        }
        return dep[x]<dep[y]?x:y;
    }
    int calc(int x, int v) {
        if (s[v].empty()) return 0;
        int l = *s[v].begin(), r = *s[v].rbegin();
        if (L[l]<L[x]||L[r]>R[x]) return 0;
        return dep[lca(l,r)]-dep[x];
    }
    int gao(int x, int v) {
        auto p = s[v].lower_bound(x);
        int l=-1, r=-1;
        if (p!=s[v].end()) r = *p;
        if (p!=s[v].begin()) l = *prev(p);
        int y = -1;
        if (l!=-1) y = lca(l, x);
        if (r!=-1) { 
            int t = lca(r, x);
            if (y==-1||dep[t]>dep[y]) y = t;
        }
        if (y==-1) return calc(x,v);
        return dep[x]-dep[y]+calc(y,v);
    }
    void add(int x, int v) {
        ans[v] += gao(x, v);
        s[v].insert(x);
    }
    void del(int x, int v) {
        s[v].erase(x);
        ans[v] -= gao(x, v);
    }
    int main() {
        scanf("%d", &n);
        for (int i=1; i<n; ++i) {
            int u, v;
            scanf("%d%d", &u, &v);
            g[u].push_back(v);
            g[v].push_back(u);
        }
        dfs(1,0,0),dfs(1,1);
        for (int i=1; i<=n; ++i) scanf("%d", a+i), add(i, a[i]);
        int q;
        scanf("%d", &q);
        while (q--) {
            char c;
            int x, y;
            scanf(" %c%d", &c, &x);
            if (c=='Q') {
                if (s[x].empty()) puts("-1");
                else printf("%d
    ", ans[x]);
            }
            else {
                scanf("%d", &y);
                del(x,a[x]);
                a[x] = y;
                add(x,a[x]);
            }
        }
    }
    View Code

    K

    CERC 2015

    A 模拟

    #include <bits/stdc++.h>
    using namespace std;
    string s[7];
    char tmp[111];
    int gao(int x) {
        if (s[0].substr(x,5)=="xxxxx"&&s[3].substr(x,5)=="x...x") return 0;
        if (s[0].substr(x,5)=="....x"&&s[1].substr(x,5)=="....x") return 1;
        if (s[1].substr(x,5)=="....x"&&s[4].substr(x,5)=="x....") return 2;
        if (s[1].substr(x,5)=="....x"&&s[3].substr(x,5)=="xxxxx") return 3;
        if (s[0].substr(x,5)=="x...x") return 4;
        if (s[1].substr(x,5)=="x...."&&s[4].substr(x,5)=="....x") return 5;
        if (s[1].substr(x,5)=="x...."&&s[4].substr(x,5)=="x...x") return 6;
        if (s[3].substr(x,5)=="....x") return 7;
        if (s[4].substr(x,5)=="x...x") return 8;
        if (s[0].substr(x,5)=="xxxxx") return 9;
        return -1;
    }
    void print(int x, int v) {
        if (v==0) {
            s[0] = s[0]+"xxxxx";
            s[1] = s[1]+"x...x";
            s[2] = s[2]+"x...x";
            s[3] = s[3]+"x...x";
            s[4] = s[4]+"x...x";
            s[5] = s[5]+"x...x";
            s[6] = s[6]+"xxxxx";
        }
        else if (v==1) {
            for (int i=0; i<7; ++i) s[i] = s[i]+"....x";
        }
        else if (v==2) {
            s[0] = s[0]+"xxxxx";
            s[1] = s[1]+"....x";
            s[2] = s[2]+"....x";
            s[3] = s[3]+"xxxxx";
            s[4] = s[4]+"x....";
            s[5] = s[5]+"x....";
            s[6] = s[6]+"xxxxx";
        }
        else if (v==3) {
            s[0] = s[0]+"xxxxx";
            s[1] = s[1]+"....x";
            s[2] = s[2]+"....x";
            s[3] = s[3]+"xxxxx";
            s[4] = s[4]+"....x";
            s[5] = s[5]+"....x";
            s[6] = s[6]+"xxxxx";
        }
        else if (v==4) {
            s[0] = s[0]+"x...x";
            s[1] = s[1]+"x...x";
            s[2] = s[2]+"x...x";
            s[3] = s[3]+"xxxxx";
            s[4] = s[4]+"....x";
            s[5] = s[5]+"....x";
            s[6] = s[6]+"....x";
        }
        else if (v==5) {
            s[0] = s[0]+"xxxxx";
            s[1] = s[1]+"x....";
            s[2] = s[2]+"x....";
            s[3] = s[3]+"xxxxx";
            s[4] = s[4]+"....x";
            s[5] = s[5]+"....x";
            s[6] = s[6]+"xxxxx";
        }
        else if (v==6) {
            s[0] = s[0]+"xxxxx";
            s[1] = s[1]+"x....";
            s[2] = s[2]+"x....";
            s[3] = s[3]+"xxxxx";
            s[4] = s[4]+"x...x";
            s[5] = s[5]+"x...x";
            s[6] = s[6]+"xxxxx";
        }
        else if (v==7) {
            s[0] = s[0]+"xxxxx";
            s[1] = s[1]+"....x";
            s[2] = s[2]+"....x";
            s[3] = s[3]+"....x";
            s[4] = s[4]+"....x";
            s[5] = s[5]+"....x";
            s[6] = s[6]+"....x";
        }
        else if (v==8) {
            s[0] = s[0]+"xxxxx";
            s[1] = s[1]+"x...x";
            s[2] = s[2]+"x...x";
            s[3] = s[3]+"xxxxx";
            s[4] = s[4]+"x...x";
            s[5] = s[5]+"x...x";
            s[6] = s[6]+"xxxxx";
        }
        else if (v==9) {
            s[0] = s[0]+"xxxxx";
            s[1] = s[1]+"x...x";
            s[2] = s[2]+"x...x";
            s[3] = s[3]+"xxxxx";
            s[4] = s[4]+"....x";
            s[5] = s[5]+"....x";
            s[6] = s[6]+"xxxxx";
        }
    }
    
    int main() {
        for (int i=0; i<7; ++i) cin>>s[i];
        int x = 0, y = 0, f = 0;
        for (int i=0; i<s[0].size(); i+=6) {
            int t = gao(i);
            if (0<=t&&t<=9) {
                if (f) y = y*10+t;
                else x = x*10+t;
            }
            else f = 1;
        }
        int cnt = 0, ans = x+y;
        while (ans) tmp[++cnt] = ans%10, ans /= 10;
        for (int i=0; i<7; ++i) s[i].clear();
        int now = 0;
        for (int i=cnt; i; --i) {
            print(now,tmp[i]);
            if (i>1) for (int i=0; i<7; ++i) s[i] += ".";
            now += 6;
        }
        for (int i=0; i<7; ++i) cout<<s[i]<<'
    ';
    }
    View Code

    B 二分答案

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 1e6+10;
    int n,a,b,sum[N];
    string s[N],t;
    int len(int i, int j) {
        return sum[j]-sum[i-1]+j-i;
    }
    int main() {
        getline(cin,t);
        stringstream ss(t);
        while (ss>>t) s[++n]=t;
        cin>>a>>b;
        for (int i=1; i<=n; ++i) sum[i]=sum[i-1]+s[i].size();
        for (int x=a; x<=b; ++x) {
            int t = 1, ans = 0;
            while (t<=n) {
                int l=t,r=n,pos=-1;
                while (l<=r) {
                    int mid = (l+r)/2;
                    if (len(t,mid)<=x) pos=mid,l=mid+1;
                    else r=mid-1;
                }
                if (ans) ans += s[t].size()+1;
                else ans += s[t].size();
                t = pos+1;
            }
            printf("%d
    ",ans);
        }
    }
    View Code

    C

    D 求一下前缀模$m$为$0$的位置个数即可

    #include <bits/stdc++.h>
    using namespace std;
    int n, m;
    char s[300010];
    int main() {
        scanf("%d%d%s", &n, &m, s+1);
        int num = 0, ans = 1;
        for (int i=1; i<=n; ++i) {
            num = (num*10+s[i]-'0')%m;
            if (!num&&i!=n) ans = ans*2ll%1000000007;
        }
        if (num) ans = 0;
        printf("%d
    ", ans);
    }
    View Code

    E 从大到小添边, 考虑添加一条边$(u,v)$, 相当于要把$u$和$v$度数$+1$, 可以独立考虑

    如果$u$度数$>2$, 那么不会有影响

    如果$u$度数$=2$, 那么边数$+1$, 点数$+1$

    如果$u$度数$=1$, 那么边数$-1$, 点数$-1$

    如果$u$度数$=0$, 那么边数不变, 点数$+1$

    但这样会把一个大环算成$0$条边, $0$个点, 再用并查集统计大环的个数

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 3e5+10;
    int n, m, ans1[N], ans2[N], deg[N], fa[N], vis[N], cnt[N], sz[N], iscycle[N];
    struct _ {int u,v,w;} e[N];
    int Find(int x) {return fa[x]?fa[x]=Find(fa[x]):x;}
    int main() {
        scanf("%d%d", &n, &m);
        for (int i=1; i<=m; ++i) scanf("%d%d%d", &e[i].u, &e[i].v, &e[i].w);
        sort(e+1, e+1+m, [](_ a,_ b){return a.w>b.w;});
        int num1 = 0, num2 = 0, cycle = 0;
        auto add = [&](int x) {
            if (deg[x]==0) ++num1;
            if (deg[x]==1) --num1,--num2;
            if (deg[x]==2) ++num1,++num2;
            ++deg[x];
        };
        for (int i=1; i<=n; ++i) sz[i] = 1;
        for (int i=1; i<=m; ++i) {
            add(e[i].u);
            add(e[i].v);
            int u = Find(e[i].u), v = Find(e[i].v);
            if (u!=v) {
                if (iscycle[u]) iscycle[u] = 0, --cycle;
                if (iscycle[v]) iscycle[v] = 0, --cycle;
                fa[u] = v;
                sz[v] += sz[u];
                cnt[v] += cnt[u]+1;
                vis[v] |= vis[u];
                if (deg[e[i].u]>2||deg[e[i].v]>2) vis[v] = 1;
            }
            else {
                if (iscycle[u]) iscycle[u] = 0, --cycle;
                if (deg[e[i].u]>2||deg[e[i].v]>2) vis[u] = 1;
                ++cnt[u];
                //点数等于边数且度数均不超过2的连通块一定为大环
                if (!vis[u]&&sz[u]==cnt[u]) iscycle[u] = 1, ++cycle;
            }
            ans1[i] = num1+cycle, ans2[i] = ++num2+cycle;
        }
        int q;
        scanf("%d", &q);
        while (q--) {
            int x;
            scanf("%d", &x);
            int l=1, r=m, p=0;
            while (l<=r) {
                int mid = (l+r)/2;
                if (e[mid].w>=x) p=mid,l=mid+1;
                else r=mid-1;
            }
            printf("%d %d
    ", ans1[p], ans2[p]);
        }
    }
    View Code

    F 设$g_{i,j}=f_{i,j}+frac{c}{a+b-1}$, $g_{i,j}=a g_{i,j-1}+b g_{i-1,j}$, 转化为格路径计数

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 2e5+10, P = 1e6+3;
    int l[N],t[N],fac[N*2],ifac[N*2];
    int qpow(long long a, int n) {
        long long ans = 1;
        for (; n; n>>=1,a=a*a%P) if (n&1) ans = ans*a%P;
        return ans;
    }
    int C(int n, int m) {
        return (long long)fac[n]*ifac[m]%P*ifac[n-m]%P;
    }
    int main() {
        int n, a, b, c;
        scanf("%d%d%d%d", &n, &a, &b, &c);
        int k = c*(long long)qpow(a+b-1,P-2)%P;
        fac[0] = 1;
        for (int i=1; i<=2*n; ++i) fac[i] = (long long)fac[i-1]*i%P;
        ifac[2*n] = qpow(fac[2*n], P-2);
        for (int i=2*n-1; i>=0; --i) ifac[i] = ifac[i+1]*(i+1ll)%P;
        for (int i=1; i<=n; ++i) scanf("%d", l+i), l[i] = (l[i]+k)%P;
        for (int i=1; i<=n; ++i) scanf("%d", t+i), t[i] = (t[i]+k)%P;
        int ans = -k;
        for (int i=2; i<=n; ++i) { 
            ans = (ans+t[i]*(long long)qpow(b,n-1)%P*qpow(a,n-i)%P*C(n-i+n-2,n-2))%P;
            ans = (ans+l[i]*(long long)qpow(a,n-1)%P*qpow(b,n-i)%P*C(n-i+n-2,n-2))%P;
        }
        if (ans<0) ans += P;
        printf("%d
    ", ans);
    }
    View Code

    G

    H

    I

    J

    最大流转化为最小割, 两点不连通那么最小割=0, 不在一个边双内, 那么最小割=1, 如果删除任意一条边, 两点都在一个边双, 那么最小割=3, 否则为2

    可以枚举删边, 跑tarjan, hash判断边双是否相同

    K 相当于给了一个基环内向树森林, 先拆掉非环上的点, 最后处理环即可

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 2e5+10;
    int n, deg[N], nxt[N], vis[N], vis2[N];
    int main() {
        scanf("%d", &n);    
        for (int i=1; i<=2*n; ++i) scanf("%d", &nxt[i]), ++deg[nxt[i]];
        vector<int> ans;
        for (int i=1; i<=2*n; ++i) if (!deg[i]&&!vis[i]) {
            int cur = i;
            while (!deg[cur]) {
                if (!vis[cur]) ans.push_back(cur), vis[cur] = vis[nxt[cur]] = 1;
                --deg[nxt[cur]];
                cur = nxt[cur];
            }
        }
        for (int i=1; i<=2*n; ++i) if (deg[i]&&!vis2[i]) {
            int cur = i, pos = i;
            while (!vis2[cur]) {
                if (vis[cur]) pos = cur;
                vis2[cur] = 1;
                cur = nxt[cur];
            }
            cur = pos;
            do {
                if (!vis[cur]) ans.push_back(cur),vis[cur]=vis[nxt[cur]]=1;
                cur = nxt[cur];
            } while (cur!=pos);
        }
        for (int i=0; i<ans.size(); ++i) printf("%d%c", ans[i], " 
    "[i+1==ans.size()]);
    }
    View Code

    L

    2018 icpc Dhaka

    A

    B  裸数位$dp$, 逆序对可以枚举两位, 贡献就是其他位选取方案, 复杂度可以优化到$O(10 n)$

    #include <bits/stdc++.h>
    using namespace std;
    int64_t sum[20], pw[20];
    int a[20],cnt[10];
    int64_t calc(int64_t x) {
        for (int i=0; i<10; ++i) cnt[i] = 0;
        int n = 0;
        while (x) a[++n] = x%10, x /= 10;
        int64_t ans = sum[n-1], now = 0;
        for (int i=n; i; --i) {
            int l = i==n, r = a[i]-1;
            if (l<=r) {
                if (i>=3) ans += (r-l+1)*45*(i-1)*(i-2)/2*pw[i-3];
                int u = 0;
                for (int x=0; x<r; ++x) u += cnt[x], ans += u*pw[i-1];
                ans += now*(r-l+1)*pw[i-1];
                if (i>=2) {
                    for (int x=0; x<9; ++x)
                        ans += (r-l+1)*(9-x)*(i-1)*cnt[x]*pw[i-2];
                    for (int x=l; x<=r; ++x)
                        ans += (9-x)*(i-1)*pw[i-2];
                }
            }
            for (int x=0; x<a[i]; ++x) now += cnt[x];
            ++cnt[a[i]];
        }
        return ans+now;
    }
    int main() {
        pw[0] = 1;
        for (int i=1; i<20; ++i) pw[i] = pw[i-1]*10;
        for (int i=2; i<20; ++i) {
            sum[i] = sum[i-1]+36*(i-1)*pw[i-2];
            if (i>2) sum[i] += 405*(i-1)*(i-2)/2*pw[i-3];
        }
        int t;
        scanf("%d", &t);
        for (int clk=1; clk<=t; ++clk) {
            int64_t l, r;
            scanf("%lld%lld", &l, &r);
            int64_t ans = calc(r);
            if (l>1) ans -= calc(l-1);
            printf("Case %d: %lld
    ", clk, ans);
        }
    }
    View Code 

    C 显然是积性函数, 统计下每个素因子贡献即可

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 1e6+10, P = 1e7+7;
    int cnt, p[N], vis[N];
    int main() {
        for (int i=2; i*i<N; ++i) if (!vis[i]) {
            for (int j=i*i; j<N; j+=i) vis[j] = 1;
        }
        for (int i=2; i<N; ++i) if (!vis[i]) p[++cnt] = i;
        for (int n; scanf("%d", &n), n; ) {
            int ans = 1;
            for (int i=1; p[i]<=n; ++i) {
                int k = 1, x = n;
                while (x/=p[i]) k = (k+x)%P;
                ans = k*(k+1ll)/2%P*ans%P;
            }
            printf("%d
    ", ans);
        }
    }
    View Code

    D

    E 模拟

    F 路径并板子

    G 相当于每次询问给定点$x$,区间$[l,r]$, 求$x$到区间$[l,r]$内点的最小距离

    考虑分块, 预处理块内的点到整棵树所有点的最短距离, 这个直接用个队列bfs就行

    块边缘的点直接$rmq$查询距离即可, 复杂度$O(nsqrt{n})$

    H

    可以发现一直上下左右旋转就可以得到所有状态, 每个状态能分成若干个环, 求出环长lcm即可

    #include <bits/stdc++.h>
    using namespace std;
    typedef vector<vector<int>> node;
    const int N = 210, P = 78294349;
    char s[N][N];
    int n, m, X[N*N], Y[N*N], vis[N][N];
    int ma[N*N], f[N*N];
    
    node rotup(node u) {
        node v = u;
        for (int i=0; i<m; ++i) {
            int cnt = 0;
            for (int j=0; j<n; ++j) if (u[j][i]) v[cnt++][i] = u[j][i];
            while (cnt!=n) v[cnt++][i] = 0;
        }
        return v;
    }
    node rotdown(node u) {
        node v = u;
        for (int i=0; i<m; ++i) {
            int cnt = n-1;
            for (int j=n-1; j>=0; --j) if (u[j][i]) v[cnt--][i] = u[j][i];
            while (cnt>=0) v[cnt--][i] = 0;
        }
        return v;
    }
    node rotleft(node u) {
        node v = u;
        for (int i=0; i<n; ++i) {
            int cnt = 0;
            for (int j=0; j<m; ++j) if (u[i][j]) v[i][cnt++] = u[i][j];
            while (cnt!=m) v[i][cnt++] = 0;
        }
        return v;
    }
    node rotright(node u) { 
        node v = u;
        for (int i=0; i<n; ++i) {
            int cnt = m-1;
            for (int j=m-1; j>=0; --j) if (u[i][j]) v[i][cnt--] = u[i][j];
            while (cnt>=0) v[i][cnt--] = 0;
        }
        return v;
    }
    int main() {
        for (int i=2; i<N*N; ++i) if (!ma[i]) {
            for (int j=i; j<N*N; j+=i) ma[j] = i;
        }
        int t;
        scanf("%d", &t);
        for (int clk=1; clk<=t; ++clk) {
            scanf("%d%d", &n, &m);
            node a(n,vector<int>(m));
            for (int i=0; i<n; ++i) scanf("%s", s[i]);
            int tot = 0;
            for (int i=0; i<n; ++i) {
                for (int j=0; j<m; ++j) {
                    if (s[i][j]=='.') a[i][j] = 0;
                    else { 
                        a[i][j] = ++tot;
                        X[tot] = i;
                        Y[tot] = j;
                    }
                }
            }
            auto b = rotleft(rotdown(rotright(rotup(a))));
            for (int i=0; i<n; ++i) for (int j=0; j<m; ++j) vis[i][j] = 0;
            for (int i=1; i<=n*m; ++i) f[i] = 0;
            for (int i=0; i<n; ++i) {
                for (int j=0; j<m; ++j) if (!vis[i][j]) {
                    int curx = i, cury = j, cnt = 0;
                    while (!vis[curx][cury]) {
                        vis[curx][cury] = 1;
                        ++cnt;
                        int nxtx = X[b[curx][cury]], nxty = Y[b[curx][cury]];
                        curx = nxtx, cury = nxty;
                    }
                    while (ma[cnt]) {
                        int t = 0, d = ma[cnt];
                        while (cnt%d==0) cnt /= d, ++t;
                        f[d] = max(f[d], t);
                    }
                }
            }
            int ans = 1;
            for (int i=1; i<=n*m; ++i) while (f[i]--) ans = (int64_t)ans*i%P;
            printf("Case %d: %d
    ", clk, ans);
        }
    }
    View Code

    I

    2020 ICPC Universidad Nacional de Colombia Programming Contest

    A 简单几何题, 不会

    B SA板子

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 2e5+10;
    struct SA {
        int n,c[N],rk[N],h[N],sa[N],a[N];
        void build(int m) {
            a[n+1] = rk[n+1] = h[n+1] = 0;
            int i,*x=rk,*y=h;
            for(i=1;i<=m;i++) c[i]=0;
            for(i=1;i<=n;i++) c[x[i]=a[i]]++;
            for(i=1;i<=m;i++) c[i]+=c[i-1];
            for(i=n;i;i--) sa[c[x[i]]--]=i;
            for(int k=1,p;k<=n;k<<=1) {
                p=0;
                for(i=n-k+1;i<=n;i++) y[++p]=i;
                for(i=1;i<=n;i++) if(sa[i]>k) y[++p]=sa[i]-k;
                for(i=1;i<=m;i++) c[i]=0;
                for(i=1;i<=n;i++) c[x[y[i]]]++;
                for(i=1;i<=m;i++) c[i]+=c[i-1];
                for(i=n;i;i--) sa[c[x[y[i]]]--]=y[i];
                swap(x,y); x[sa[1]]=1; p=1;
                for(i=2;i<=n;i++)
                    x[sa[i]]=(y[sa[i-1]]==y[sa[i]] && y[sa[i-1]+k]==y[sa[i]+k])?p:++p;
                if(p==n) break; m=p;
            }
            for(i=1;i<=n;i++) rk[sa[i]]=i;
            for(int i=1,j,k=0;i<=n;i++) if (rk[i]!=1) {
                if(k) k--;
                j=sa[rk[i]-1];
                while(a[i+k]==a[j+k]) k++;
                h[rk[i]] = k;
            }
        }
    } sa1, sa2;
    char s[N],t[N];
    int main() {
        scanf("%s%s", s+1, t+1);
        int n = strlen(s+1), m = strlen(t+1);
        for (int i=1; i<=n; ++i) sa1.a[i] = s[i]-'a'+1;
        for (int i=1; i<=m; ++i) sa2.a[i] = t[i]-'a'+1;
        sa1.n = n, sa2.n = m;
        sa1.build(200), sa2.build(200);
        for (int i=sa1.sa[n]; i<=n; ++i) { 
            if (i==sa1.sa[n]||s[i]>=t[sa2.sa[m]]) putchar(s[i]);
            else break;
        }
        for (int i=sa2.sa[m]; i<=m; ++i) putchar(t[i]);
        puts("");
    }
    View Code

    C 设长$n$的答案为$f_n$, 可以得到$f_n=a^n-sumlimits_{d|n,d ot =n} f_d$

    #include <bits/stdc++.h>
    using namespace std;
    const int P = 1e9+7;
    int dp[5000010];
    int main() {
        int a,k;
        scanf("%d%d", &a, &k);
        int b = a, ans = 0;
        for (int i=1; i<=k; ++i) {
            dp[i] = (dp[i]+b)%P;
            for (int j=2*i; j<=k; j+=i) dp[j] = (dp[j]-dp[i])%P;
            b = (int64_t)b*a%P;
            ans = (ans+dp[i])%P;
        }
        if (ans<0) ans += P;
        printf("%d
    ", ans);
    }
    View Code

    D 就是求一个多项式$n$次幂, 快速幂优化一下

    #include <bits/stdc++.h>
    using namespace std;
    const int P = 1e9+7;
    int n, m, k, a[222], f[222], tmp[222];
    int qpow(int64_t a, int n) {
        int64_t ans = 1;
        for (; n; a=a*a%P,n>>=1) if (n&1) ans=ans*a%P;
        return ans;
    }
    int main() {
        scanf("%d%d%d", &n, &k, &m);
        int cnt = 0;
        for (int i=1; i<=k; ++i) if (i%m) ++cnt, ++a[i%m];
        for (int i=1; i<m; ++i) a[i] = (int64_t)a[i]*qpow(cnt,P-2)%P;
        f[0] = 1;
        for (; n; n>>=1) {
            if (n&1) {
                for (int x=0; x<m; ++x) tmp[x] = 0;
                for (int x=0; x<m; ++x) 
                    for (int y=0; y<m; ++y)
                        tmp[(x+y)%m] = (tmp[(x+y)%m]+(int64_t)f[x]*a[y])%P;
                for (int x=0; x<m; ++x) f[x] = tmp[x];
            }
            for (int x=0; x<m; ++x) tmp[x] = 0;
            for (int x=0; x<m; ++x)
                for (int y=0; y<m; ++y)
                    tmp[(x+y)%m] = (tmp[(x+y)%m]+(int64_t)a[x]*a[y])%P;
            for (int x=0; x<m; ++x) a[x] = tmp[x];
        }
        printf("%d
    ", f[0]);
    }
    View Code

    E 简单交互, 从根节点往下问就行, 问到最后一层直接输出

    #include <bits/stdc++.h>
    int main() {
        int n;
        scanf("%d", &n);
        auto ask = [&](int x) {
            if (!n--) {
                printf("! %d
    ", x);
                fflush(stdout);
                exit(0);
            }
            printf("%d
    ", x);
            fflush(stdout);
            int t;
            scanf("%d", &t);
            if (!t) {
                printf("! %d
    ", x);
                fflush(stdout);
                exit(0);
            }
            return t;
        };
        int dep = ask(1), now = 1, d = dep-1;
        while (1) {
            int t = ask(2*now);
            if (t==d) now *= 2, --d;
            else now = now*2+1, --d;
            if (d<0) ask(now);
        }
    }
    View Code

    F 分层图最短路

    #include <bits/stdc++.h>
    using namespace std;
    const int INF = 1e9, N = 1e4+10;
    int n, m, a, b, k;
    vector<pair<int,int>> g[N],f[N];
    vector<vector<int>> dij(vector<pair<int,int>> g[], int x) { 
        struct Q {
            int id,y,w;
            bool operator < (const Q &rhs) const {
                return w>rhs.w;
            }
        };
        vector<vector<int>> d(k+1,vector<int>(n,INF));
        vector<vector<int>> vis(k+1,vector<int>(n));
        priority_queue<Q> q;
        q.push({x,0,d[0][x]=0});
        while (q.size()) {
            Q u = q.top(); q.pop();
            if (vis[u.y][u.id]) continue;
            vis[u.y][u.id] = 1;
            for (auto &t:g[u.id]) {
                int v = t.first, w = d[u.y][u.id]+t.second;
                if (w<d[u.y][v]) q.push({v,u.y,d[u.y][v]=w});
                if (u.y==k) continue;
                w = d[u.y][u.id];
                if (w<d[u.y+1][v]) q.push({v,u.y+1,d[u.y+1][v]=w});
            }
        }
        return d;
    }
    int main() {
        scanf("%d%d%d%d%d", &n, &m, &a, &b, &k);
        while (m--) {
            int u, v, w;
            scanf("%d%d%d", &u, &v, &w);
            g[u].push_back({v,w});
            f[v].push_back({u,w});
        }
        auto ga = dij(g,a), gb = dij(g,b);
        auto fa = dij(f,a), fb = dij(f,b);
        int ans = INF, pos = -1;
        for (int i=0; i<n; ++i) 
            if (i!=a&&i!=b) 
                for (int x=0; x<=k; ++x)
                    for (int y=0; x+y<=k; ++y)
                        for (int z=0; z<=k; ++z)
                            for (int w=0; z+w<=k; ++w)
                                if (ga[x][i]+(int64_t)gb[z][i]+fa[y][i]+fb[w][i]<ans) 
                                    ans = ga[x][i]+gb[z][i]+fa[y][i]+fb[w][i], pos = i;
        if (pos==-1) puts(">:(");
        else printf("%d %d
    ", pos, ans);
    }
    View Code

    G 题目保证$m$个对不交叉, 所以答案只跟$n,m$有关

    #include <bits/stdc++.h>
    using namespace std;
    const int P = 1e9+7;
    int main() {
        int n, m;
        scanf("%d%d", &n, &m);
        int ans = 1;
        for (int i=1; i<=n; ++i) ans = (int64_t)ans*i%P;
        for (int i=1; i<=m; ++i) ans = ans*(P+1ll)/2%P;
        printf("%d
    ", ans);
    }
    View Code

    H 回文树板子, 就是求下本质不同奇回文个数

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 1e5+10;
    struct PalindromicTree {
        int tot,cnt,last,len[N],fa[N],ch[N][26];
        char s[N];
        void init() {
            s[0] = '#', last = 0, cnt = 1, tot = 0;
            fa[0] = 1, len[0] = 0, len[1] = -1;
            memset(ch[0],0,sizeof ch[0]);
            memset(ch[1],0,sizeof ch[0]);
        }
        int newnode() {
            ++cnt;
            memset(ch[cnt],0,sizeof ch[0]);
            fa[cnt] = len[cnt] = 0;
            return cnt;
        }
        int getfail(int x) {
            while (s[tot-len[x]-1]!=s[tot]) x=fa[x];
            return x;
        }
        void ins(int c) {
            s[++tot] = c;
            int p = getfail(last);
            if (!ch[p][c]) {
                int q = newnode();
                len[q] = len[p]+2;
                fa[q] = ch[getfail(fa[p])][c];
                ch[p][c] = q;
            }
            last = ch[p][c];
        }
    } pam;
    char s[N];
    int main() {
        int n;
        scanf("%d%s", &n, s+1);
        pam.init();
        for (int i=1; i<=n; ++i) pam.ins(s[i]-'a');
        int ans = 0;    
        for (int i=1; i<=pam.cnt; ++i) if (pam.len[i]%2&&pam.len[i]>1) ++ans;
        printf("%d
    ", ans);
    }
    View Code

    I 单调栈处理一下左侧右侧能到达的最近的点, 然后按权值从大到小$dp$

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 1e5+10;
    int L[N], R[N];
    pair<int,int> a[N];
    int64_t dp[N];
    int main() {
        int n;
        scanf("%d", &n);
        for (int i=1; i<=n; ++i) scanf("%d", &a[i].first), a[i].second=i;
        vector<int> sta;
        for (int i=1; i<=n; ++i) {
            while (sta.size()&&a[i].first>a[sta.back()].first) sta.pop_back();
            if (sta.size()&&a[i].first==a[sta.back()].first) L[i] = L[sta.back()];
            else {
                if (sta.size()) L[i] = sta.back();
                sta.push_back(i);
            }
        }
        sta.clear();
        for (int i=n; i; --i) {
            while (sta.size()&&a[i].first>a[sta.back()].first) sta.pop_back();
            if (sta.size()&&a[i].first==a[sta.back()].first) R[i] = R[sta.back()];
            else {
                if (sta.size()) R[i] = sta.back();
                sta.push_back(i);
            }
        }
        sort(a+1, a+1+n, greater<pair<int,int>>());
        for (int i=1; i<=n; ++i) {
            int id = a[i].second;
            if (L[id]) dp[id] = max(dp[id], dp[L[id]]+id-L[id]);
            if (R[id]) dp[id] = max(dp[id], dp[R[id]]+R[id]-id);
        }
        for (int i=1; i<=n; ++i) printf("%lld ", dp[i]);puts("");
    }
    View Code

    J

    K 模拟

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 1e5+10;
    char A[N], B[N];
    int main() {
        scanf("%s%s", A+1, B+1);
        int n = strlen(A+1);
        int m = strlen(B+1);
        int now = 1;
        for (int i=1; i<=n; ++i) {
            if (isdigit(A[i])) {
                int j = i;
                while (j<n&&isdigit(A[j+1])) ++j;
                int nxt = now;
                while (nxt<m&&isdigit(B[nxt+1])) ++nxt;
                if (nxt-now!=j-i) return puts(j-i<nxt-now?"<":">"),0;
                for (int k=i; k<=j; ++k) {
                    if (A[k]!=B[now]) return puts(A[k]<B[now]?"<":">"),0;
                    ++now;
                }
                i = j;
            }
            else {
                if (A[i]!=B[now]) return puts(A[i]<B[now]?"<":">"),0;
                ++now;
            }
        }
        puts("=");
    }
    View Code

    L 预处理上下左右隧道走到的位置, 从终点$bfs$一下, 然后从起点贪心求字典序最小的最短路

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 2e3+10;
    const int dx[]={0,0,-1,1};
    const int dy[]={-1,1,0,0};
    int n, m, L[N][N], R[N][N], U[N][N], D[N][N], dis[N][N];
    char s[N][N];
    int main() {
        scanf("%d%d", &n, &m);
        for (int i=0; i<n; ++i) scanf("%s", s[i]);
        queue<pair<int,int>> q;
        int x, y;
        for (int i=0; i<n; ++i) {
            for (int j=0; j<m; ++j) {
                L[i][j] = R[i][j] = U[i][j] = D[i][j] = dis[i][j] = -1;
                if (s[i][j]=='E') q.push({i,j}), dis[i][j] = 0;
            }
        }
        for (int i=0; i<n; ++i) {
            for (int j=0; j<m; ++j) if (s[i][j]!='X') {
                if (j+1<m&&s[i][j+1]=='X') {
                    int k = j+1;
                    while (k+1<m&&s[i][k+1]=='X') ++k;
                    if (k+1<m) R[i][j] = k+1;
                }
                if (j-1>=0&&s[i][j-1]=='X') {
                    int k = j-1;
                    while (k-1>=0&&s[i][k-1]=='X') --k;
                    if (k-1>=0) L[i][j] = k-1;
                }
                if (i+1<n&&s[i+1][j]=='X') {
                    int k = i+1;
                    while (k+1<n&&s[k+1][j]=='X') ++k;
                    if (k+1<n) D[i][j] = k+1;
                }
                if (i-1>=0&&s[i-1][j]=='X') {
                    int k = i-1;
                    while (k-1>=0&&s[k-1][j]=='X') --k;
                    if (k-1>=0) U[i][j] = k-1;
                }
            }
        }
        while (q.size()) {
            auto u = q.front(); q.pop();
            if (U[u.first][u.second]!=-1&&dis[U[u.first][u.second]][u.second]==-1) {
                dis[U[u.first][u.second]][u.second] = dis[u.first][u.second]+1;
                q.push({U[u.first][u.second],u.second});
            }
            if (D[u.first][u.second]!=-1&&dis[D[u.first][u.second]][u.second]==-1) {
                dis[D[u.first][u.second]][u.second] = dis[u.first][u.second]+1;
                q.push({D[u.first][u.second],u.second});
            }
            if (L[u.first][u.second]!=-1&&dis[u.first][L[u.first][u.second]]==-1) {
                dis[u.first][L[u.first][u.second]] = dis[u.first][u.second]+1;
                q.push({u.first,L[u.first][u.second]});
            }
            if (R[u.first][u.second]!=-1&&dis[u.first][R[u.first][u.second]]==-1) {
                dis[u.first][R[u.first][u.second]] = dis[u.first][u.second]+1;
                q.push({u.first,R[u.first][u.second]});
            }
            for (int k=0; k<4; ++k) {
                pair<int,int> v(u.first+dx[k],u.second+dy[k]);
                if (v.first<0||v.second<0||v.first>=n||v.second>=m) continue;
                if (s[v.first][v.second]!='X'&&dis[v.first][v.second]==-1) {
                    dis[v.first][v.second] = dis[u.first][u.second]+1;
                    q.push(v);
                }
            }
        }
        int ans;
        for (int i=0; i<n; ++i)
            for (int j=0; j<m; ++j)
                if (s[i][j]=='S') ans = dis[i][j], x = i, y = j;
        if (ans==-1) return puts("-1"), 0;
        printf("%d
    ", ans);
        vector<pair<int,int>> g[2];
        g[0].push_back({x,y});
        for (int i=0; i<ans; ++i) {
            g[i&1^1].clear();
            for (auto [x,y]:g[i&1]) {
                int ok = 0;
                if (x+1<n&&dis[x+1][y]==dis[x][y]-1) { 
                    ok = 1;
                    g[i&1^1].push_back({x+1,y});
                }
                if (D[x][y]!=-1&&dis[D[x][y]][y]==dis[x][y]-1) {
                    ok = 1;
                    g[i&1^1].push_back({D[x][y],y});
                }
                if (ok) {
                    putchar('D');
                    continue;
                }
                if (y>0&&dis[x][y-1]==dis[x][y]-1) {
                    ok = 1;
                    g[i&1^1].push_back({x,y-1});
                }
                if (L[x][y]!=-1&&dis[x][L[x][y]]==dis[x][y]-1) {
                    ok = 1;
                    g[i&1^1].push_back({x,L[x][y]});
                }
                if (ok) {
                    putchar('L');
                    continue;
                }
                if (y+1<m&&dis[x][y+1]==dis[x][y]-1) { 
                    ok = 1;
                    g[i&1^1].push_back({x,y+1});
                }
                if (R[x][y]!=-1&&dis[x][R[x][y]]==dis[x][y]-1) {
                    g[i&1^1].push_back({x,R[x][y]});    
                    ok = 1;
                }
                if (ok) {
                    putchar('R');
                    continue;
                }
                if (x>0&&dis[x-1][y]==dis[x][y]-1) {
                    g[i&1^1].push_back({x-1,y});
                    ok = 1;
                }
                if (U[x][y]!=-1&&dis[U[x][y]][y]==dis[x][y]-1) {
                    g[i&1^1].push_back({U[x][y],y});
                    ok = 1;
                }
                if (!ok) throw;
                putchar('U');
            }
        }
        puts("");
    }
    View Code

    M 序列自动机板子

    #include <bits/stdc++.h>
    const int N = 2e5+10;
    int nxt[N][26];
    char s[N];
    
    int main() {
        scanf("%s", s+1);
        int n = strlen(s+1);
        for (int i=0; i<26; ++i) nxt[n+1][i]=nxt[n+2][i]=n+1;
        for (int i=n; i; --i) {
            memcpy(nxt[i],nxt[i+1],sizeof nxt[i]);
            nxt[i][s[i]-'a'] = i;
        }
        int q;
        scanf("%d", &q);
        while (q--) {
            scanf("%s", s+1);
            int m = strlen(s+1), now = 1;
            for (int i=1; i<=m; ++i) {
                now = nxt[now][s[i]-'a']+1;
                if (now>n+1) {
                    if (i==1) puts("IMPOSSIBLE");
                    else s[i] = 0, puts(s+1);
                    break;
                }
                if (i==m) puts(s+1);
            }
        }
    }
    View Code

    N

    2016-2017 National Taiwan University World Final Team Selection Contest

    A 离线二分答案, 转化为对于一个$01$序列进行排序, 用线段树模拟即可

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 1e5+10;
    int n, m, a[N], b[N], c[N], l[N], r[N];
    struct {
        int sum,tag;
        void upd(int v, int len) {
            sum = v*len;
            tag = v;
        }
    } tr[N<<2];
    void build(int o, int l, int r) {
        tr[o].tag = -1;
        if (l==r) tr[o].sum = c[l];
        else {
            int mid = (l+r)/2;
            build(o<<1,l,mid),build(o<<1|1,mid+1,r);
            tr[o].sum = tr[o<<1].sum+tr[o<<1|1].sum;
        }
    }
    void pd(int o, int l, int r) {
        if (tr[o].tag!=-1) {
            int mid = (l+r)/2;
            tr[o<<1].upd(tr[o].tag,mid-l+1);
            tr[o<<1|1].upd(tr[o].tag,r-mid);
            tr[o].tag=-1;
        }
    }
    int query(int o, int l, int r, int ql, int qr) {
        if (ql<=l&&r<=qr) return tr[o].sum;
        pd(o,l,r);
        int ans = 0, mid = (l+r)/2;
        if (mid>=ql) ans += query(o<<1,l,mid,ql,qr);
        if (mid<qr) ans += query(o<<1|1,mid+1,r,ql,qr);
        return ans;
    }
    void upd(int o, int l, int r, int ql, int qr, int v) {
        if (ql<=l&&r<=qr) return tr[o].upd(v,r-l+1);
        pd(o,l,r);
        int mid = (l+r)/2;
        if (mid>=ql) upd(o<<1,l,mid,ql,qr,v);
        if (mid<qr) upd(o<<1|1,mid+1,r,ql,qr,v);
        tr[o].sum = tr[o<<1].sum+tr[o<<1|1].sum;
    }
    int chk(int x) {
        for (int i=1; i<=n; ++i) c[i] = a[i]>=x;
        build(1,1,n);
        for (int i=1; i<=m; ++i) {
            if (l[i]<=r[i]) {
                int sum = query(1,1,n,l[i],r[i]);
                if (sum&&sum!=r[i]-l[i]+1) {
                    upd(1,1,n,r[i]-sum+1,r[i],1);
                    upd(1,1,n,l[i],r[i]-sum,0);
                }
            }
            else {
                int sum = query(1,1,n,r[i],l[i]);
                if (sum&&sum!=l[i]-r[i]+1) {
                    upd(1,1,n,r[i],r[i]+sum-1,1);
                    upd(1,1,n,r[i]+sum,l[i],0);
                }
            }
        }
        return query(1,1,n,(1+n)/2,(1+n)/2);
    }
    int main() {
        scanf("%d%d", &n, &m);
        for (int i=1; i<=n; ++i) scanf("%d", a+i), b[i] = a[i];
        for (int i=1; i<=m; ++i) scanf("%d%d",l+i,r+i);
        sort(b+1,b+1+n);
        int lx = 1, rx = n, ans;
        while (lx<=rx) {
            int mid = (lx+rx)/2;
            if (chk(b[mid])) ans=mid,lx=mid+1;
            else rx=mid-1;
        }
        printf("%d
    ", b[ans]);
    }
    View Code

    B

    C 一个格子合法等价于边界的交点数等于$2$, 并且合法的格子一定是八连通的, 直接$bfs$模拟

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 2e3+10;
    const int dx[]={0,0,-1,-1,-1,1,1,1};
    const int dy[]={1,-1,0,-1,1,0,-1,1};
    int n, clk, a[N][N], vis[N][N];
    int chk(int x, int y, int x1, int y1, int x2, int y2) {
        int L = min(x1, x2)+1, R = max(x1, x2);
        int D = min(y1, y2)+1, U = max(y1, y2);
        if (x<L||x>R||y<D||y>U) return 0;
        if (vis[x][y]==clk) return 0;
        if (y1<y2) swap(x1,x2),swap(y1,y2);
        int t = (x1-x2)*(y-y1)+x1*(y1-y2), cnt = 0;
        if ((x-1)*(y1-y2)<=t&&t<x*(y1-y2)) ++cnt;
        t = (x1-x2)*(y-1-y1)+x1*(y1-y2);
        if ((x-1)*(y1-y2)<t&&t<=x*(y1-y2)) ++cnt;
        if (x1<x2) swap(x1,x2),swap(y1,y2);
        t = (y1-y2)*(x-x1)+y1*(x1-x2);
        if ((y-1)*(x1-x2)<t&&t<=y*(x1-x2)) ++cnt;
        t = (y1-y2)*(x-1-x1)+y1*(x1-x2);
        if ((y-1)*(x1-x2)<=t&&t<y*(x1-x2)) ++cnt;
        return cnt>=2;
    }
    
    int main() {
        scanf("%d", &n);
        while (n--) {
            int x1,y1,x2,y2;
            scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
            if (x1==x2||y1==y2) continue;
            queue<pair<int,int> > q;
            ++clk;
            for (int k=0; k<8; ++k) { 
                if (chk(x1+dx[k],y1+dy[k],x1,y1,x2,y2)) { 
                    q.push({x1+dx[k],y1+dy[k]});
                    vis[x1+dx[k]][y1+dy[k]] = clk;
                }
            }
            while (q.size()) {
                auto u = q.front(); q.pop();
                for (int k=0; k<8; ++k) {
                    pair<int,int> v(u.first+dx[k],u.second+dy[k]);
                    if (chk(v.first,v.second,x1,y1,x2,y2)) {
                        q.push(v);
                        vis[v.first][v.second] = clk;
                    }
                }
            }
        }
        int sum = 0;
        for (int i=1; i<=2000; ++i) for (int j=1; j<=2000; ++j) if (vis[i][j]) ++sum;
        printf("%d
    ", sum);
    }
    View Code

    D

    E

    F

    G 二分答案, 考虑$bfs$判断和不超过$x$的个数是否超过$k$

    先排序, 每个状态存当前和,以及选取的最大数, 每次往后暴力遍历, 和超过$x$就退出, 这样每次检验复杂度就是$O(k)$的

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 2e5+10;
    int n, k, a[N];
    int chk(long long x) {
        struct Q {long long s;int id;};
        queue<Q> q;
        q.push({0,1});
        int cnt = 0;
        while (q.size()) {
            auto u = q.front(); q.pop();
            for (int i=u.id; i<=n; ++i) {
                if (u.s+a[i]<=x) {
                    if (++cnt==k) return 1;
                    q.push({u.s+a[i],i+1});
                }
                else break;
            }
        }
        return 0;
    }
    int main() {
        scanf("%d%d", &n, &k);
        for (int i=1; i<=n; ++i) scanf("%d", a+i);
        sort(a+1,a+1+n);
        long long l=0, r=1e18, ans;
        while (l<=r) {
            long long mid = (l+r)/2;
            if (chk(mid)) ans=mid,r=mid-1;
            else l=mid+1;
        }
        printf("%lld
    ", ans);
    }
    View Code

    H

    I
    J

    最优解一定是选取一个区间, 然后把区间内$1$全移走, 还有剩余操作数的话, 再把区间外的$0$移进来

    所以一个区间$[l,r]$的贡献就为$sum(l,r,0)+k-sum(l,r,1)$, 并且要求满足$sum(l,r,1)le k$, 枚举右端点, 单调队列求出最优左端点即可

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 1e6+10;
    char s[N];
    int n, q, sum[N][2];
    void chkmax(int &a, int b) {a<b?a=b:0;}
    void work() {
        int k;
        scanf("%d", &k);
        deque<int> q;
        q.push_back(0);
        int ans = k;
        for (int i=1; i<=n; ++i) {
            while (q.size()&&sum[q[0]][1]<sum[i][1]-k) q.pop_front();
            if (q.size()) ans = max(ans, sum[i][0]-sum[q[0]][0]+k-sum[i][1]+sum[q[0]][1]);
            while (q.size()&&sum[i][1]-sum[i][0]>=sum[q.back()][1]-sum[q.back()][0]) q.pop_back();
            q.push_back(i);
        }
        ans = min(ans, sum[n][0]);
        printf("%d
    ", ans);
    }
    int main() {
        scanf("%s%d", s+1, &q);
        n = strlen(s+1);
        for (int i=1; i<=n; ++i) {
            sum[i][0] = sum[i-1][0];
            sum[i][1] = sum[i-1][1];
            ++sum[i][s[i]=='1'];
        }
        while (q--) work();
    }
    View Code

    2019 icpc Jakarta

    A 简单签到

    B

    直接暴力树形$dp$, 设${dp}_{0,0}$表示没和儿子匹配方案, ${dp}_{0,1}$表示没和儿子匹配,但之前有可以匹配的没匹配的方案, ${dp}_{1,0}$表示和一个儿子匹配的方案, ${dp}_{1,1}$表示和一个儿子配,但之前有可以匹配没匹配的方案,${dp}_{2,0}$表示与两个儿子匹配的方案

    枚举所有情况转移即可, 假设与儿子匹配, 那么就从${dp}_{y,0,0},{dp}_{y,1,0},{dp}_{y,1,1}$三种状态转移, 假设不与儿子匹配, 那么就从${dp}_{y,0,0},{dp}_{y,1,0},{dp}_{y,2,0}$三种状态转移, 一共有$5 imes 6=30$种转移方式, 还要注意${dp}_{x,2,0}$不能与儿子匹配, 那么也就是$27$种转移. 写完要数一下27种, 保证不重不漏

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 1e5+10, P = 1e9+7;
    int n, dp[N][3][2];
    vector<int> g[N];
    void dfs(int x, int f) {
        dp[x][0][0] = 1;
        for (int y:g[x]) if (y!=f) {
            dfs(y,x);
            int sum = (dp[y][0][0]+dp[y][1][0]+(int64_t)dp[y][2][0])%P;
            int add = (dp[y][0][0]+dp[y][1][0]+(int64_t)dp[y][1][1])%P;
            int nxt00 = (int64_t)dp[x][0][0]*dp[y][2][0]%P;
            int nxt01 = ((int64_t)dp[x][0][0]*dp[y][0][0]+(int64_t)dp[x][0][0]*dp[y][1][0]+(int64_t)dp[x][0][1]*sum)%P;
            int nxt10 = ((int64_t)dp[x][1][0]*dp[y][2][0]+(int64_t)dp[x][0][0]*add)%P;
            int nxt11 = ((int64_t)dp[x][1][0]*dp[y][0][0]+(int64_t)dp[x][1][0]*dp[y][1][0]+(int64_t)dp[x][1][1]*sum+(int64_t)dp[x][0][1]*add)%P;
            int nxt20 = ((int64_t)dp[x][1][0]*add+(int64_t)dp[x][1][1]*add+(int64_t)dp[x][2][0]*sum)%P;
            dp[x][0][0] = nxt00;
            dp[x][0][1] = nxt01;
            dp[x][1][0] = nxt10;
            dp[x][1][1] = nxt11;
            dp[x][2][0] = nxt20;
        }
    }
    int main() {
        scanf("%d", &n);
        for (int i=1; i<n; ++i) {
            int u, v;
            scanf("%d%d", &u, &v);
            g[u].push_back(v);
            g[v].push_back(u);
        }
        dfs(1,0);
        printf("%lld
    ", (dp[1][0][0]+dp[1][1][0]+(int64_t)dp[1][2][0])%P);
    }
    View Code

    C 只要起点终点为偶数, 并且中间行和中间列的奇偶性不变, 那么起点终点对应的一块矩形就全是偶数

    #include <bits/stdc++.h>
    using namespace std;
    
    int main() {
        int n, q;
        scanf("%d%d", &n, &q);
        vector<int> R(n),C(n);
        for (int i=0; i<n; ++i) scanf("%d", &R[i]), R[i] &= 1;
        for (int i=0; i<n; ++i) scanf("%d", &C[i]), C[i] &= 1;
        vector<int> f(n), g(n);
        for (int i=0; i<n; ++i) {
            int j = i;
            while (j+1<n&&R[j+1]==R[i]) ++j;
            for (int k=i; k<=j; ++k) f[k] = i;
            i = j;
        }
        for (int i=0; i<n; ++i) {
            int j = i;
            while (j+1<n&&C[j+1]==C[i]) ++j;
            for (int k=i; k<=j; ++k) g[k] = i;
            i = j;
        }
        while (q--) {
            int ra,ca,rb,cb;
            scanf("%d%d%d%d", &ra, &ca, &rb, &cb);
            --ra, --ca, --rb, --cb;
            if (f[ra]==f[rb]&&g[ca]==g[cb]&&(R[ra]+C[ca])%2==0&&(R[rb]+C[cb])%2==0) puts("YES");
            else puts("NO");
        }
    }
    View Code

    D

    E 从后往前贪心求出每个位置可取范围, 然后从前往后贪心求字典序最小

    #include <bits/stdc++.h>
    using namespace std;
    int main() {
        int n, l, r, k;
        scanf("%d%d%d%d", &n, &l, &r, &k);
        vector<int> a(n), L(n), R(n);
        for (int i=0; i<n; ++i) scanf("%d", &a[i]);
        L[n-1] = l, R[n-1] = r;
        for (int i=n-2; i>=0; --i) {
            L[i] = l, R[i] = r;
            if (a[i]>a[i+1]) {
                L[i] = max(L[i], L[i+1]+1);
                R[i] = min(R[i], R[i+1]+k);
            }
            else if (a[i]==a[i+1]) {
                L[i] = max(L[i], L[i+1]);
                R[i] = min(R[i], R[i+1]);
            }
            else {
                L[i] = max(L[i], L[i+1]-k);
                R[i] = min(R[i], R[i+1]-1);
            }
            if (L[i]>R[i]) return puts("-1"), 0;
        }
        vector<int> ans;
        ans.push_back(L[0]);
        for (int i=1; i<n; ++i) {
            if (a[i]>a[i-1]) {
                ans.push_back(max(ans.back()+1,L[i]));
            }
            else if (a[i]==a[i-1]) {
                ans.push_back(ans.back());
            }
            else {
                ans.push_back(max(ans.back()-k,L[i]));
            }
        }
        for (int i=0; i<n; ++i) printf("%d%c", ans[i], " 
    "[i+1==n]);
    }
    View Code

    F 合法点一定只能是重心, 直接暴力枚举重心, 树hash判同构即可

    #include <bits/stdc++.h>
    using namespace std;
    const int P = 799898821, B = 2333333;
    const int N = 4e3+10;
    int n, sz[N], deg[N], p[N];
    vector<int> g[N],h;
    void dfs(int x, int f) {
        sz[x] = 1;
        for (int y:g[x]) if (y!=f) {
            dfs(y,x);
            sz[x] += sz[y];
        }
    }
    void dfs3(int x, int f, int tf) {
        vector<int> v;
        for (int y:g[x]) if (y!=f&&y!=tf) {
            dfs3(y,x,tf);
            v.push_back(p[y]);
        }
        sort(v.begin(),v.end());
        p[x] = 1;
        for (auto &t:v) p[x] = ((int64_t)p[x]*B+t)%P;
    }
    void dfs2(int x, int f, int tf) {
        dfs3(x,-1,tf);
        h.push_back(p[x]);
        for (int y:g[x]) if (y!=f) dfs2(y,x,tf);
    }
    void gethash(int x, int y) {
        h.clear();
        dfs2(x,y,y);
        sort(h.begin(),h.end());
    }
    
    int main() {
        scanf("%d", &n);
        for (int i=1; i<n; ++i) {
            int u, v;
            scanf("%d%d", &u, &v);
            g[u].push_back(v);
            g[v].push_back(u);
            ++deg[u],++deg[v];
        }
        int ans = -1;
        for (int i=1; i<=n; ++i) if (deg[i]>1) {
            int now_sz = -1, ok = 1;
            for (int j:g[i]) { 
                dfs(j,i);
                if (now_sz==-1) now_sz = sz[j];
                else if (now_sz!=sz[j]) {
                    ok = 0;
                    break;
                }
            }
            if (!ok) continue;
            vector<int> has;
            int cnt = 0;
            for (int j:g[i]) {
                gethash(j,i);
                if (has.empty()) has = h;
                else if (has!=h) {
                    ok = 0;
                    break;
                }
                ++cnt;
            }
            if (ok) ans = max(ans, cnt);
        }
        printf("%d
    ", ans);
    }
    View Code

    G 只用考虑每一天比$x$小的个数$cnt$, 如果存在一天使得$cnt<R$, 那么不合法, 否则合法

    可以用线段树维护不合法的天数

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 1e5+10;
    int n, m, q, w, a[N], cnt[N], now[N];
    vector<int> b[N];
    struct {
        int mi,tag;
        void add(int x) {mi+=x;tag+=x;}
    } tr[N<<2];
    void build(int o, int l, int r) {
        if (l==r) { 
            tr[o].mi = now[l]-(int)b[l].size();
        }
        else {
            int mid = (l+r)/2;
            build(o<<1,l,mid);
            build(o<<1|1,mid+1,r);
            tr[o].mi = min(tr[o<<1].mi, tr[o<<1|1].mi);
        }
    }
    void pd(int o) {
        if (tr[o].tag) {
            tr[o<<1].add(tr[o].tag);
            tr[o<<1|1].add(tr[o].tag);
            tr[o].tag = 0;
        }
    }
    void add(int o, int l, int r, int ql, int qr, int v) {
        if (ql<=l&&r<=qr) return tr[o].add(v);
        pd(o); int mid = (l+r)/2;
        if (mid>=ql) add(o<<1,l,mid,ql,qr,v);
        if (mid<qr) add(o<<1|1,mid+1,r,ql,qr,v);
        tr[o].mi = min(tr[o<<1].mi,tr[o<<1|1].mi);
    }
    int find(int o, int l, int r, int ql, int qr, int v) {
        if (tr[o].mi>v) return -1;
        int mid = (l+r)/2;
        if (ql<=l&&r<=qr) {
            if (l==r) return w=tr[o].mi, l;
            pd(o);
            if (tr[o<<1].mi<=v) return find(o<<1,l,mid,ql,qr,v);
            return find(o<<1|1,mid+1,r,ql,qr,v);
        }
        pd(o);
        if (mid>=qr) return find(o<<1,l,mid,ql,qr,v);
        if (mid<ql) return find(o<<1|1,mid+1,r,ql,qr,v);
        int t = find(o<<1,l,mid,ql,qr,v);
        if (t!=-1) return t;
        return find(o<<1|1,mid+1,r,ql,qr,v);
    }
    int main() {
        scanf("%d%d%d", &n, &m, &q);
        for (int i=1; i<=n; ++i) { 
            scanf("%d", &a[i]);
            if (a[i]<a[1]) ++cnt[0];
        }
        for (int i=1; i<=m; ++i) {
            int r;
            scanf("%d", &r);
            b[i].resize(r);
            for (int j=0; j<r; ++j) { 
                scanf("%d", &b[i][j]);
                if (b[i][j]<a[1]) ++cnt[i];
            }
        }
        now[1] = cnt[0];
        int ans = 0;
        for (int i=1; i<=m; ++i) { 
            if (now[i]<b[i].size()) ++ans, now[i+1] = cnt[i];
            else now[i+1] = now[i]-b[i].size()+cnt[i];
        }
        build(1,1,m);
        for (int i=1; i<=q; ++i) {
            int x, y, z;
            scanf("%d%d%d", &x, &y, &z);
            if (x<m) {
                if (z>a[1]&&b[x][y-1]<a[1]) {
                    int pos = find(1,1,m,x+1,m,0);
                    if (pos!=-1) {
                        if (w==0) ++ans;
                        add(1,1,m,x+1,pos,-1);
                    }
                    else add(1,1,m,x+1,m,-1);
                }
                if (z<a[1]&&b[x][y-1]>a[1]) {
                    int pos = find(1,1,m,x+1,m,-1);
                    if (pos!=-1) {
                        if (w==-1) --ans;
                        add(1,1,m,x+1,pos,1);
                    }
                    else add(1,1,m,x+1,m,1);
                }
            }
            b[x][y-1] = z;
            puts(ans?"0":"1");
        }
    }
    View Code

    H 暴力枚举一边求另一边最大值即可, double乘法误差过大, 不要用

    #include <bits/stdc++.h>
    using namespace std;
    
    int main() {
        int n;
        struct node {
            int x,y,id;
            bool operator < (const node &rhs) const {
                return x>rhs.x;
            }
        };
        struct sum {
            void add(int id, int v) {
                if (a[0].second==id) a[0].first = max(a[0].first, v);
                else if (a[1].first<v) a[1] = {v,id};
                if (a[0].first<a[1].first) swap(a[0], a[1]);
            }
            pair<int,int> a[2] = {{0,-1},{0,-1}};
        } now;
        vector<node> v;
        scanf("%d", &n);
        int cnt = 0;
        uint64_t ans = 0;
        for (int i=0; i<n; ++i) { 
            int x, y;
            scanf("%d%d", &x, &y);
            v.push_back({x,y,i});
            v.push_back({y,x,i});
            ans = max(ans, (uint64_t)x*y);
        }
        sort(v.begin(),v.end());
        for (int i=0; i<v.size(); ++i) {
            now.add(v[i].id,v[i].y);
            if (now.a[1].second!=-1) ans = max(ans, (uint64_t)v[i].x*now.a[1].first*2);
        }
        if (ans%2==0) printf("%llu.0
    ", ans/2);
        else printf("%llu.5
    ", ans/2);
    }
    View Code

    I

    J

    '#'个数很少, 所以把中间连续很长的'.'提出来, 跑dp, 最后再统计提出来的贡献

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 1e5+10;
    int n, k, g1, g2, g3, dp[610][610];
    char s[N],t[N];
    void chkmax(int &a, int b) {a<b?a=b:0;}
    int main() {
        scanf("%d%d%d%d%d%s", &n, &k, &g1, &g2, &g3, s+1);
        int cnt = 0, m = 0;
        for (int i=1; i<=n; ++i) {
            if (s[i]=='.') {
                int j = i;
                while (j+1<n&&s[j+1]=='.') ++j;
                if (j-i+1>=10) {
                    int len = j-i+1-10;
                    if (len%2) --len;
                    cnt += len;
                    for (int k=0; k<j-i+1-len; ++k) t[++m] = '.';
                }
                else {
                    for (int k=0; k<j-i+1; ++k) t[++m] = '.';
                }
                i = j;
            }
            else t[++m] = '#';
        }
        memset(dp,0xef,sizeof dp);
        dp[0][0] = 0;
        for (int i=1; i<=m; ++i) {
            for (int j=0; j<i; ++j) {
                int &r = dp[i-1][j];
                if (r<0) continue;
                chkmax(dp[i][j],r);
                if (t[i]=='.') chkmax(dp[i][j+1],r+g1);
                if (t[i]=='.'&&t[i+1]=='.') chkmax(dp[i+1][j],r+g2);
                if (t[i]=='.'&&t[i+1]=='#'&&t[i+2]=='.') chkmax(dp[i+2][j],r+g3);
            }
        }
        int ans = 0;
        for (int i=0; i<=k; ++i) {
            ans = max(ans, dp[m][i]+cnt/2*g2);
            int t = min(k-i,cnt);
            ans = max(ans, dp[m][i]+t*g1+(cnt-t)/2*g2);
            if (t&1) ans = max(ans, dp[m][i]+(t-1)*g1+(cnt-t+1)/2*g2);
        }
        printf("%d
    ", ans);
    }
    View Code 

    K 线段树维护矩阵转移即可

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 1e5+10, P = 1e9+7;
    int n, q;
    char s[N];
    struct node {
        int a[2][2],b[2][2],tag;
        node operator + (const node &rhs) const {
            node ret;
            memset(ret.a,0,sizeof ret.a);
            memset(ret.b,0,sizeof ret.b);
            ret.tag = 0;
            for (int k=0; k<2; ++k)
                for (int i=0; i<2; ++i)
                    for (int j=0; j<2; ++j)
                        ret.a[i][j] = (ret.a[i][j]+(int64_t)a[k][j]*rhs.a[i][k])%P,
                        ret.b[i][j] = (ret.b[i][j]+(int64_t)b[k][j]*rhs.b[i][k])%P;
            return ret;
        }
        void flip() {tag^=1,swap(a,b);}
    } tr[N<<2];
    void build(int o, int l, int r) {
        if (l==r) {
            tr[o].a[0][0] = 1;
            tr[o].a[0][1] = 1;
            tr[o].a[1][0] = 0;
            tr[o].a[1][1] = 1;
            tr[o].b[0][0] = 1;
            tr[o].b[0][1] = 0;
            tr[o].b[1][0] = 1;
            tr[o].b[1][1] = 1;
            if (s[l]=='B') swap(tr[o].a,tr[o].b);
            return;
        }
        int mid = (l+r)/2;
        build(o<<1,l,mid);
        build(o<<1|1,mid+1,r);
        tr[o] = tr[o<<1]+tr[o<<1|1];
    }
    void pd(int o) {
        if (tr[o].tag) {
            tr[o<<1].flip();
            tr[o<<1|1].flip();
            tr[o].tag = 0;
        }
    }
    void upd(int o, int l, int r, int ql, int qr) {
        if (ql<=l&&r<=qr) return tr[o].flip();
        pd(o); int mid = (l+r)/2;
        if (mid>=ql) upd(o<<1,l,mid,ql,qr);
        if (mid<qr) upd(o<<1|1,mid+1,r,ql,qr);
        tr[o] = tr[o<<1]+tr[o<<1|1];
    }
    node query(int o, int l, int r, int ql, int qr) {
        if (ql<=l&&r<=qr) return tr[o];
        pd(o); int mid = (l+r)/2;
        if (mid>=qr) return query(o<<1,l,mid,ql,qr);
        if (mid<ql) return query(o<<1|1,mid+1,r,ql,qr);
        return query(o<<1,l,mid,ql,qr)+query(o<<1|1,mid+1,r,ql,qr);
    }
    int main() {
        scanf("%d%d%s", &n, &q, s+1);
        build(1,1,n);
        while (q--) {
            int op, l, r, a, b;
            scanf("%d%d%d", &op, &l, &r);
            if (op==1) upd(1,1,n,l,r);
            else {
                node u = query(1,1,n,l,r);
                scanf("%d%d", &a, &b);
                int ansa = ((int64_t)u.a[0][0]*a+(int64_t)u.a[0][1]*b)%P;
                int ansb = ((int64_t)u.a[1][0]*a+(int64_t)u.a[1][1]*b)%P;
                printf("%d %d
    ", ansa, ansb);
            }
        }
    }
    View Code

    2019 ccpc final

    A 模拟

    B

    连一条$(1,1)$的自环, 保证最终路径最终一定无限循环

    从起点开始贪心, 维护一个可取集合, 每次走最小, 这样可以$O(nm)$求出一条长$3n$的最小路径

    那么前$n$步中某一步会进入一个环, 然后$kmp$在[n+1,3n]中找环即可 

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 2e3+10, P = 1e9+7;
    int n, m, vis[N], fail[N*2], pw[N*2];
    vector<pair<int,int>> g[N],f[N];
    int qpow(int a, int n) {
        int ans = 1;
        for (; n; n>>=1,a=(int64_t)a*a%P) if (n&1) ans=(int64_t)ans*a%P;
        return ans;
    }
    void dfs(int x) {
        if (vis[x]) return;
        vis[x] = 1;
        for (auto &e:f[x]) dfs(e.first);
    }
    vector<int> get_cyc(vector<int> v) {
        fail[0] = fail[1] = 0;
        int j = 0;
        for (int i=1; i<v.size(); ++i) {
            while (j&&v[i]!=v[j]) j=fail[j];
            if (v[i]==v[j]) ++j;
            fail[i+1] = j;
        }
        int len = v.size()-fail[v.size()];
        v.resize(len);
        return v;
    }
    int calc(vector<int> v) {
        int num = 0;
        for (int t:v) num = (num*10ll+t)%P;
        return num;
    }
    int main() {
        pw[0] = 1;
        for (int i=1; i<2*N; ++i) pw[i] = pw[i-1]*700000005ll%P;
        int t;
        scanf("%d", &t);
        for (int clk=1; clk<=t; ++clk) {
            scanf("%d%d", &n, &m);
            for (int i=0; i<n; ++i) { 
                g[i].clear(), f[i].clear();
                vis[i] = 0;
            }
            for (int i=0; i<m; ++i) {
                int u, v, w;
                scanf("%d%d%d", &u, &v, &w);
                g[u].push_back({v,w});
                f[v].push_back({u,w});
            }
            g[1].push_back({1,0});
            dfs(1);
            vector<int> ans, cur;
            cur.push_back(0);
            for (int i=0; i<3*n; ++i) {
                int mi = 1e9;
                for (int x:cur) for (auto e:g[x]) if (vis[e.first]) mi = min(mi, e.second);
                vector<int> nxt(n);
                for (int x:cur) for (auto e:g[x]) if (vis[e.first]&&e.second==mi) nxt[e.first] = 1;
                ans.push_back(mi);
                cur.clear();
                for (int i=0; i<n; ++i) if (nxt[i]) cur.push_back(i);
            }
            vector<int> ans2(ans.begin()+n,ans.end());
            ans.resize(n);
            ans2 = get_cyc(ans2);
            int x = calc(ans), y = calc(ans2);
            y = (int64_t)y*pw[ans.size()+ans2.size()]%P*qpow(1-pw[ans2.size()],P-2)%P;
            x = (int64_t)x*pw[ans.size()]%P;
            x = (x+y)%P;
            if (x<0) x += P;
            printf("Case #%d: %d
    ", clk, x);
        }
    }
    View Code

    C

    D

    E

    选了一个点后, 要删掉周围一个菱形区域, 对坐标分块, 暴力检验附近的点即可

    F

    G

    H

    中间的SAD直接算在答案里, 然后每个串左侧只有是$D,AD$才有贡献,右侧只有是$S,SA$才有贡献,或者是单独一个$A$

    大力贪心分类讨论即可

    I

    简单构造, 把(i,j)放在(j,1,i),(j,2,i)即可

    J

    直接dsu on tree可过

    L 暴力找规律, 特判$1 imes 1,1 imes 2$, 否则答案为$2(n+m-2)$

    The 13th Chinese Northeast Collegiate Programming Contest

    A

    B

    枚举分母, 堆贪心求分子最大值

    #include <bits/stdc++.h>
    using namespace std;
    int64_t gcd(int64_t a, int64_t b) {return b?gcd(b,a%b):a;}
    int main() {
        int t;
        scanf("%d", &t);
        while (t--) {
            int n, m;
            scanf("%d%d", &n, &m);
            vector<int> l(m);
            vector<vector<int>> g(m);
            for (int i=0; i<m; ++i) scanf("%d", &l[i]);
            for (int i=0; i<n; ++i) {
                int a, b;
                scanf("%d%d", &a, &b);
                --b;
                g[b].push_back(a);
            }
            for (int i=0; i<m; ++i) sort(g[i].begin(),g[i].end(),greater<int>());
            vector<int> id(m);
            for (int i=0; i<m; ++i) id[i] = i;
            sort(id.begin(),id.end(),[&](int a,int b){return l[a]<l[b];});
            int now = 0;
            struct Q {
                int id,pos;
                bool operator < (const Q &rhs) const {
                    return pos>rhs.pos;
                }
            };
            priority_queue<Q> q;
            int64_t sum = 0;
            int64_t ansx = 0, ansy = 0;
            for (int i=1; i<=n; ++i) {
                while (now<m&&l[id[now]]<=i) q.push({id[now++],0});
                while (q.size()&&q.top().pos<i) {
                    auto u = q.top(); q.pop();
                    if (u.pos<g[u.id].size()) {
                        sum += g[u.id][u.pos++];
                        q.push(u);
                    }
                }
                if (i==1) ansx = sum, ansy = 1;
                else if (ansx*i<sum*ansy) ansx = sum, ansy = i;
            }
            int64_t t = gcd(ansx, ansy);
            printf("%lld/%lld
    ", ansx/t, ansy/t);
        }
    }
    View Code

    C

    简单几何

    #include <bits/stdc++.h>
    using namespace std;
    int n;
    int64_t ans;
    int gcd(int a, int b) {return b?gcd(b,a%b):a;}
    pair<int,int> reduce(int x, int y) {
        int g = gcd(abs(x),abs(y));
        x /= g, y /= g;
        if (x<0) x = -x, y = -y;
        return {x,y};
    }
    int main() {
        int t;
        scanf("%d", &t);
        while (t--) {
            int n;
            scanf("%d", &n);
            ans = n*(n-1ll)/2;
            struct node {
                map<int64_t,int> m;
                int sz;
                void add(int64_t x) {
                    if (m.empty()) sz = m[x] = 1;
                    else ::ans -= sz++-m[x]++;
                }
            } s;
            map<pair<int,int>,node> mp;
            for (int i=0; i<n; ++i) {
                int x1,x2,y1,y2;
                scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
                int x, y;
                tie(x,y) = reduce(x1-x2,y1-y2);
                if (!x) s.add(x1);
                else mp[{x,y}].add((int64_t)y*(-x2)+(int64_t)y2*x);
            }
            printf("%lld
    ", ans);
        }
    }
    View Code

    D

    询问离线, 建虚树暴力即可

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 5e5+10;
    int clk, dep[N], L[N], fa[N], val[N];
    vector<int> g[N];
    struct edge {int to,len,w;} f[N];
    bool cmp(int a, int b) {return L[a]<L[b];}
    void dfs(int x, int f, int d) {
        dep[x] = d, fa[x] = f, L[x] = ++clk;
        for (int y:g[x]) if (y!=f) dfs(y,x,d+1);
    }
    int lca(int x, int y) {
        while (x!=y) {
            if (dep[x]<dep[y]) y = fa[y];
            else x = fa[x];
        }
        return x;
    }
    void build(vector<int> a) { 
        sort(a.begin(),a.end(),cmp);
        a.erase(unique(a.begin(),a.end()),a.end());
        vector<int> s;
        s.push_back(a[0]);
        for (int i=1; i<a.size(); ++i) {
            int l = lca(a[i], a[i-1]);
            if (dep[l]!=dep[s.back()]) {
                while (s.size()>1&&dep[s[s.size()-2]]>=dep[l]) {
                    f[s.back()] = {s[s.size()-2],dep[s.back()]-dep[s[s.size()-2]]-1,0};
                    s.pop_back();
                }
                if (dep[s.back()]>dep[l]) {
                    f[s.back()] = {l,dep[s.back()]-dep[l]-1,0};
                    s.back() = l;
                }
            }
            s.push_back(a[i]);
        }
        while (s.size()>1) {
            f[s.back()] = {s[s.size()-2],dep[s.back()]-dep[s[s.size()-2]]-1,0};
            s.pop_back();
        }
    }
    
    int main() {
        int t;
        scanf("%d", &t);
        while (t--) {
            int n, m;
            scanf("%d%d", &n, &m);
            struct Q {int op, u, v, k;};
            vector<Q> q(m);
            clk = 0;
            for (int i=1; i<=n; ++i) { 
                g[i].clear();
                val[i] = 0;
            }
            for (int i=0; i<n-1; ++i) {
                int u, v;
                scanf("%d%d", &u, &v);
                g[u].push_back(v);
                g[v].push_back(u);
            }
            dfs(1,0,0);
            vector<int> v;
            for (int i=0; i<m; ++i) {
                scanf("%d%d%d", &q[i].op, &q[i].u, &q[i].v);
                if (q[i].op<=3||q[i].op==7) scanf("%d", &q[i].k);
                v.push_back(q[i].u);
                v.push_back(q[i].v);
            }
            build(v);
            for (int i=0; i<m; ++i) {
                int x = q[i].u, y = q[i].v, k = q[i].k;
                if (q[i].op==1) {
                    while (x!=y) {
                        if (dep[x]<dep[y]) swap(x, y);
                        val[x] += k;
                        if (f[x].len) f[x].w += k;
                        x = f[x].to;
                    }
                    val[x] += k;
                }
                else if (q[i].op==2) {
                    while (x!=y) {
                        if (dep[x]<dep[y]) swap(x, y);
                        val[x] ^= k;
                        if (f[x].len) f[x].w ^= k;
                        x = f[x].to;
                    }
                    val[x] ^= k;
                }
                else if (q[i].op==3) {
                    while (x!=y) {
                        if (dep[x]<dep[y]) swap(x, y);
                        if (val[x]>=k) val[x] -= k;
                        if (f[x].len&&f[x].w>=k) f[x].w -= k;
                        x = f[x].to;
                    }
                    if (val[x]>=k) val[x] -= k;
                }
                else if (q[i].op==4) {
                    int64_t ans = 0;
                    while (x!=y) {
                        if (dep[x]<dep[y]) swap(x, y);
                        ans += val[x];
                        if (f[x].len) ans += (int64_t)f[x].w*f[x].len;
                        x = f[x].to;
                    }
                    ans += val[x];
                    printf("%lld
    ", ans);
                }
                else if (q[i].op==5) {
                    int ans = 0;
                    while (x!=y) {
                        if (dep[x]<dep[y]) swap(x, y);
                        ans ^= val[x];
                        if (f[x].len&1) ans ^= f[x].w;
                        x = f[x].to;
                    }
                    ans ^= val[x];
                    printf("%d
    ", ans);
                }
                else if (q[i].op==6) {
                    int ma = -2e9, mi = 2e9;
                    while (x!=y) {
                        if (dep[x]<dep[y]) swap(x, y);
                        ma = max(ma, val[x]);
                        mi = min(mi, val[x]);
                        if (f[x].len) { 
                            ma = max(ma, f[x].w);
                            mi = min(mi, f[x].w);
                        }
                        x = f[x].to;
                    }
                    ma = max(ma, val[x]), mi = min(mi, val[x]);
                    printf("%d
    ", ma-mi);
                }
                else {
                    int mi = 2e9;
                    while (x!=y) {
                        if (dep[x]<dep[y]) swap(x, y);
                        mi = min(mi, abs(k-val[x]));
                        if (f[x].len) mi = min(mi, abs(k-f[x].w));
                        x = f[x].to;
                    }
                    mi = min(mi, abs(k-val[x]));
                    printf("%d
    ", mi);
                }
            }
        }
    }
    View Code

    E

    考虑每个点的邻接边, 用最小边与其他边连通即可

    #include <bits/stdc++.h>
    using namespace std;
    int main() {
        int t;
        scanf("%d", &t);
        while (t--) {
            int n;
            scanf("%d", &n);
            vector<vector<int>> g(n);
            for (int i=1; i<n; ++i) {
                int u, v, w;
                scanf("%d%d%d", &u, &v, &w);
                --u, --v;
                g[u].push_back(w);
                g[v].push_back(w);
            }
            int64_t ans = 0;
            for (int i=0; i<n; ++i) if (g[i].size()>1) { 
                int mi = 2e9;
                for (auto &t:g[i]) ans += t, mi = min(mi, t);
                ans += (g[i].size()-2ll)*mi;
            }
            printf("%lld
    ", ans);
        }
    }
    View Code

    F

    G

    x轴y轴可以独立考虑, 转化为给定一些区间, 求一个位置使得所有区间到它距离和最小, 坐标离散化后枚举即可

    也可以直接用中位数做

    #include <bits/stdc++.h>
    using namespace std;
    int main() {
        int t;
        scanf("%d", &t);
        while (t--) {
            int n;
            scanf("%d", &n);
            vector<pair<int,int>> col(n), raw(n);
            for (int i=0; i<n; ++i) scanf("%d%d%d%d", &col[i].first, &raw[i].first, &col[i].second, &raw[i].second);
            auto solve = [&](vector<pair<int,int>> g) {
                vector<pair<int,int>> events;
                int64_t suml = 0, sumr = 0;
                for (int i=0; i<g.size(); ++i) {
                    sumr += g[i].first;
                    events.push_back({g[i].first,1});
                    events.push_back({g[i].second+1,-1});
                }
                sort(events.begin(),events.end());
                int cntl = 0, cntr = g.size()-1;
                sumr -= events[0].first;
                int64_t ans = 1e18;
                for (int i=1; i<events.size(); ++i) {
                    int l = events[i-1].first, r = events[i].first-1;
                    if (l<=r) {
                        if (cntl>cntr) {
                            ans = min(ans, (int64_t)cntl*l-suml+sumr-(int64_t)cntr*l);
                        }
                        else {
                            ans = min(ans, (int64_t)cntl*r-suml+sumr-(int64_t)cntr*r);
                        }
                    }
                    if (events[i].second<0) suml += events[i].first-1, ++cntl;
                    else sumr -= events[i].first, --cntr;
                }
                return ans;
            };
            printf("%lld
    ", solve(col)+solve(raw));
        }
    }
    View Code

    H

    一个区间答案是总和减去相邻数最小值的和, 直接用线段树维护

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 1e5+10;
    int a[N];
    int64_t tr[N<<2],tag[N<<2];
    int64_t tr2[N<<2],tag2[N<<2];
    void build(int o, int l, int r) { 
        tag[o] = 0;
        if (l==r) tr[o] = a[l];
        else { 
            int mid = (l+r)/2;
            build(o<<1,l,mid);
            build(o<<1|1,mid+1,r);
            tr[o] = tr[o<<1]+tr[o<<1|1];
        }
    }
    void build2(int o, int l, int r) {
        tag2[o] = 0;
        if (l==r) tr2[o] = min(a[l], a[l+1]);
        else {
            int mid = (l+r)/2;
            build2(o<<1,l,mid);
            build2(o<<1|1,mid+1,r);
            tr2[o] = tr2[o<<1]+tr2[o<<1|1];
        }
    }
    void pd(int64_t tr[], int64_t tag[], int o, int l, int r) {
        if (tag[o]) {
            tag[o<<1] += tag[o];
            tag[o<<1|1] += tag[o];
            int mid = (l+r)/2;
            tr[o<<1] += tag[o]*(mid-l+1);
            tr[o<<1|1] += tag[o]*(r-mid);
            tag[o] = 0;
        }
    }
    void add(int64_t tr[], int64_t tag[], int o, int l, int r, int ql, int qr, int64_t v) {
        if (ql<=l&&r<=qr) {
            tr[o] += (int64_t)(r-l+1)*v;
            tag[o] += v;
            return;
        }
        pd(tr,tag,o,l,r);
        int mid = (l+r)/2;
        if (mid>=ql) add(tr,tag,o<<1,l,mid,ql,qr,v);
        if (mid<qr) add(tr,tag,o<<1|1,mid+1,r,ql,qr,v);
        tr[o] = tr[o<<1]+tr[o<<1|1];
    }
    int64_t query(int64_t tr[], int64_t tag[], int o, int l, int r, int ql, int qr) { 
        if (ql<=l&&r<=qr) return tr[o];
        pd(tr,tag,o,l,r);
        int mid = (l+r)/2;
        int64_t ans = 0;
        if (mid>=ql) ans += query(tr,tag,o<<1,l,mid,ql,qr);
        if (mid<qr) ans += query(tr,tag,o<<1|1,mid+1,r,ql,qr);
        return ans;
    }
    void upd(int o, int l, int r, int x, int64_t v) {
        if (l==r) tr2[o] = v;
        else {
            pd(tr2,tag2,o,l,r);
            int mid = (l+r)/2;
            if (mid>=x) upd(o<<1,l,mid,x,v);
            else upd(o<<1|1,mid+1,r,x,v);
            tr2[o] = tr2[o<<1]+tr2[o<<1|1];
        }
    }
    int main() {
        int t;
        scanf("%d", &t);
        while (t--) {
            int n, m;
            scanf("%d%d", &n, &m);
            for (int i=0; i<n; ++i) scanf("%d", &a[i]);
            build(1,0,n-1);
            if (n!=1) build2(1,0,n-2);
            while (m--) {
                int op, l, r, k;
                scanf("%d%d%d", &op, &l, &r);
                --l, --r;
                if (op==1) {
                    scanf("%d", &k);
                    add(tr,tag,1,0,n-1,l,r,k);
                    if (l!=r) add(tr2,tag2,1,0,n-2,l,r-1,k);
                    if (l) {
                        int64_t w = min(query(tr,tag,1,0,n-1,l-1,l-1),query(tr,tag,1,0,n-1,l,l));
                        upd(1,0,n-2,l-1,w);
                    }
                    if (r!=n-1) {
                        int64_t w = min(query(tr,tag,1,0,n-1,r,r),query(tr,tag,1,0,n-1,r+1,r+1));
                        upd(1,0,n-2,r,w);
                    }
                }
                else { 
                    int64_t ans = query(tr,tag,1,0,n-1,l,r);
                    if (l!=r) ans -= query(tr2,tag2,1,0,n-2,l,r-1);
                    printf("%lld
    ", ans);
                }
            }
        }
    }
    View Code

    I

    J

    签到

    2018 icpc Seoul

    A

    区间离散化, 枚举右侧直线, 线段树维护左侧直线贡献

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 1e6+10;
    int mx[N<<2],tag[N<<2];
    void add(int o, int l, int r, int ql, int qr, int v) {
        if (ql<=l&&r<=qr) mx[o]+=v,tag[o]+=v;
        else {
            if (tag[o]) {
                tag[o<<1] += tag[o];
                tag[o<<1|1] += tag[o];
                mx[o<<1] += tag[o];
                mx[o<<1|1] += tag[o];
                tag[o] = 0;
            }
            int mid = (l+r)/2;
            if (mid>=ql) add(o<<1,l,mid,ql,qr,v);
            if (mid<qr) add(o<<1|1,mid+1,r,ql,qr,v);
            mx[o] = max(mx[o<<1],mx[o<<1|1]);
        }
    }
    int main() {
        int n;
        scanf("%d", &n);
        vector<pair<int,int>> interval,events;
        vector<int> b;
        interval.push_back({-1,-1});
        for (int i=1; i<=n; ++i) {
            int ux, uy, vx, vy;
            scanf("%d%d%d%d", &ux, &uy, &vx, &vy);
            interval.push_back({vy,uy});
            events.push_back({vy,i});
            events.push_back({uy+1,-i});
            b.push_back(vy);
            b.push_back(uy);
        }
        sort(b.begin(),b.end());
        b.erase(unique(b.begin(),b.end()),b.end());
        for (auto &t:interval) {
            t.first = lower_bound(b.begin(),b.end(),t.first)-b.begin();
            t.second = lower_bound(b.begin(),b.end(),t.second)-b.begin();
        }
        sort(events.begin(),events.end());
        set<int> cur;
        int ans = 0;
        cur.insert(events[0].second);
        for (int i=1; i<events.size(); ++i) {
            int len = events[i].first-events[i-1].first;
            if (len) {
                ans = max(ans, (int)cur.size()+mx[1]);
            }
            int id = events[i].second;
            if (id>0) { 
                cur.insert(id);
            }
            else {
                id = -id;
                cur.erase(id);
                add(1,0,b.size()-1,interval[id].first,interval[id].second,1);
            }
        }
        printf("%d
    ", ans);
    }
    View Code

    B

    转化为给定有向图, 求任意点对路径最小值最大值, 可以直接floyd

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 555;
    int n, m, c[N][N], deg[N], g[N][N];
    int main() {
        scanf("%d%d", &n, &m);
        for (int i=0; i<m; ++i) {
            vector<int> v(n);
            for (int j=0; j<n; ++j) scanf("%d", &v[j]);
            for (int j=0; j<n; ++j) if (v[j]) {
                for (int k=0; k<n; ++k) if (k!=j) {
                    if (v[k]>v[j]||!v[k]) ++c[j][k];
                }
            }
        }
        for (int i=0; i<n; ++i) {
            for (int j=0; j<n; ++j) {
                if (c[i][j]>c[j][i]) g[i][j] = c[i][j];
            }
        }
        for (int k=0; k<n; ++k) {
            for (int i=0; i<n; ++i) {
                for (int j=0; j<n; ++j) {
                    g[i][j] = max(g[i][j], min(g[i][k],g[k][j]));
                }
            }
        }
        vector<int> v;
        for (int i=0; i<n; ++i) {
            int ok = 1;
            for (int j=0; j<n; ++j) if (g[i][j]<g[j][i]) ok = 0;
            if (ok) v.push_back(i+1);
        }
        for (int i=0; i<v.size(); ++i) printf("%d%c", v[i], " 
    "[i+1==v.size()]);
    }
    View Code

    C

    D 签到

    E

    考虑二分答案, 先考虑左右两段的位置, 最后再考虑中间段

    V1显然取越大越好, 但V2不一定是越小越好, 因为可能导致中间段L1的值超过L2

    可以直接维护每个位置$L2$最大取值, 然后枚举中间段判断即可

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 3e5+10;
    int n;
    pair<int,int> a[N];
    int chk(int x) {
        int pos = -1;
        for (int i=1; i<=n; ++i) {
            if (a[i].second>x) {
                pos = i;
                break;
            }
        }
        if (pos==-1) return 1;
        int ma = a[n].second, mi = a[n].second, posr = -1;
        vector<int64_t> p(n+1);
        for (int i=n; i; --i) { 
            ma = max(ma, a[i].second);
            mi = min(mi, a[i].second);
            int64_t lx = max(0, ma-x), rx = (int64_t)mi+x;
            if (lx<=rx) p[i] = rx;
            else {
                posr = i;
                break;
            }
        }
        if (posr==-1) return 1;
        if (pos>posr) return 1;
        ma = a[pos].second, mi = a[pos].second;
        for (int i=pos; i<=n; ++i) {
            ma = max(ma, a[i].second);
            mi = min(mi, a[i].second);
            int64_t lx = max(0, ma-x), rx = (int64_t)mi+x;
            if (lx<=rx) {
                if (i==n) return 1;
                if (i>=posr&&lx<=p[i+1]) return 1;
            }
        }
        return 0;
    }
    
    int main() {
        scanf("%d", &n);
        int l = 0, r = 2e9+10, ans = -1;
        for (int i=1; i<=n; ++i) { 
            scanf("%d%d", &a[i].first, &a[i].second), a[i].second *= 2;
            if (a[i].first==0) {
                l = max(l, a[i].second);
            }
        }
        sort(a+1,a+1+n);
        while (l<=r) {
            int mid = ((int64_t)l+r)/2;
            if (chk(mid)) ans = mid, r = mid-1;
            else l = mid+1;
        }
        assert(ans!=-1);
        printf("%.1lf
    ", ans/2.);
    }
    View Code

    F

    模拟

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 1e5+10;
    int nxt[N],ok=1;
    string s;
    int isop(char x) {
        return x=='+'||x=='-'||x=='*'||x=='/'||x=='%';
    }
    pair<int,int> dfs(int l, int r) {
        if (l>r) return {0,0};
        if (s[l]=='(') {
            int t = dfs(l+1,nxt[l]-1).first;
            if (t==0) {
                puts("error");
                exit(0);
            }
            if (t==1) ok = 0;
            if (t>2) ok = 0;
            int pos = nxt[l]+1;
            if (pos>r) return {1,1};
            if (!isop(s[pos])) {
                puts("error");
                exit(0);
            }
            t = dfs(pos+1,r).first;
            if (t==0) {
                puts("error");
                exit(0);
            }
            int ans = t+1;
            if (ans>=3) ok = 0;
            return {ans,0};
        }
        if (!isalpha(s[l])) {
            puts("error");
            exit(0);
        }
        int pos = l+1;
        if (pos>r) return {1,0};
        if (!isop(s[pos])) {
            puts("error");
            exit(0);
        }
        int t = dfs(pos+1,r).first;
        if (t==0) {
            puts("error");
            exit(0);
        }
        int ans = t+1;
        if (ans>=3) ok = 0;
        return {ans,0};
    }
    int main() {
        getline(cin,s);
        string tmp;
        for (auto &c:s) if (c!=' ') tmp.push_back(c);
        s = tmp;
        vector<int> sta;
        for (int i=0; i<s.size(); ++i) {
            if (s[i]=='(') sta.push_back(i);
            else if (s[i]==')') {
                if (sta.empty()) return puts("error"),0;
                nxt[sta.back()] = i;
                sta.pop_back();
            }
        }
        if (sta.size()) return puts("error"),0;
        if (dfs(0,s.size()-1).second) ok = 0;
        puts(ok?"proper":"improper");
    }
    View Code

    G

    H

    I

    状态(u,v)表示从一个人类点走到u,从一个非人类点走到v, 暴力bfs即可

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 1e3+10;
    int n, w, c, h, m;
    int a[N], b[N], vis[N][N];
    vector<pair<int,int>> g[N];
    int main() {
        scanf("%d%d%d%d%d", &n, &w, &c, &h, &m);
        for (int i=0; i<h; ++i) {
            int x;
            scanf("%d", &x);
            a[x] = 1;
        }
        for (int i=0; i<m; ++i) {
            int x;
            scanf("%d", &x);
            b[x] = 1;
        }
        for (int i=0; i<w; ++i) {
            int u, v, w;
            scanf("%d%d%d", &u, &w, &v);
            g[u].push_back({v,w});
        }
        queue<pair<int,int>> q;
        for (int i=0; i<n; ++i) if (a[i]) {
            for (int j=0; j<n; ++j) if (!a[j]) {
                q.push({i,j});
                vis[i][j] = 1;
            }
        }
        while (q.size()) {
            auto u = q.front(); q.pop();
            if (b[u.first]&&b[u.second]) return puts("YES"),0;
            for (auto x:g[u.first]) for (auto y:g[u.second]) {
                if (x.second==y.second) {
                    if (!vis[x.first][y.first]) {
                        q.push({x.first,y.first});
                        vis[x.first][y.first] = 1;
                    }
                }
            }
        }
        puts("NO");
    }
    View Code

    K

    2sat板子

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 1e6+10;
    struct TwoSAT {
        int n;
        vector<int> g[N<<1];
        bool mark[N<<1];
        int s[N<<1], c;
        bool dfs(int x) {
            if (mark[x^1]) return 0;
            if (mark[x]) return 1;
            mark[x] = 1;
            s[c++] = x;
            for (int y:g[x]) if (!dfs(y)) return 0;
            return 1;
        }
        //x==xv or y==yv
        void add(int x, int xv, int y, int yv) {
            x = x*2+xv;
            y = y*2+yv;
            g[x^1].push_back(y);
            g[y^1].push_back(x);
        }
        bool solve() {
            for (int i=0; i<n*2; i+=2) {
                if (!mark[i]&&!mark[i+1]) {
                    c = 0;
                    if (!dfs(i)) {
                        while (c) mark[s[--c]]=0;
                        if (!dfs(i+1)) return 0;
                    }
                }
            }
            return 1;
        }
    } solver;
    
    int main() {
        int n, k;
        scanf("%d%d", &n, &k);
        solver.n = n;
        for (int i=1; i<=k; ++i) {
            int a, b, c;
            char x, y, z;
            scanf(" %d %c %d %c %d %c", &a, &x, &b, &y, &c, &z);
            solver.add(a,x=='B',b,y=='B');
            solver.add(a,x=='B',c,z=='B');
            solver.add(b,y=='B',c,z=='B');
        }
        if (!solver.solve()) return puts("-1"), 0;
        for (int i=1; i<=n; ++i) printf("%c", solver.mark[i<<1]?'R':'B');
        puts("");
    }
    View Code

    L 签到

    2012-2013 ACM-ICPC Pacific Northwest Regional Contest

    A 签到

    B 签到

    C

    D 直接用pair排序即可

    #include <bits/stdc++.h>
    using namespace std;
    int main() {
        int n, w, h;
        while (scanf("%d%d%d", &n, &w, &h),n) {
            vector<pair<int,int>> a(n);
            for (int i=0; i<n; ++i) scanf("%d%d", &a[i].first, &a[i].second);
            sort(a.begin(),a.end());
            for (int i=0; i<n/2; ++i) printf("%d %d
    ", a[i].first, a[i].second);
        }
    }
    View Code

    3sat问题, 直接暴力即可

    #include <bits/stdc++.h>
    using namespace std;
    int rings, runes;
    int64_t A[111],B[111],C[111];
    int main() {
        int t;
        scanf("%d", &t);
        while (t--) {
            scanf("%d%d", &rings, &runes);
            int err = 0;
            for (int i=0; i<runes; ++i) {
                int64_t a, b, c;
                scanf("%lld%lld%lld%*d", &a, &b, &c);
                if (!a||!b||!c) err = 1;
                if (err==1) continue;
                if (abs(a)>rings||abs(b)>rings||abs(c)>rings) err = 2;
                if (err==2) continue;
                if (abs(a)==abs(b)||abs(a)==abs(c)||abs(b)==abs(c)) err = 3;
                if (err==3) continue;
                A[i] = a, B[i] = b, C[i] = c;
            }
            if (err) {
                if (err==1) puts("INVALID: NULL RING");
                if (err==2) puts("INVALID: RING MISSING");
                if (err==3) puts("INVALID: RUNE CONTAINS A REPEATED RING");
                continue;
            }
            int ok = 0;
            for (int i=0; i<(1<<rings); ++i) {
                for (int j=0; j<=runes; ++j) {
                    if (j==runes) ok = 1;
                    if ((A[j]>0)==((i>>abs(A[j])-1)&1)) continue;
                    if ((B[j]>0)==((i>>abs(B[j])-1)&1)) continue;
                    if ((C[j]>0)==((i>>abs(C[j])-1)&1)) continue;
                    break;
                }
                if (ok) break;
            }
            if (ok) puts("RUNES SATISFIED!");
            else puts("RUNES UNSATISFIABLE! TRY ANOTHER GATE!");
        }
    }
    View Code

    F

    G 简单数位dp

    #include <bits/stdc++.h>
    using namespace std;
    int64_t dp[100][3][2];
    int main() {
        int64_t n;
        while (~scanf("%lld", &n)) {
            memset(dp,0,sizeof dp);
            dp[61][0][1] = 1;
            int64_t ans = -1;
            for (int i=60; i>=0; --i) for (int d=0; d<3; ++d) {
                for (int lim=0; lim<=1; ++lim) {
                    int64_t &res = dp[i+1][d][lim];
                    if (res==0) continue;
                    int mx = lim?n>>i&1:1;
                    for (int z=0; z<=mx; ++z) { 
                        if (i==0) { 
                            if ((d+z)%3==0) ans += res;
                        }
                        else dp[i][(d+z)%3][lim&&z==mx] += res;
                    }
                }
            }
            printf("Day %lld: Level = %lld
    ", n, ans);
        }
    }
    View Code

    H 树状数组求逆序对

    #include <bits/stdc++.h>
    using namespace std;
    int c[100010], a[100010];
    map<string,int> f;
    int main() {
        int n;
        ios::sync_with_stdio(0);
        cin.tie(0);
        while (cin>>n, n) {
            f.clear();
            for (int i=1; i<=n; ++i) {
                string s;
                cin>>s;
                f[s] = i;
            }
            for (int i=1; i<=n; ++i) {
                string s;
                cin>>s;
                a[i] = f[s];
            }
            int64_t ans = 0;
            for (int i=1; i<=n; ++i) c[i] = 0;
            for (int i=1; i<=n; ++i) {
                for (int j=a[i]; j<=n; j+=j&-j) ans += c[j];
                for (int j=a[i]; j; j^=j&-j) ++c[j];
            }
            printf("%lld
    ", ans);
        }
    }
    View Code

    J

    K 每个字符'I', 建个点限流, 跑最大流

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 1e6+10, S = N-2, T = N-1, INF = 0x3f3f3f3f;
    int n, m;
    struct edge {
        int to,w,next;
        edge(int to=0,int w=0,int next=0):to(to),w(w),next(next){}
    } e[N];
    int head[N], dep[N], vis[N], cur[N], cnt;
    queue<int> Q;
    int bfs() {
        for (int i=1; i<=2*n*m; ++i) dep[i]=INF,vis[i]=0,cur[i]=head[i];
        dep[S]=INF,vis[S]=0,cur[S]=head[S];
        dep[T]=INF,vis[T]=0,cur[T]=head[T];
        dep[S]=0,Q.push(S);
        while (Q.size()) {
            int u = Q.front(); Q.pop();
            for (int i=head[u]; i; i=e[i].next) {
                if (dep[e[i].to]>dep[u]+1&&e[i].w) {
                    dep[e[i].to]=dep[u]+1;
                    Q.push(e[i].to);
                }
            }
        }
        return dep[T]!=INF;
    }
    int dfs(int x, int w) {
        if (x==T) return w;
        int used = 0;
        for (int i=cur[x]; i; i=e[i].next) {
            cur[x] = i;
            if (dep[e[i].to]==dep[x]+1&&e[i].w) {
                int f = dfs(e[i].to,min(w-used,e[i].w));
                if (f) used+=f,e[i].w-=f,e[i^1].w+=f;
                if (used==w) break;
            }
        }
        return used;
    }
    int dinic() {
        int ans = 0;
        while (bfs()) ans+=dfs(S,INF);
        return ans;
    }
    void add(int u, int v, int w) {
        e[++cnt] = edge(v,w,head[u]);
        head[u] = cnt;
        e[++cnt] = edge(u,0,head[v]);
        head[v] = cnt;
    }
    string s[50];
    const int dx[]={0,0,-1,1};
    const int dy[]={-1,1,0,0};
    int main() {
        while (1) {
            string tmp;
            n = 0;
            while (1) {
                if (!getline(cin,tmp)) break;
                if (tmp.size()) s[n++] = tmp;
                else break;
            }
            m = s[0].size();
            if (!n) break;
            cnt = 1;
            for (int i=1; i<=2*n*m; ++i) head[i] = 0;
            head[S] = head[T] = 0;
            for (int i=0; i<n; ++i) {
                for (int j=0; j<m; ++j) {
                    int id = i*m+j+1;
                    if (s[i][j]=='W') add(S,id,1);
                    else if (s[i][j]=='N') add(id,T,1);
                    else if (s[i][j]=='I') {
                        add(id,id+n*m,1);
                        for (int k=0; k<4; ++k) {
                            int x=i+dx[k], y=j+dy[k];
                            if (x<0||y<0||x>=n||y>=m) continue;
                            if (s[x][y]=='W') add(x*m+y+1,id,1);
                            if (s[x][y]=='N') add(id+n*m,x*m+y+1,1);
                        }
                    }
                }
            }
            printf("%d
    ", dinic());
        }
    }
    View Code

    L 模拟, 读入很坑, 数据有空行

    #include <bits/stdc++.h>
    using namespace std;
    char get(char x) {
        if (x=='a') return 'e';
        if (x=='i') return 'o';
        if (x=='y') return 'u';
        if (x=='e') return 'a';
        if (x=='o') return 'i';
        if (x=='u') return 'y';
        string t = "bkxznhdcwgpvjqtsrlmf";
        int m = t.size();
        for (int i=0; i<m; ++i) if (x==t[i]) {
            return t[(i+10)%m];
        }
        throw;
    }
    int main() {
        while (1) { 
            char c;
            string s;
            do { 
                c=getchar();
                if (32<=c&&c<=126) s.push_back(c); 
            } while (c!=EOF&&c!='
    ');
            if (c==EOF) break;
            for (int i=0; i<s.size(); ++i) {
                if (isupper(s[i])) s[i] = get(s[i]-'A'+'a')-'a'+'A';
                else if (islower(s[i])) s[i] = get(s[i]);
            }
            cout<<s<<'
    ';
        }
    }
    View Code

    2017-2018 ACM-ICPC Latin American Regional Programming Contest

    A

    B 跑暴力找规律, 发现答案只跟中间非元音个数有关

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 1e5+10;
    char s[N];
    int chk(char x) {
        return x=='a'||x=='e'||x=='i'||x=='o'||x=='u';
    }
    int main() {
        scanf("%s", s+1);
        int cnt = 0, n = strlen(s+1);
        for (int i=1; i<=n; ++i) cnt += chk(s[i]);
        if (!cnt) return puts("1"), 0;
        if (!chk(s[1])) return puts("0"),0;
        cnt = (cnt+1)/2;
        int now = 0;
        for (int i=1; i<=n; ++i) {
            if (chk(s[i])&&++now==cnt) {
                int j = i;
                while (j+1<=n&&!chk(s[j+1])) ++j;
                printf("%d
    ", j-i+1);
                return 0;
            }
        }
    }
    View Code

    C k很小, 直接暴力

    #include <bits/stdc++.h>
    using namespace std;
    int n, k, a[1010];
    map<int,int> s;
    void add(int x) {
        ++s[x];
    }
    void del(int x) {
        if (!--s[x]) s.erase(x);
    }
    int main() {
        scanf("%d%d", &k, &n);
        for (int i=0; i<n; ++i) {
            int t;
            scanf("%d", &t);
            ++a[t];
        }
        for (int i=1; i<=k; ++i) add(a[i]);
        for (int i=1; i<=k; ++i) {
            del(a[i]);
            ++a[i];
            add(a[i]);
            if (s.size()==1) printf("+%d
    ", i),exit(0);
            del(a[i]);
            a[i] -= 2;
            add(a[i]);
            if (s.size()==1) printf("-%d
    ", i),exit(0);
            del(a[i]);
            ++a[i];
            add(a[i]);
        }
        for (int i=1; i<=k; ++i) {
            for (int j=1; j<=k; ++j) {
                del(a[i]),del(a[j]);
                --a[i],++a[j];
                add(a[i]),add(a[j]);
                if (s.size()==1) printf("-%d +%d
    ", i, j),exit(0);
                del(a[i]),del(a[j]);
                ++a[i],--a[j];
                add(a[i]),add(a[j]);
            }
        }
        puts("*");
    }
    View Code

    D 数据随机, 同色区间非常少, 直接珂朵莉树

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 1e5+10;
    int l, c, n, vis[N];
    struct v:map<int,pair<int,int>> {
        auto split(int x) {
            auto p = lower_bound(x);
            if (p!=end()&&p->first==x) return p;
            --p;
            if (x>p->second.first) return end();
            auto t = *p;
            erase(p);
            emplace(t.first,pair<int,int>(x-1,t.second.second));
            return emplace(x,t.second).first;
        }
    } mp;
    int main() {
        scanf("%d%d%d", &l, &c, &n);
        mp[0] = {l-1, 1};
        for (int i=1; i<=n; ++i) {
            int p, x, a, b, s = 0;
            scanf("%d%d%d%d", &p, &x, &a, &b);
            for (auto &t:mp) if (t.second.second==p) s += t.second.first-t.first+1;
            int m1 = (a+(int64_t)s*s)%l;
            int m2 = (a+(int64_t)(s+b)*(s+b))%l;
            if (m1>m2) swap(m1, m2);
            auto R = mp.split(m2+1), L = mp.split(m1);
            mp.erase(L,R);
            mp[m1] = {m2,x};
        }
        int ans = 0;
        for (auto &t:mp) ans = max(ans, vis[t.second.second]+=t.second.first-t.first+1);
        printf("%d
    ", ans);
    }
    View Code

    E dp求出后缀模k是否能为x, 然后从前往后贪心

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 1111;
    char s[N];
    int k,pw[N],dp[N][N];
    
    int main() {
        scanf("%s%d", s+1, &k);
        int n = strlen(s+1);
        pw[0] = 1;
        for (int i=1; i<=n; ++i) pw[i] = pw[i-1]*10%k;
        dp[n+1][0] = 1;
        for (int i=n; i; --i) {
            for (int j=0; j<k; ++j) if (dp[i+1][j]) {
                int l = i==1, r = 9;
                if (s[i]!='?') l = s[i]-'0', r = s[i]-'0';
                for (int x=l; x<=r; ++x) dp[i][(j+x*pw[n-i])%k] = 1;
            }
        }
        if (!dp[1][0]) return puts("*"), 0;
        int now = 0;
        for (int i=1; i<=n; ++i) {
            int l = i==1, r = 9;
            if (s[i]!='?') l = s[i]-'0', r = s[i]-'0';
            for (int x=l; x<=r; ++x) {
                if (dp[i+1][((now-x*pw[n-i])%k+k)%k]) {
                    now = ((now-x*pw[n-i])%k+k)%k;
                    s[i] = x+'0';
                    break;
                }
            }
        }
        puts(s+1);
    }
    View Code

    F 一维排序, 另一维树状数组优化

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 1e5+10;
    int n, b[N];
    struct node {
        int x,y;
        int64_t w;
        bool operator < (const node &rhs) const { 
            if (x!=rhs.x) return x<rhs.x;
            return y<rhs.y;
        }
    } a[N];
    int64_t c[N],dp[N];
    
    int main() {
        scanf("%d", &n);
        int cnt = 0;
        for (int i=1; i<=n; ++i) { 
            scanf("%d%d%lld", &a[i].x, &a[i].y, &a[i].w);
            b[++cnt] = a[i].y;
        }
        sort(b+1, b+1+cnt);
        cnt = unique(b+1, b+1+cnt)-b-1;
        for (int i=1; i<=n; ++i) {
            a[i].y = lower_bound(b+1,b+1+cnt,a[i].y)-b;
        }
        sort(a+1, a+1+n);
        int m = 1;
        for (int i=2; i<=n; ++i) {
            if (a[i].x==a[m].x&&a[i].y==a[m].y) a[m].w += a[i].w;
            else a[++m] = a[i];
        }
        n = m;
        int64_t ans = 0;
        for (int i=1; i<=n; ++i) {
            int j = i;
            while (j+1<=n&&a[j+1].x==a[i].x) ++j;
            for (int k=i; k<=j; ++k) {
                dp[k] = a[k].w;
                for (int x=a[k].y-1; x; x^=x&-x) dp[k] = max(dp[k], c[x]+a[k].w);
            }
            for (int k=i; k<=j; ++k) {
                for (int x=a[k].y; x<=cnt; x+=x&-x) c[x] = max(c[x], dp[k]);
            }
            i = j;
        }
        printf("%lld
    ", *max_element(dp+1,dp+1+n));
    }
    View Code

    G dp[x][0/1][0/1][0/1]表示不考虑故障点返回值, 考虑故障点返回值, 是否有故障点的方案数

    暴力转移即可

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 1e5+10, P = 1e9+7;
    int dp[N][8];
    int n, L[N], R[N], val[N];
    int nand(int a, int b, int v) {
        int ans = 3^(a&b), f = a>>2|b>>2;
        if (v==0) ans &= 1, f = 1;
        if (v==1) ans |= 2, f = 1;
        if (!f) {
            if (ans&1) ans |= 2;
            else ans &= 1;
        }
        return ans|f<<2;
    }
    void add(int &a, int64_t b) {a=(a+b)%P;}
    void dfs(int x) {
        if (!x) return;
        dfs(L[x]),dfs(R[x]);
        for (int u=0; u<8; ++u) for (int v=0; v<8; ++v) {
            add(dp[x][nand(u,v,val[x])],(int64_t)dp[L[x]][u]*dp[R[x]][v]);
        }
    }
    int main() {
        dp[0][0] = dp[0][3] = 1;
        scanf("%d", &n);
        for (int i=1; i<=n; ++i) scanf("%d%d%d", &L[i], &R[i], &val[i]);
        dfs(1);
        printf("%d
    ", (dp[1][5]+dp[1][6])%P);
    }
    View Code

    H 签到

    I (kruskal)重构树求瓶颈路

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 2e5+10;
    int n, r, fa[N], val[N], sz[N], dep[N];
    int son[N], top[N];
    map<pair<int,int>,int> E;
    struct edge {
        int u, v, w;
        bool operator < (const edge &rhs) const {
            return w<rhs.w;
        }
    } e[N];
    vector<int> g[N];
    int Find(int x) {return fa[x]?fa[x]=Find(fa[x]):x;}
    void dfs(int x, int f, int d) {
        sz[x] = 1, fa[x] = f, dep[x] = d;
        for (int y:g[x]) if (y!=f) {
            dfs(y,x,d+1);
            sz[x] += sz[y];
            if (sz[y]>sz[son[x]]) son[x] = y;
        }
    }
    void dfs(int x, int tf) {
        top[x] = tf;
        if (son[x]) dfs(son[x],tf);
        for (int y:g[x]) if (!top[y]) dfs(y,y);
    }
    int lca(int x, int y) {
        while (top[x]!=top[y]) {
            if (dep[top[x]]<dep[top[y]]) y = fa[y];
            else x = fa[x];
        }
        return dep[x]<dep[y]?x:y;
    }
    int main() {
        scanf("%d%d", &n, &r);
        for (int i=1; i<=r; ++i) {
            int u, v, w;
            scanf("%d%d%d", &u, &v, &w);
            E[{u,v}] = w;
            e[i] = {u,v,w};
        }
        sort(e+1,e+1+r);
        int tot = n, sum = 0;
        for (int i=1; i<=r; ++i) {
            int u = Find(e[i].u), v = Find(e[i].v);
            if (u!=v) {
                ++tot;
                fa[u] = fa[v] = tot;
                g[tot].push_back(u);
                g[tot].push_back(v);
                sum += val[tot] = e[i].w;
            }
        }
        dfs(tot,0,0),dfs(tot,tot);
        int q;
        scanf("%d", &q);
        while (q--) {
            int u, v;
            scanf("%d%d", &u, &v);
            printf("%d
    ", sum-val[lca(u,v)]+E[{u,v}]);
        }
    }
    View Code

    J 暴力检验因子

    #include <bits/stdc++.h>
    using namespace std;
    int cnt[100010],vis[100010];
    char s[100010];
    int main() {
        scanf("%s", s);
        int n = strlen(s);
        auto gao = [&](int x) {
            for (int i=0; i<x; ++i) cnt[i] = 0;
            for (int i=0; i<n; ++i) if (s[i]=='P') ++cnt[i%x];
            for (int i=0; i<x; ++i) if (cnt[i]==0) vis[x] = 1;
        };
        for (int i=1; i<=n; ++i) if (n%i==0) gao(i),gao(n/i);
        int ans = 0;
        for (int i=1; i<n; ++i) if (vis[i]) {
            ++ans;
            for (int j=i; j<n; j+=i) vis[j] = 1;
        }
        printf("%d
    ", ans);
    }
    View Code

    K 'o'向外提供1个度数, '-'提供两个度数, 建二分图看是否满流即可

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 1e6+10, S = N-2, T = N-1, INF = 0x3f3f3f3f;
    int n, m;
    struct edge {
        int to,w,next;
        edge(int to=0,int w=0,int next=0):to(to),w(w),next(next){}
    } e[N];
    int head[N], dep[N], vis[N], cur[N], cnt=1;
    queue<int> Q;
    int bfs() {
        for (int i=1;i<=n*m;++i) dep[i]=INF,vis[i]=0,cur[i]=head[i];
        dep[S]=INF,vis[S]=0,cur[S]=head[S];
        dep[T]=INF,vis[T]=0,cur[T]=head[T];
        dep[S]=0,Q.push(S);
        while (Q.size()) {
            int u = Q.front(); Q.pop();
            for (int i=head[u]; i; i=e[i].next) {
                if (dep[e[i].to]>dep[u]+1&&e[i].w) {
                    dep[e[i].to]=dep[u]+1;
                    Q.push(e[i].to);
                }
            }
        }
        return dep[T]!=INF;
    }
    int dfs(int x, int w) {
        if (x==T) return w;
        int used = 0;
        for (int i=cur[x]; i; i=e[i].next) {
            cur[x] = i;
            if (dep[e[i].to]==dep[x]+1&&e[i].w) {
                int f = dfs(e[i].to,min(w-used,e[i].w));
                if (f) used+=f,e[i].w-=f,e[i^1].w+=f;
                if (used==w) break;
            }
        }
        return used;
    }
    int dinic() {
        int ans = 0;
        while (bfs()) ans+=dfs(S,INF);
        return ans;
    }
    void add(int u, int v, int w) {
        e[++cnt] = edge(v,w,head[u]);
        head[u] = cnt;
        e[++cnt] = edge(u,0,head[v]);
        head[v] = cnt;
    }
    char s[22][22];
    const int dx[]={0,0,-1,1};
    const int dy[]={-1,1,0,0};
    int main() {
        scanf("%d%d", &n, &m);
        for (int i=0; i<n; ++i) scanf("%s", s[i]);
        int sum = 0;
        for (int i=0; i<n; ++i) {
            for (int j=0; j<m; ++j) {
                int w = s[i][j]=='o'?1:2;
                sum += w;
                if (i+j&1) add(S,i*m+j+1,w);
                else add(i*m+j+1,T,w);
                for (int k=0; k<4; ++k) {
                    int ii=i+dx[k],jj=j+dy[k];
                    if (ii<0||jj<0||ii>=n||jj>=m) continue;
                    if (i+j&1) add(i*m+j+1,ii*m+jj+1,w);
                    else add(ii*m+jj+1,i*m+j+1,w);
                }
            }
        }
        puts(dinic()*2==sum?"Y":"N");
    }
    View Code

    L

    M 预处理SA, 每一步贪心取后缀最小的

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 1e6+10, P = 1e9+7;
    struct SA {
        int n,c[N],rk[N],h[N],sa[N],a[N];
        void build(int m) {
            a[n+1] = rk[n+1] = h[n+1] = 0;
            int i,*x=rk,*y=h;
            for(i=1;i<=m;i++) c[i]=0;
            for(i=1;i<=n;i++) c[x[i]=a[i]]++;
            for(i=1;i<=m;i++) c[i]+=c[i-1];
            for(i=n;i;i--) sa[c[x[i]]--]=i;
            for(int k=1,p;k<=n;k<<=1) {
                p=0;
                for(i=n-k+1;i<=n;i++) y[++p]=i;
                for(i=1;i<=n;i++) if(sa[i]>k) y[++p]=sa[i]-k;
                for(i=1;i<=m;i++) c[i]=0;
                for(i=1;i<=n;i++) c[x[y[i]]]++;
                for(i=1;i<=m;i++) c[i]+=c[i-1];
                for(i=n;i;i--) sa[c[x[y[i]]]--]=y[i];
                swap(x,y); x[sa[1]]=1; p=1;
                for(i=2;i<=n;i++)
                    x[sa[i]]=(y[sa[i-1]]==y[sa[i]] && y[sa[i-1]+k]==y[sa[i]+k])?p:++p;
                if(p==n) break; m=p;
            }
            for(i=1;i<=n;i++) rk[sa[i]]=i;
            for(int i=1,j,k=0;i<=n;i++) if (rk[i]!=1) {
                if(k) k--;
                j=sa[rk[i]-1];
                while(a[i+k]==a[j+k]) k++;
                h[rk[i]] = k;
            }
        }
    } sa;
    int vis[N],pw[N];
    int main() {
        int n, cnt = 0;
        scanf("%d", &n);
        for (int i=0; i<n; ++i) {
            int k, x;
            scanf("%d", &k);
            vis[sa.n+1] = 1;
            cnt += k;
            while (k--) {
                scanf("%d", &x);
                sa.a[++sa.n] = x;
            }
            sa.a[++sa.n] = 500;
        }
        sa.build(500);
        priority_queue<int,vector<int>,greater<int>> q;
        for (int i=1; i<=sa.n; ++i) if (vis[i]) q.push(sa.rk[i]);
        int ans = 0;
        for (int i=cnt; i; --i) {
            int p = sa.sa[q.top()];
            q.pop();
            ans = (ans*365ll+sa.a[p])%P;
            if (p+1!=sa.n&&sa.a[p+1]!=500) q.push(sa.rk[p+1]);
        }
        ans = ans*365ll%P;
        printf("%d
    ", ans);
    }
    View Code
  • 相关阅读:
    前端安全【面试】
    防xss攻击
    前端工程化
    前端项目构建——运维
    react入门
    OpenGL Windows 窗口程序环境搭建
    Django 列的自定义显示
    设计模式之 SOA面向服务的体系
    设计模式之Builder建造者模式 代码初见
    设计模式之Factory工厂模式的好处
  • 原文地址:https://www.cnblogs.com/fs-es/p/13756769.html
Copyright © 2020-2023  润新知