• 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;
        }
    }
    
  • 相关阅读:
    mysql基础命令(一)
    vue组件之间的通信
    wepy的使用
    mockjs中的方法(三)
    每周散记 20181022
    api资源
    三七
    画中画 视频合成
    每周散记 20180910
    linux文件权限多一个+啥意思
  • 原文地址:https://www.cnblogs.com/darker-wxl/p/15136293.html
Copyright © 2020-2023  润新知