• 2019 Multi-University Training Contest 1


    2019 Multi-University Training Contest 1

    A

    题意:给长度为 n 的序列染 4 种颜色,有 m 个限制,每个形如 ([l_i,r_i]) 区间内恰好有 (x_i) 种颜色。问合法方案数。 (n,m le 100)

    key:dp

    其实思路很简单,主要问题在于如何记录状态才能表示出这个限制。考虑一个区间内的颜色种数怎么表示:首先这个区间内必须已经染色,其次每种颜色只贡献 1 。后者可以用该颜色最后一次出现位置表示。

    所以 (f_{i,j,k,l}) 表示四种颜色最后一次出现的位置。为了表示方便可以给他们排个序:为四种颜色最后一次出现位置排序后分比为 (i, j, k, l, i<j<k<l) 。这样每次就可以检查以 l 为结尾的所有区间是否合法了。 复杂度 (O(n^4+n^3m))

    #include<bits/stdc++.h>
    using namespace std;
    
    typedef long long LL;
    typedef long double LD;
    typedef pair<int,int> pii;
    typedef pair<LL,int> pli;
    const int SZ = 8e6 + 10;
    const int INF = 1e9 + 10;
    const int mod = 998244353;
    const LD eps = 1e-8;
    
    LL read() {
        LL n = 0;
        char a = getchar();
        bool flag = 0;
        while(a > '9' || a < '0') { if(a == '-') flag = 1; a = getchar(); }
        while(a <= '9' && a >= '0') { n = n * 10 + a - '0',a = getchar(); }
    	if(flag) n = -n;
    	return n;
    }
    
    int f[2][110][110][110];
    vector<pii> q[110];
    
    bool check(int i,int j,int k,int t) {
        for(pii p : q[t]) {
            int l = p.first,x = p.second;
            //cout << l << " " << x << endl;
            if(l<=i) if(x!=4) return false; else continue;
            if(l<=j) if(x!=3) return false; else continue;
            if(l<=k) if(x!=2) return false; else continue;
            if(l<=t) if(x!=1) return false; else continue;
        }
        return true;
    }
    
    int main() {
        int T = read();
        while(T --) {
            int n = read(),m = read();
            for(int i = 1;i <= n;i ++) q[i].clear();
            for(int i = 1;i <= m;i ++) {
                int l = read(),r = read(),x = read();
                q[r].push_back(make_pair(l,x));
            }
    
           // cout << check(0,1,2,3) << endl;
    
            memset(f,0,sizeof f);
            f[0][0][0][0] = 1;
            for(int t = 0,cur = 0;t < n;t ++,cur^=1) {
                for(int k = 0;k <= max(t-1,0);k ++)
                    for(int j = 0;j <= max(k-1,0);j ++)
                        for(int i = 0;i <= max(j-1,0);i ++)
                            f[cur^1][i][j][k] = 0;
                for(int k = 0;k <= max(t-1,0);k ++) {
                    for(int j = 0;j <= max(k-1,0);j ++) {
                        for(int i = 0;i <= max(j-1,0);i ++) {
                            if(check(i,j,k,t)) {
                       //         printf("(%d,%d,%d,%d) = %d
    ",i,j,k,t,f[cur][i][j][k]);
                                (f[cur^1][j][k][t] += f[cur][i][j][k]) %= mod;
                                (f[cur^1][i][k][t] += f[cur][i][j][k]) %= mod;
                                (f[cur^1][i][j][t] += f[cur][i][j][k]) %= mod;
                                (f[cur^1][i][j][k] += f[cur][i][j][k]) %= mod;
                            }
                          // else printf("!(%d,%d,%d,%d) = %d
    ",i,j,k,t,f[cur][i][j][k]);
                        }
                    }
                }
            }
            int ans = 0;
            for(int k = 0;k <= max(n-1,0);k ++) {
                for(int j = 0;j <= max(k-1,0);j ++) {
                    for(int i = 0;i <= max(j-1,0);i ++) {
                        if(check(i,j,k,n)) {
                            (ans += f[n&1][i][j][k]) %= mod;
                        }
                    }
                }
            }
            printf("%d
    ",ans);
        }
    }
    /**
    2
    4 1
    1 3 3
    */
    
    

    B

    题意:给一个序列,每次操作是尾部加入一个数、查询区间内的线性基。强制在线。 (n,m le 2*10^5)

    key:线性基

    CF1100F

    C

    D

    E

    #include<bits/stdc++.h>
    using namespace std;
    
    typedef long long LL;
    typedef long double LD;
    typedef pair<int,int> pii;
    typedef pair<LL,int> pli;
    const int SZ = 8e6 + 10;
    const int INF = 1e9 + 10;
    const int mod = 998244353;
    const LD eps = 1e-8;
    
    LL read() {
        LL n = 0;
        char a = getchar();
        bool flag = 0;
        while(a > '9' || a < '0') { if(a == '-') flag = 1; a = getchar(); }
        while(a <= '9' && a >= '0') { n = n * 10 + a - '0',a = getchar(); }
    	if(flag) n = -n;
    	return n;
    }
    
    vector<pii> g[SZ];
    
    LL dist[SZ];
    bool vis[SZ];
    int n,m;
    
    void dij(int s) {
        for(int i = 1;i <= n;i ++) dist[i] = 1e18,vis[i] = 0;
        dist[s] = 0;
        priority_queue<pli> q; q.push(make_pair(0,s));
        while(q.size()) {
            int u = q.top().second; q.pop();
            if(vis[u]) continue;
            vis[u] = 1;
            for(pii e : g[u]) {
                int v = e.first;
                if(dist[v] > dist[u] + e.second) {
                    dist[v] = dist[u] + e.second;
                    q.push(make_pair(-dist[v],v));
                }
            }
        }
    }
    
    
    struct edge {
        int f,t;
        LL d;
    }l[SZ];
    
    int head[SZ],nxt[SZ],tot = 1;
    
    void build(int f,int t,LL d) {
        l[++ tot] = (edge){f,t,d};
        nxt[tot] = head[f];
        head[f] = tot;
    }
    
    void insert(int f,int t,LL d) {
        build(f,t,d); build(t,f,0);
    }
    
    int deep[SZ];
    
    bool bfs(int s,int e) {
        for(int i = 1;i <= n;i ++) deep[i] = 0;
        deep[s] = 1;
        queue<int> q; q.push(s);
        while(q.size()) {
            int u = q.front(); q.pop();
            for(int i = head[u];i;i = nxt[i]) {
                int v = l[i].t;
                if(l[i].d && !deep[v]) {
                    deep[v] = deep[u] + 1;
                    q.push(v);
                    if(v == e) return true;
                }
            }
        }
        return false;
    }
    
    LL dfs(int u,LL flow,int e) {
        if(u == e || flow == 0) return flow;
        LL ans = flow;
        for(int i = head[u];i;i = nxt[i]) {
            int v = l[i].t;
            if(l[i].d && deep[v] == deep[u] + 1) {
                LL f = dfs(v,min(ans,l[i].d),e);
                if(f > 0) {
                    l[i].d -= f; l[i^1].d += f;
                    ans -= f;
                    if(ans == 0) break;
                }
                else deep[v] = 0;
            }
        }
        if(ans == flow) deep[u] = 0;
        return flow - ans;
    }
    
    LL dinic(int s,int e) {
        LL ans = 0;
        while(bfs(s,e)) {
            LL tmp = dfs(s,1e18,e);
            if(tmp == 0) break;
            ans += tmp;
        }
        return ans;
    }
    
    int ff[SZ],tt[SZ],dd[SZ];
    
    int main() {
        int T = read();
        while(T --) {
            n = read(),m = read();
            for(int i = 1;i <= n;i ++) g[i].clear(),head[i] = 0; tot = 1;
            for(int i = 1;i <= m;i ++) {
                int x = read(),y = read(),z = read();
                g[x].push_back(make_pair(y,z));
                ff[i] = x; tt[i] = y; dd[i] = z;
            }
            dij(1);
            if(dist[n] == 1e18) { puts("0"); continue; }
            for(int i = 1;i <= m;i ++) {
                if(dist[tt[i]] == dist[ff[i]] + dd[i]) {
                    insert(ff[i],tt[i],dd[i]);
                }
            }
            printf("%lld
    ",dinic(1,n));
        }
    }
    
    

    F

    题意:定义字符串生成操作:每次花费 p 的代价从尾部添加一个字符,或者 q 的代价从尾部添加一个当前字符串的子串。问生成给定字符串的最小代价。 (sum |S| le 5*10^6)

    key:sam

    显然是要 dp 的。容易得到一个 dp:添加一个字符,或者从之前某个位置。后者一定是当前字符串后缀是除去它的字符串的一个子串,具体地说,[j+1,i] 是 [1,j] 的一个子串。

    这样的 j 可能有很多。由于随着长度的增大,代价也越来越大,所以肯定是维护最小的 j 。考虑已经维护了一个 j ,当添加了一个字符时,要么不动,要么在 sam 上走。所以复杂度是 (O(n))

    G

    H

    I

    题意:给一个字符串,求长度恰好为 k ,每个字符出现次数在 ([l_i,r_i]) 内的字典序最小子序列。 (sum |S| le 3*10^5)

    key:贪心

    没有出现次数的限制就是之前计蒜之道的那个题,这个题的限制更强一些。

    考虑当前已经构造出了一个前缀,当前位置在 p ,考虑下一个字符应该选哪个。肯定是位置在 p 之后,字典序尽量小,并且选了它之后可以构造出符合要求的答案。

    由于同种字母肯定选出现位置最靠前的,所以待选位置至多只有 O(m) 个,而做一次check也是 O(m) 的。所以复杂度是 (O(nm^2)) ,其中 m 是字符集,即26

    #include<bits/stdc++.h>
    using namespace std;
    
    typedef long long LL;
    typedef long double LD;
    typedef pair<int,int> pii;
    typedef pair<LL,int> pli;
    const int SZ = 1e6 + 10;
    const int INF = 1e9 + 10;
    const int mod = 998244353;
    const LD eps = 1e-8;
    
    LL read() {
        LL n = 0;
        char a = getchar();
        bool flag = 0;
        while(a > '9' || a < '0') { if(a == '-') flag = 1; a = getchar(); }
        while(a <= '9' && a >= '0') { n = n * 10 + a - '0',a = getchar(); }
    	if(flag) n = -n;
    	return n;
    }
    
    char a[SZ];
    int k,n;
    char S[SZ];
    int top;
    int b[SZ][27],L[27],R[27];
    
    bool check(int a[27],int len,int b[27]) {
        int suml = 0,sumr = 0,sum = 0,s = 0;
        for(int i = 0;i < 26;i ++) {
            if(a[i] + b[i] < L[i]) return false;
            if(a[i] < L[i]) {
                sum += L[i] - a[i];
            }
            if(a[i] > R[i]) return false;
            s += min(b[i],R[i]-a[i]);
            suml += L[i];
            sumr += R[i];
        }
        if(s < len) return false;
        if(!(suml <= k && k <= sumr)) return false;
        if(sum > len) return false;
        return true;
    }
    
    char ans[SZ];
    
    int main() {
        while(~scanf("%s%d",a,&k)) {
            for(int i = 0;i < 26;i ++) L[i] = read(),R[i] = read();
            int n = strlen(a);
            memset(b[n],0,sizeof b[n]);
            vector<int> g[27];
            for(int i = 0;i < n;i ++) g[a[i]-'a'].push_back(i);
            for(int i = n-1;i >= 0;i --) {
                for(int j = 0;j < 26;j ++) b[i][j] = b[i+1][j];
                b[i][a[i]-'a'] ++;
            }
    
            if(!check(b[n],k,b[0])) { puts("-1"); continue; }
    
            int nowid = -1;
            int h[27] = {},num[27] = {};
            for(int len = 0;len < k;len ++) {
                for(int i = 0;i < 26;i ++) {
                    while(h[i] < g[i].size() && g[i][h[i]] <= nowid) h[i] ++;
                    if(h[i] == g[i].size()) continue;
                    int tmp[27] = {},id = g[i][h[i]];
                    for(int j = 0;j < 26;j ++) tmp[j] = num[j];
                    tmp[i] ++;
                    if(check(tmp,k-len-1,b[id+1])) {
                       // printf("%d %d %d
    ",len,i,id);
                        nowid = id;
                        ans[len] = i+'a';
                        num[i] ++;
                        break;
                    }
                }
            }
            for(int i = 0;i < k;i ++) printf("%c",ans[i]); puts("");
    
        }
    }
    

    J

    K

    题意:求

    [sum_{1le i le n} gcd(lfloor sqrt[3]{i} floor,i), n le 10^{21} ]

    key:推导

    主要是傻逼了,卡了奇怪的地方……

    枚举三次根号后的那个值,(lfloor sqrt[3]{n} floor) 特判。很容易推到

    [sum_{dle m-1} phi(d) sum_{ile (m-1)/d} lfloorfrac{(id+1)^3-1}{d} floor - lfloorfrac{(id)^3-1}{d} floor ]

    然后后面那个看起来很难搞的东西只要展开就好了………………然后就得到了一个 (O(n^{1/3})) 的做法

    #include<bits/stdc++.h>
    using namespace std;
    
    typedef __int128 LL;
    typedef long double LD;
    typedef pair<int,int> pii;
    typedef pair<LL,int> pli;
    const int SZ = 1e7 + 10;
    const int INF = 1e9 + 10;
    const int mod = 998244353;
    const LD eps = 1e-8;
    
    template <class T>
    void read(T &x) {
    	static char ch;static bool neg;
    	for(ch=neg=0;ch<'0' || '9'<ch;neg|=ch=='-',ch=getchar());
    	for(x=0;'0'<=ch && ch<='9';(x*=10)+=ch-'0',ch=getchar());
    	x=neg?-x:x;
    }
    
    LL sqrt3(LL n) {
        LL l = 0,r = 1e7+10;
        while(r-l>1) {
            LL mid = (l+r) / 2;
            if(mid * mid * mid <= n) l = mid;
            else r = mid;
        }
        return l;
    }
    
    int phi[SZ];
    LL sum[SZ];
    int pri[SZ / 10];
    bool vis[SZ];
    
    void shai(int n) {
        phi[1] = 1;
        int tot = 0;
        for(int i = 2;i <= n;i ++) {
            if(!vis[i]) pri[++ tot] = i,phi[i] = i-1;
            for(int j = 1,m;j <= tot && (m=i*pri[j]) <= n;j ++) {
                vis[m] = 1;
                if(i%pri[j] == 0) {
                    phi[m] = phi[i] * pri[j];
                    break;
                }
                else {
                    phi[m] = phi[i] * phi[pri[j]];
                }
            }
        }
    }
    
    LL ksm(LL a,LL b) {
        LL ans = 1;
        while(b) {
            if(b&1) ans = a * ans % mod;
            a = a *a % mod;
            b >>= 1;
        }
        return ans;
    }
    
    LL ni6 = ksm(6,mod-2);
    LL ni2 = ksm(2,mod-2);
    
    LL f2(LL n) {
        return n * (n+1) * (2*n+1) / 6;
    }
    
    LL f1(LL n) {
        return n * (n+1) / 2;
    }
    
    int baoli(int n) {
        int ans = 0;
        for(int i = 1;i <= n;i ++) {
            int x = (int)(pow(i,1.0/3)+1e-6);
            ans += __gcd(x,i);
        }
        return ans;
    }
    
    int main() {
        shai(1e7);
        int T; read(T);
        while(T --) {
            LL n; read(n);
            int m = sqrt3(n);
           // cout << m << endl;
            LL ans = 0;
            for(int d = 1;d < m;d ++) {
                LL tmp = 3*d*f2((m-1)/d)+ 3*f1((m-1)/d) + ((m-1)/d);
                ans += phi[d] * tmp;
            }
          //  cout << ans << endl;
            for(int d = 1;d <= m;d ++) {
                if(m % d == 0) {
                    ans += phi[d] * (((n/d) - ((LL)m*m*m-1)/d));
                }
            }
            ans %= mod;
            cout << ans << endl;
        }
    }
    
    

    L

    题意:给一个序列 a ,m 次操作,每次是求 $b_i = sum_{j = i - k cdot x} a_j mod 998244353 (0 leq x, 1leq j leq i) $ ,然后用 (b_i) 替换 (a_i) 。求最终序列。 (n le 10^5, m le 10^6, k in {1,2,3})

    key:ntt

    题解里写的非常清楚…………

    首先生成函数分析一下: (sum b_ix^i=(sum x^{ki})(sum a_ix^i)) ,所以满足交换律,顺序没有关系。问题在于求 ((sum x^{ki})^n=sum {n-1+i choose i} x^{ki}) 。然后就做完了。

    #include<bits/stdc++.h>
    using namespace std;
    
    typedef long long LL;
    typedef long double LD;
    typedef pair<int,int> pii;
    typedef pair<LL,int> pli;
    const int SZ = 2e6 + 10;
    const int INF = 1e9 + 10;
    const int mod = 998244353;
    const LD eps = 1e-8;
    
    LL read() {
        LL n = 0;
        char a = getchar();
        bool flag = 0;
        while(a > '9' || a < '0') { if(a == '-') flag = 1; a = getchar(); }
        while(a <= '9' && a >= '0') { n = n * 10 + a - '0',a = getchar(); }
    	if(flag) n = -n;
    	return n;
    }
    
    LL ksm(LL a,LL b) {
        LL ans = 1;
        while(b) {
            if(b&1) ans = a * ans % mod;
            a = a *a % mod;
            b >>= 1;
        }
        return ans;
    }
    
    struct NTTranform {
       	const int g = 3;
    	void Transform(int *a,int n,int opt) {
            for(int i = 0,j = 0;i < n;i ++) {
                if(i < j) swap(a[i],a[j]);
                for(int k = n >> 1;(j ^= k) < k;k >>= 1);
            }
            for(int l = 2;l <= n;l *= 2) {
                int m = l / 2;
                int wn = ksm(g,(mod-1)/l);
                if(opt == -1) wn = ksm(wn,mod - 2);
                for(int *p = a;p != a + n;p += l) {
                    for(int i = 0,w = 1;i < m;i ++,w=1ll*w*wn%mod) {
                        int t = 1ll * w * p[m + i] % mod;
                        p[m + i] = (p[i] - t + mod) % mod;
    					(p[i] += t) %= mod;
                    }
                }
            }
    	}
    	void dft(int *a,const int n) {
            Transform(a,n,1);
    	}
    	void idft(int *a,const int n) {
            Transform(a,n,-1);
            int t = ksm(n,mod - 2);
            for(int i = 0;i < n;i ++) a[i] = 1ll * a[i] * t % mod;
    	}
    }ntt;
    
    void multiply(int *a,int n,int *b,int m,int *ans) { /// need 4 times memory
        static int c1[SZ],c2[SZ];
    	int len = 1;
        while(len < n + m) len *= 2;
        for(int i = 0;i < len;i ++) c1[i] = c2[i] = 0;
        for(int i = 0;i < n;i ++) c1[i] = a[i];
        for(int i = 0;i < m;i ++) c2[i] = b[i];
        ntt.dft(c1,len); ntt.dft(c2,len);
        for(int i = 0;i < len;i ++) c1[i] = 1ll * c1[i] * c2[i] % mod;
        ntt.idft(c1,len);
        for(int i = 0;i < n + m - 1;i ++) ans[i] = (c1[i] + mod) % mod;
    }
    
    int a[SZ],b[SZ],c[SZ],fac[SZ],invfac[SZ];
    
    int C(int n,int m) {
        if(n<m) return 0;
        return 1ll * fac[n] * invfac[m] % mod * invfac[n-m] % mod;
    }
    
    int main() {
        fac[0] = 1;
        for(int i = 1;i <= 2e6;i ++) fac[i] = 1ll * i * fac[i-1] % mod;
        for(int i = 0;i <= 2e6;i ++) invfac[i] = ksm(fac[i],mod-2);
        int T = read();
        while(T --) {
            int n = read(),m = read();
            for(int i = 0;i < n;i ++) a[i] = read();
            int t[4] = {};
            for(int i = 1;i <= m;i ++) t[read()] ++;
    
            for(int k = 1;k <= 3;k ++) {
                if(t[k] == 0) continue;
                for(int i = 0;i < n;i ++) b[i] = c[i] = 0; b[0] = 1;
                int m = t[k];
                for(int i = 0;i < n;i += k) c[i] = C(i/k-1+m,i/k);
               // for(int i = 0;i < n;i ++) printf("%d ",b[i]); puts("");
               // for(int i = 0;i < n;i ++) printf("%d ",c[i]); puts("");
                multiply(b,n,c,n,b);
              //  for(int i = 0;i < n;i ++) printf("%d ",b[i]); puts("");
                multiply(b,n,a,n,a);
            }
            LL ans = 0;
           // for(int i = 0;i < n;i ++) printf("%d ",a[i]); puts("");
            for(int i = 0;i < n;i ++) {
                ans ^= (i+1ll) * a[i];
            }
            printf("%lld
    ",ans);
        }
    }
    
    /**
    2
    5 2
    3 2 2 4 1
    2 2
    */
    
    

    M

    题意:二维平面上给两类点,问是否有一条直线把两类点分开。 (n le 100)

    key:计算几何

    判断两个凸包是否相离。

  • 相关阅读:
    解决Original error: Could not proxy command to remote server. Original error: Error: socket hang up
    python各进制转换
    爬楼梯问题,yield学习总结
    微信开放平台API开发资料
    数据切分——Atlas读写分离Mysql集群的搭建
    svn SSL 错误:Key usage violation in certificate has been detected
    如何将一个空间里的内容全部复制到另一个空间,文件名不变
    SVN客户端安装 Linux
    Web端即时通讯技术盘点:短轮询、Comet、Websocket、SSE
    搭建SVN服务器
  • 原文地址:https://www.cnblogs.com/dqsssss/p/11235469.html
Copyright © 2020-2023  润新知