zoukankan      html  css  js  c++  java
  • 省选模拟测试5

    期望得分: (100+40+30 = 170)

    实际得分: (100+0+0 = 100)

    (T1) (noip) 难度的题,不说了。

    (T2) 虽然正解是线段树合并,但自己却拿线段树合并优化暴力 jk。

    (T3) 本来打了 (30) 分的暴力分,但是出题人没造那一档的数据,然后我就愉快的爆零了。

    T1 茶 a

    题意描述

    一开始,你拥有一个饼干,现在有三种操作

    1. 一种是将手里的饼干数量 (+1)
    2. 一种是将手里的 (a) 个饼干换成 1¥
    3. 一种是将 1¥换成 (b) 个饼干

    求出在 (k) 次操作后最多有多少个饼干。

    数据范围: (k,a,bleq 10^9)

    solution

    水题,不说了。

    注意特判各种情况就行。

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    #define int long long
    int k,a,b;
    inline int read()
    {
        int s = 0,w = 1; char ch = getchar();
        while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
        while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0'; ch = getchar();}
        return s * w;
    }
    signed main()
    {
        freopen("a.in","r",stdin);
        freopen("a.out","w",stdout);
        k = read(); a = read(); b = read();
        if(a >= b || k <= a) printf("%lld
    ",k+1);
        else 
        {
            int tmp = a + (k-a+1)/2 * (b-a);
            if((k-a+1)%2 == 1) tmp++;
            printf("%lld
    ",max(k+1,tmp));  
        }
        fclose(stdin); fclose(stdout);
        return 0;
    }
    

    T2 民族 b

    题意描述

    (n) 座城市,一些民族。这些城市之间由 (n-1) 条有边权道路连接形成了以城市 (1) 为根的有根树。
    每个城市都是某一民族的聚居地,已知第 (i) 个城市的民族是 (a_i),人数是 (1)
    我们定义一个民族 (x) 的来往程度 (f(x)) 为民族为 (x) 的点两两之间的距离之和,距离定义为树上两点间
    最短路距离。
    他想知道以 (i) 为根的子树内来往程度最大的民族 (x) 是多少,如果有多个,求编号最小。
    以及对于给定的 (k_i) ,求 子树内编号的 (k_i) 小民族 (y)(f(y))

    数据范围: (nleq 10^5, a_ileq 10^5)

    solution

    线段树合并/DSU。

    我们把距离柿子拆一下变成: (dis[x]+dis[y]-2 imes dis[lca(x,y)]) , (dis[i]) 表示根节点到 (i) 的边权和。

    我们考虑那线段树合并来维护这个东西。

    我们对于每个节点,开一个以民族编号为下标的权值线段树。

    对于每个叶子节点 (o) ,设其民族编号为 (x), 维护如下信息:

    • (sum[o]) 表示 (f(x)) 即民族编号为 (x) 的来往程度。
    • (w[o]) 表示所有民族编号为 (x) 的点的 (dis) 之和。
    • (siz[o]) 表示民族编号为 (x) 的点的个数。
    • (tag[o]) 表示民族编号为 (x) 的点是否出现过。

    对于非叶子节点 (o) 维护如下信息:

    • (sum[o]) 表示 (o) 的子孙结点中叶子节点的 (sum) 最大值。
    • (id[o]) 表示 (o) 的子孙节点中 (sum) 最大的叶子结点的编号是多少。
    • (tag[o]) 表示 (o) 的子孙节点中叶子结点不为空的个数。

    对于线段树合并的时候,我们只需要在叶子节点合并,其他的非叶子节点由儿子节点 (up) 一下即可。

    假如说我们现在要把 (rt[x])(rt[to]) 两颗线段树合并,对两颗线段树的叶子节点 (p,q) 合并时,设合并之后的叶子节点为 (p) ,则有:

    • (sum[p] = sum[p] + sum[q] + siz[p] imes sum[q] + siz[q] imes sum[p] - 2 imes siz[p] imes siz[q] imes dis[x])
    • (w[p] = w[p] + w[q])
    • (siz[p] = siz[p] + siz[q])
    • (tag[p] = tag[p] or tag[q])

    我们只需要遍历一遍整棵树,维护出上述信息,对于询问 (1) 就是 (tr[rt[x]].id) ,对于询问 (2) ,我们只需要利用维护出的 (tag) 在线段树上二分即可。

    Code

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    #define int long long
    const int N = 1e5+10;
    int n,cnt,tot,u,v,w,maxn;
    int a[N],k[N],rt[N],head[N],sum[N],ans1[N],ans2[N];
    inline int read()
    {
        int s = 0,w = 1; char ch = getchar();
        while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
        while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0'; ch = getchar();}
        return s * w;
    }
    struct node
    {
        int to,net,w;
    }e[N<<1];
    struct Tree
    {
        int lc,rc;
        int sum,siz,w,id,tag;
    }tr[10000010];
    void add(int x,int y,int w)
    {
    	e[++tot].to = y;
    	e[tot].w = w;
    	e[tot].net = head[x];
    	head[x] = tot;
    }
    void up(int o)
    {
        tr[o].tag = tr[tr[o].lc].tag + tr[tr[o].rc].tag;
        tr[o].sum = max(tr[tr[o].lc].sum,tr[tr[o].rc].sum);
        if(tr[o].sum == 0)
        {
            if(tr[tr[o].lc].tag) tr[o].id = tr[tr[o].lc].id;
            else tr[o].id = tr[tr[o].rc].id;
        }
        else tr[o].id = tr[o].sum == tr[tr[o].lc].sum ? tr[tr[o].lc].id : tr[tr[o].rc].id;
    }
    void insert(int &o,int l,int r,int x,int val)
    {
        if(!o) o = ++cnt;
        if(l == r)
        {
            tr[o].w += val;
            tr[o].siz++;
            tr[o].tag = 1;
            tr[o].id = l;
            return;
        }
        int mid = (l+r)>>1;
        if(x <= mid) insert(tr[o].lc,l,mid,x,val);
        if(x > mid) insert(tr[o].rc,mid+1,r,x,val);
        up(o);
    }
    void merage(int &x,int y,int l,int r,int root)
    {
        if(!x) {swap(x,y); return;}
        if(!y) return;
        if(l == r)
        {
            tr[x].sum = tr[x].sum + tr[y].sum + tr[x].w * tr[y].siz + tr[y].w * tr[x].siz - 2 * sum[root] * tr[x].siz * tr[y].siz;
            tr[x].siz += tr[y].siz;
            tr[x].tag |= tr[y].tag;
            tr[x].w += tr[y].w;
            return;
        }
        int mid = (l+r)>>1;
        merage(tr[x].lc,tr[y].lc,l,mid,root);
        merage(tr[x].rc,tr[y].rc,mid+1,r,root);
        up(x);
    }
    int query(int o,int l,int r,int k)
    {
        if(l == r) return tr[o].sum;
        int mid = (l+r)>>1;
        int num = tr[tr[o].lc].tag;
        if(k <= num) return query(tr[o].lc,l,mid,k);
        else return query(tr[o].rc,mid+1,r,k-num);
    }
    void dfs1(int x,int fa,int w)
    {
        sum[x] = sum[fa] + w;
        for(int i = head[x]; i; i = e[i].net)
        {
            int to = e[i].to;
            if(to == fa) continue;
            dfs1(to,x,e[i].w);
        }
    }
    void dfs2(int x,int fa)
    {
        insert(rt[x],1,maxn,a[x],sum[x]);
        for(int i = head[x]; i; i = e[i].net)
        {
            int to = e[i].to;
            if(to == fa) continue;
            dfs2(to,x);
            merage(rt[x],rt[to],1,maxn,x);
        }
        ans1[x] = tr[rt[x]].id;
        if(tr[rt[x]].tag < k[x]) ans2[x] = -1;
        else ans2[x] = query(rt[x],1,maxn,k[x]);
    }
    signed main()
    {
        freopen("b.in","r",stdin);
        freopen("b.out","w",stdout);
        n = read();
        for(int i = 1; i <= n-1; i++)
        {
            u = read(); v = read(); w = read();
            add(u,v,w); add(v,u,w);
        }
        dfs1(1,0,0);
        for(int i = 1; i <= n; i++) a[i] = read(), maxn = max(maxn,a[i]);
        for(int i = 1; i <= n; i++) k[i] = read();
        dfs2(1,0);
        for(int i = 1; i <= n; i++) printf("%lld %lld
    ",ans1[i],ans2[i]);
        fclose(stdin); fclose(stdout);
        return 0;
    }
    

    T3 c

    题意描述

    给你三个整数 (k,a,b) ,满足 (gcd(a,b) = 1) ,让你求不能被 (ax+by, xgeq 0, ygeq 0) 表示出来的第 (k) 大的数是多少。

    数据范围:(a,bleq 5 imes 10^7,kleq 10^{18})

    solution

    首先有一个经典的结论:不能被 (ax+by) 表示出来的最大的数是 (a imes b-a-b) ,不能被表示出来的数的个数为 ((a-1) imes (b-1)over 2)

    具体证明的话,我不太会证。

    对于这道题我们可以先转化为求第 (k) 小的是多少,即: (k = {(a-1) imes (b-1)over 2}-k+1)

    我们把 ([1,a imes b-a-b]) 分成若干块,每一块的长度均为 (a) ,如图:

    我们可以确定第 (k) 小的是在那一块,具体来说就是求一个 (pos) 使得 (1-pos imes a) 中不能被表示出来的数 (geq k), (1-(pos-1) imes a) 中不能被表示出来的数 (leq k)

    那么我们怎么求 (1-ia) 中不能被表示出来的数的个数呢?这个我们可以转化为求能被表示出来的个数。

    具体来说就是,设 (sum[i]) 表示 (1-i imes a) 中能被表示出来的数的个数。

    则有递推式 :(sum[i] = sum[i-1] + now + 1), 其中 (now)(1-i imes a) 中为 (b) 的倍数有多少个。

    具体证明如下,首先 ([1,i imes a]) 中能被表示出来的数的个数等价于 ([(i-1) imes a+1,i imes a]) 这一段区间能被表示出来的数的个数加上 ([1,(i-1) imes a]) 这一段区间能被表示出来的个数。

    对于 ([1,(i-1) imes a]) 这一段区间能被表示出来的数的个数就是 (sum[i-1])

    对于 (by_1) ,我们可以使他加上 (ax1) 使得 ((i-1) imes a leq ax_1+by_1leq i imes a)

    这样的 (by_1) 共有 (now) 个,又因为 (gcd(a,b) = 1) ,可知 (ax_1+by_1) 是不会重复的。

    所以 ([(i-1) imes a,i imes a]) 这一段区间可以被表示出来的数的个数为 (now+1), (+1) 则是因为 (i imes a) 也可以被表示出来。

    因此,我们的递推式 (sum[i] = sum[i-1]+now+1) 是正确的。

    那么有了 (sum[i]) ,我们的 (pos) 就很好求出来了。

    求出来 (pos) 之后,我们先用 (k) 减去 ([1,(pos-1) imes a]) 中不能被表示出来的数的个数,然后 ([(pos-1) imes a+1,pos imes a]) 中第 (k) 个不能被表示出来的数,就是我们的答案。

    又因为每一块的元素不超过 (a) 个,所以我们可以枚举 ([(pos-1) imes a+1,pos imes a]) 这一段区间中的数,来判断它是否是第 (k) 个不能被表示出来的数。

    那么怎么判断数 (s) 是否可以被表示出来呢?

    很简单,因为 (s) 可以被表示出来,等价于 (s=ax+by) 即: (sequiv bypmod a) ,所以我们在枚举 (b) 的倍数的时候把 (by\% a) 标记为 (1), 如果 (s\%a) 被标记过,那么 (s) 就可以被表示出来。

    然后我们这道题就做完了。

    Code

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<map>
    using namespace std;
    #define int long long
    int a,b,k,cnt,now,sum,pos;
    bool vis[50000010];
    inline int read()
    {
        int s = 0,w = 1; char ch = getchar();
        while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
        while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0'; ch = getchar();}
        return s * w;
    }
    signed main()
    {
        freopen("c.in","r",stdin);
        freopen("c.out","w",stdout);
        a = read(); b = read(); k = read();
        k = ((a-1)*(b-1)/2)-k+1; vis[0] = 1;
        for(int i = 0; ; i++)
        {
        	while((now+1)*b <= i*a) now++, vis[(now*b)%a] = 1;
        	if(i*a-sum-now >= k) 
        	{
        		pos = i;
        		break;
    		}
    		sum += now + 1;
    	}
    	k -= (pos-1)*a-sum+1;//这里+1是因为0也可以被表示出来
    	for(int i = (pos-1)*a+1; i <= pos*a; i++)
    	{
    		if(!vis[i%a]) k--;
    		if(k == 0) 
    		{
    			printf("%lld
    ",i);
    			return 0;
    		}
    	}
    	fclose(stdin); fclose(stdout);
        return 0;
    }
    
  • 相关阅读:
    《仔仔细细分析Ext》 第N2章 GridPanel的小难点 第一节 每条数据后面跟随几个操作按钮
    TextField输入验证
    Ext.FormPanel 及控件横排显示
    备份
    重写
    this关键字
    TestCircle程序分析
    java方法重载
    static关键字
    super关键字
  • 原文地址:https://www.cnblogs.com/genshy/p/14484059.html
Copyright © 2011-2022 走看看