国庆训练

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

$n\le 2p$时, $r$只能为$0$

$n\le 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\n", 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\n", 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\n", 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]<<'\n';
    else if (mx<t) cout<<"+ "<<val2[x]<<"/ "<<sz[x]<<'\n';
    else {
        cout<<"- "<<val2[x]<<"/ "<<sz[x]<<'\n';
        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\n", x, y);
        else {
            int l, r;
            tie(l,r) = st.rmq(x, y-1);
            printf("%d %d\n", 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, " \n"[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\n", 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\n", i), 0;
    }
}
View Code

一个区间的gcd种类数是$O(\log A)$的, 先预处理出前缀$gcd$和后缀$gcd$, 再处理出每个数作为最大值时的范围. 最后暴力合并即可, 复杂度是$O(n\log^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\n", 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\n", 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\n", 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\n", 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\n", 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\n", 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\n", 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\n", 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\n", 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\n", 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\n", 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\n", ans);
}
View Code

K

L

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

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

如果存在一堆$x\ge 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\n", 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\n", 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\n", 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]<<'\n';
}
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\n",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\n", 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\n", 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\n", 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], " \n"[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\n", 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\n", ans);
    }
}
View Code

D

E 模拟

F 路径并板子

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

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

块边缘的点直接$rmq$查询距离即可, 复杂度$O(n\sqrt{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\n", 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-\sum\limits_{d|n,d\not =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\n", 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\n", f[0]);
}
View Code

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

#include <bits/stdc++.h>
int main() {
    int n;
    scanf("%d", &n);
    auto ask = [&](int x) {
        if (!n--) {
            printf("! %d\n", x);
            fflush(stdout);
            exit(0);
        }
        printf("%d\n", x);
        fflush(stdout);
        int t;
        scanf("%d", &t);
        if (!t) {
            printf("! %d\n", 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\n", 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\n", 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\n", 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\n", 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\n", 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\n", 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\n", 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\n", 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\times 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\n", (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], " \n"[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\n", 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\n", ans/2);
    else printf("%llu.5\n", 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\n", 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\n", 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\n", 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\times 1,1\times 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\n", 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\n", 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\n", 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\n", 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\n", 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\n", 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\n", 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\n", 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\n", 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\n", 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], " \n"[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\n", 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\n", 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", 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\n", 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\n", 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!='\n');
        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<<'\n';
    }
}
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\n", 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\n", i),exit(0);
        del(a[i]);
        a[i] -= 2;
        add(a[i]);
        if (s.size()==1) printf("-%d\n", 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\n", 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\n", 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\n", *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\n", (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\n", 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\n", 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\n", ans);
}
View Code
posted @ 2020-10-01 09:33  dz8gk0j  阅读(169)  评论(0)    收藏  举报