zoukankan      html  css  js  c++  java
  • 2021“MINIEYE杯”中国大学生算法设计超级联赛(8)

    2021“MINIEYE杯”中国大学生算法设计超级联赛(8)

    1003. Ink on paper

    • 题意

    滴墨水在纸上,墨水以每秒(0.5)向四面八方感染, 求多久后所有的墨水都能连接起来。

    • 思路

    (prim)求一遍最小生成树即可,套板子0.0。

    code :

    int n, m;
    int g[N][N], dist[N];
    //邻接矩阵存储所有边
    //dist存储其他点到S的距离
    bool st[N];
    int x[N], y[N];
    int get_(int a,int b) {
        return (x[a] - x[b]) * (x[a] - x[b]) + (y[a] - y[b]) * (y[a] - y[b]);
    }
    
    int prim() {
        //如果图不连通返回INF, 否则返回res
        memset(dist, INF, sizeof dist);
        int res = 0;
        memset(st,0,sizeof st);
        for(int i = 0; i < n; i++) {
            int t = -1;
            for(int j = 1; j <= n; j++) 
                if(!st[j] && (t == -1 || dist[t] > dist[j]))
                    t = j;
            //寻找离集合S最近的点        
            if(i && dist[t] == INF) return INF;
            //判断是否连通,有无最小生成树
    
            if(i) res = max(dist[t], res);
            st[t] = true;
            //更新最新S的权值和
    
            for(int j = 1; j <= n; j++) dist[j] = min(dist[j], g[t][j]);
        }
    
        return res;
    }
    
    void solve() {
        cin >> n;
        int u, v, w;
        for(int i = 1;i <= n;i ++) cin >> x[i] >> y[i];
        for(int i = 1; i <= n; i++)
            for(int j = i + 1; j <= n; j++){
                g[i][j] = g[j][i] = get_(i,j);
            }
        int t = prim();
        cout << t << endl;
    }
    

    1004. Counting Stars

    • 题意

    有一堆星星,长度为(n)的序列(a_i)记录了初始值,然后定义两种操作。

    1. (forall i in [l,r]), 所有的星星减少(a_i & (-a_i))
    2. (forall i in [l,r]), (a_i eq),所有的星星增加(2^k)(2^k <= a_i <= 2^{k + 1})
    • 思路

    操作(1)就是二进制中最低位从(1 o 0),操作 (2)就是二进制中最高位往后移一位。
    那么我们可以发现,最高位的变化和一些低位的变化是毫无关联的,那么我们怎么做到修改呢?
    首先最高位增加很简单,存所有的最高位每次(* 2)即可,然低位怎么考虑,我们可以发现低位的数值最多能降多少次?30次,最多30次后就和低位没关系了,发现这个性质就可以考虑低位直接单点修改即可,最总总复杂度最多(* 30),然后就码线段树就行了,注意处理降到 (0)的情况。

    code :

    const int mod = 998244353;
    const int N = 100100;
    #define mid (l + r >> 1)
    #define lsn (u << 1)
    #define rsn (u << 1 | 1)
    int n;
    int sum1[N << 2], sum2[N << 2], laz[N << 2], zero[N << 2];
    int a[N];
    
    void up(int u) {
        sum1[u] = (sum1[lsn] + sum1[rsn]) % mod;
        sum2[u] = (sum2[lsn] + sum2[rsn]) % mod;
        zero[u] = zero[lsn] & zero[rsn];
    }
    
    void cover1(int u, int la) {
        laz[u] = laz[u] * la % mod;
        sum1[u] = sum1[u] * la % mod;
    }
    
    void down(int u) {
        cover1(lsn,laz[u]);
        cover1(rsn,laz[u]);
        zero[lsn] |= zero[u];
        zero[rsn] |= zero[u];
        if(zero[lsn]) sum2[lsn] = 0;
        if(zero[rsn]) sum2[rsn] = 0;
        laz[u] = 1;
    }
    void build(int u = 1,int l = 1,int r = n) {
        laz[u] = 1, zero[u] = 0;
        if(l == r) {
            for(int i = 30;i >= 0;i --) {
                int j = a[l] >> i & 1;
                if(j) {
                    sum1[u] = 1 << i;
                    sum2[u] = a[l] - (1 << i);
                    break;
                }
            }
            return;
        }
        build(lsn,l,mid);
        build(rsn,mid + 1,r);
        up(u);
    }
    
    void update1(int L,int R,int u = 1,int l = 1,int r = n) {
        if(l == r) {
            if(sum2[u]) {
                sum2[u] -= lowbit(sum2[u]);
            }else {
                sum1[u] = 0;
                zero[u] = 1;
            }
            return;
        }
        down(u);
        if(L <= mid && !zero[lsn]) update1(L,R,lsn,l,mid);
        if(R > mid && !zero[rsn]) update1(L,R,rsn,mid + 1,r);
        up(u);
    }
    
    void update2(int L,int R,int u = 1,int l = 1,int r = n) {
        if(L <= l && R >= r) {
            sum1[u] = sum1[u] * 2 % mod;
            laz[u] = laz[u] * 2 % mod;
            return;
        }
        down(u);
        if(L <= mid) update2(L,R,lsn,l,mid);
        if(R > mid) update2(L,R,rsn,mid + 1,r);
        up(u);
    }
    
    int query(int L,int R,int u = 1,int l = 1,int r = n) {
        if(L <= l && R >= r) {
            return (sum1[u] + sum2[u]) % mod;
        }
        down(u);
        int res = 0;
        if(L <= mid && !zero[lsn]) res += query(L,R,lsn,l,mid) % mod;
        if(R > mid && !zero[rsn]) res += query(L,R,rsn,mid + 1,r) % mod;
        return res % mod;
    }
    
    void solve(){
        cin >> n;
        for(int i = 1;i <= n;i ++) cin >> a[i];
        build();
        int m;
        cin >> m;
        for(int i = 1;i <= m;i ++) {
            int op,l,r;
            cin >> op >> l >> r;
            if(op == 1) {
                cout << query(l,r) << endl;
            }else if(op == 2) {
                update1(l,r);
            }else if(op == 3) {
                update2(l,r);
            }
        }
    }
    

    1005. Separated Number

    • 题意

    给你一个(k)(n), ((1<= k <= n <= 10^{10^6})),求最多将(n)分成(k)的部分(允许前导零)的所有解(((11)(451)(4) = 11 + 451 + 4 = 466))的总和是多少。

    • 思路

    计算每一位数字的贡献,如当前是第(i)位数字,将(i o i + j)括起来,那么第(i)位数字(d)的当前贡献为(d * 10^j)在乘上(i)的前面随意取的区间个数 + (i + j)的后面随意取的区间个数 (= sum_{b =0}^{k-2} C(frac{b}{n - j - 2})),最后注意(i + j = n)的情况(只取前面的随意取的区间个数) , 然后预处理出所有的(sum_{b=0}^{k-1} C(frac{b}{a}) and sum_{b=0}^{k-2} C(frac{b}{a}))即可。

    注意(2sum_{b = 0}^tC(frac{b}{a - 1}) - C(frac{b}{a - 1}) = sum_{b = 0}^tC(frac{b}{a}))

    code :

    int fact[N], infact[N], power[N];
    void init(int n) {
    	fact[0] = 1;
        for (int i = 1 ; i <= n; ++i) {
            fact[i] = 1LL * fact[i - 1] * i % mod;
        }
        infact[n] = pow_mod(fact[n], mod - 2);
        for(int i = n - 1;i >= 0;i --) infact[i] = 1LL * infact[i + 1] * (i + 1) % mod;
        power[0] = 1;
        for(int i = 1;i <= n;i ++)  power[i] = power[i - 1] * 10 % mod;
    }
    
    int C(int a,int b) {
    	if(a<0||b<0||a<b) return 0;
    	return fact[a] * infact[a - b] % MOD * infact[b] % MOD;
    }
    int f2[N], f1[N];
    char str[N];
    void solve(){
        int k, n;
        cin >> k >> (str + 1);
        int len = strlen(str + 1);
        f1[0] = 1;
        f2[0] = (k >= 2); // 特判k为1的情况
        for(int i = 1;i <= len;i ++) {
            f1[i] = (2LL * f1[i - 1] % mod - C(i - 1,k - 1) + 2 * mod) % mod;
            f2[i] = (2LL * f2[i - 1] % mod - C(i - 1,k - 2) + 2 * mod) % mod;
        } 
        int sum = 0;
        int ans = 0;
        for(int i = len;i > 0;i --) {
            int now = str[i] - '0';
            if(i < len) sum = (sum + 1LL * power[len - i - 1] * f2[i - 1] % mod) % mod;
            ans = (ans + 1LL * now * (sum + 1LL * power[len - i] * f1[i - 1] % mod) % mod + mod) % mod;
        }
        cout << ans << endl;
    }
    

    1006. GCD Game

    • 题意

    给出长度为(n)的序列(a), 每次可以选择一个数字(a_i),然后选择一个数字(x (1 <= x < a_i)), 将(a_i = gcd(a_i,x))

    • 思路

    可以发现一个数字最多可以(gcd),它的质因子的次数之和次,然后就是一个经典的(nim)博弈,(n)堆石子,每次选择可以拿 (1 o f(a_i))个。

    code :

    int primes[M], tot;
    int cnt[M];
    bool st[M];
    
    void init() {
        for(int i = 2;i < M;i ++) {
            if(!st[i]) {
                primes[++ tot] = i;
                cnt[i] = 1;
            }
            for(int j = 1; j <= tot && primes[j] * i < M;j ++) {
                cnt[i * primes[j]] = cnt[i] + 1;
                st[i * primes[j]] = 1;
                if(i % primes[j] == 0) break;
            }
        }
    }
    
    
    void solve(){
        int n;
        cin >> n;
        int ans = 0;
        for(int i = 1;i <= n;i ++) {
            int x;
            cin >> x;
            ans ^= cnt[x];
        }
        if(ans == 0) {
            cout << "Bob" << endl;
        }else {
            cout << "Alice" << endl;
        }
    }
    

    1008. Square Card

    • 题意

    给两个圆,第一个圆是得分区域,第二个园是得奖区域,问现在随意向第一个圆内扔一个旋转的正方形,但这个正方形严格在圆内才可以算,问这个正方形即在得分区域又在得奖区域的概率。

    • 思路

    画个图直接算,注意正方形不用转换成圆,用正方形取探圆的边缘区域,那么让正方形进来而需要减少的半径就是那一小段圆弧和 (r / 2)​​的路径。
    st
    就这橙线上面的剪掉, 两个圆都剪掉(然后正方形可以看成一个点了),然后两圆相交面积 / 大圆面积就是答案。

    code :

    #define PI acos(-1)
    struct Point{
        double x,y;
        Point(){}
        Point(double _x,double _y) {
            x = _x;
            y = _y;
        }
        double distance(Point p){
            return hypot(x - p.x,y - p.y);
        }
    
    };
    // 求两圆相交的面积
    double Area_of_overlap(Point c1, double r1, Point c2, double r2) 
    {
        double d = c1.distance(c2);
        if (r1 + r2 < d + eps)
        {
            return 0;
        } 
        if (d < fabs(r1 - r2) + eps)
        {
            double r = min(r1, r2);
            return PI * r * r;
        }
        double x = (d * d + r1 * r1 - r2 * r2) / (2 * d);
        double t1 = acos(x / r1);
        double t2 = acos((d - x) / r2);
        return r1 * r1 * t1 + r2 * r2 * t2 - d * r1 * sin(t1);
    }
    
    void solve(){
        db r1,r2,x1,x2,y1,y2;
        sc("%lf%lf%lf", &r1, &x1, &y1);
        sc("%lf%lf%lf", &r2, &x2, &y2);
        db r;
        sc("%lf", &r);
        
        r = 1.0 * r / 2.0; // a / 2
        if(r > r1 || r > r2) {
            cout << "0.000000" << endl;
            return;
        }
        db cs = r / r1; // cos0
        db del1 = r1 - r1 * sin(acos(cs)); // 小圆弧内需要删的
        r1 -= del1 + r; // r1 - del1 - a / 2
        if(r1 < eps) {
            cout << "0.000000" << endl;
            return;
        }
        cs = r / r2; // cos0
        db del2 = r2 - r2 * sin(acos(cs)); // 小圆弧内需要删的
        r2 -= del2 + r; // r2 - del1 - a / 2
        if(r2 < eps) {
            cout << "0.000000" << endl;
            return;
        }
        Point a = Point(x1,y1), b = Point(x2,y2);
        if(a.distance(b) >= r1 + r2) {
            cout << "0.000000" << endl;
            return;
        }
        db inter = Area_of_overlap(a, r1, b, r2);
        db o = PI * r1 * r1;
        pr("%.6f
    ", inter / o);
    }
    

    1009. Singing Superstar

    • 题意

    给一个目标串和一堆模式串,让你去计算每个不重复的匹配子串个数(如 "(aba)" (in) "(ababa)" = 1)

    • 思路

    ac自动机模板改一下,或者哈希字符串匹配(记得开map,count()操作(O(logn)))

    code :

    const int  maxn = 100010;
    struct node{ int index; char s[30];}a[maxn];
    struct AC{
    	int num,ch[6*maxn][30],f[6*maxn],last[6*maxn],val[6*maxn];
        int times[6*maxn],dep[7*maxn],pos[6*maxn];
    	void init(){
    		num=0;
            memset(ch,0,sizeof(ch));
            memset(f,0,sizeof(f));
            memset(last,0,sizeof(last));
            memset(val,0,sizeof(val));
            memset(times,0,sizeof(times));
            memset(dep,0,sizeof(dep));
            memset(pos,-1,sizeof(pos));
            return;
    	}
    	void insert(char *s,int qN){
            int u=0,len=strlen(s),id;
            for(int i=0;i<len;i++){
                id=s[i]-'a';
                if(!ch[u][id])ch[u][id]=++num;
                u=ch[u][id];
            }
            val[u]=1;
            dep[u]=len;
            a[qN].index=u;
            return;
        }
    	void getfail(){
            queue<int> Q;
            Q.push(0);
            int x,u,v;
            while(!Q.empty()){
                x=Q.front();Q.pop();
                for(int i=0;i<26;i++){
                    u=ch[x][i];
                    if(!u){
                        ch[x][i]=ch[f[x]][i];
                        continue;
                    }
                    Q.push(u);
                    if(x==0) continue;
                    v=f[x];
                    while(v&&!ch[v][i]) v=f[v];
                    f[u]=ch[v][i];
                    last[u]=val[f[u]]?f[u]:last[f[u]];
                }
            }
            return;
        }
    	void find(char *s){
            memset(times,0,sizeof(times));
            int len=strlen(s),u=0,id,j;
            for(int i=0;i<len;i++){
                id=s[i]-'a';
                u=ch[u][id];
                j=u;
                do{
                    if(i - pos[j] >= dep[j]){ // 不相等
                        times[j]++;
                        pos[j]=i;
                    }
                    j=last[j];
                }while(j);
            }
            return;
        }
    }ac;
    char to[N];
    void solve(){
        int n;
        cin >> to;
        cin >> n;
        ac.init();
        for(int i = 1; i <= n; i++)
        {
            cin >> a[i].s;
            ac.insert(a[i].s, i);
        }
        ac.getfail();
        ac.find(to);
        for(int i = 1; i <= n; i++)
        {
            cout << ac.times[a[i].index] << endl;
        }
    }
    
  • 相关阅读:
    168. Excel Sheet Column Title
    171. Excel Sheet Column Number
    264. Ugly Number II java solutions
    152. Maximum Product Subarray java solutions
    309. Best Time to Buy and Sell Stock with Cooldown java solutions
    120. Triangle java solutions
    300. Longest Increasing Subsequence java solutions
    63. Unique Paths II java solutions
    221. Maximal Square java solutions
    279. Perfect Squares java solutions
  • 原文地址:https://www.cnblogs.com/darker-wxl/p/15136293.html
Copyright © 2011-2022 走看看