• 2022牛客蔚来杯第五场KBHDCG


    2022牛客蔚来杯第五场

    K.Headphones

    • 题意

      • 总共有n对不同的耳机,已经拿出k对
      • 问至少需要再拿多少只耳机才能从n-k对中取出k+1对耳机
    • 题解

      • 签到题
      • 如果剩余的n-k对已经不足以凑出k+1对,那么一定取不出k+1对耳机
      • 如果剩余的n-k对数量上足够凑出k+1对,那么考虑取多少只耳机才能保证一定能取出k+1对耳机。剩余2*(n-k)只耳机,最坏情况为把所有的单只耳机取出来了,即取了n-k只耳机,根据鸽巢原理只需再取k+1只耳机一定能保证取出k+1对耳机。即总共需取(n-k)+(k+1)=n+1只耳机
    • 代码

    #include 
    
    using namespace std;
    
    int main() {
        int n,k;
        cin>>n>>k;
        if(n-k
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    B.Watches

    • 题意

      • 给n个表,给定每个表的价格,有m元钱
      • 买表的价格为,手表本身的价钱+购买表的数量*表原始给的顺序i
      • 问最多可以买多少个手表
    • 题解

      • 研究对象是手表的时候,不能确定哪些价值更小,因为k是不确定的
      • 研购买究数量k时,k对买表价格的影响是单调的,所以可以二分k。k确定的情况下所有表的价格确定,直接算出价格排序后check前k个能不能买下来即可。
    • 代码

    #include 
    #include 
    
    using namespace std;
    #define int long long
    const int N=1e5+5;
    
    int n,m;
    int a[N],b[N];//注意开两个数组,一个存原价值,一个用来check时计算新价值排序的,因为需要check多次,所以只用一个数组不行
    
    bool check(int k) {
        for(int i=1;i<=n;i++) b[i]=a[i]+k*i;//计算新价值
        sort(b+1,b+1+n);//排序
        
        int res=0;
        for(int i=1;i<=k;i++) {//检验是否能买下
            res+=b[i];
            if(res>m) return false;
        }
        return true;
    }
    
    void solve() {
        for(int i=1;i<=n;i++) cin>>a[i];
        
        int l=0,r=n;
        while(l>1;
            if(check(mid)) l=mid;
            else r=mid-1;
        }
        cout<>n>>m) solve();
        
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    H.Cutting Papers

    • 题意

      • 求下两式图形的并集面积
        { ∣ x ∣ + ∣ y ∣ + ∣ x + y ∣ < = n x 2 + y 2 = n 2 4
        {|x|+|y|+|x+y|<=nx2+y2=n24" role="presentation" style="position: relative;">{|x|+|y|+|x+y|<=nx2+y2=n24
        {x+y+x+y<=nx2+y2=4n2
    • 题解

      • 如图,为半个圆+两个小正方形的面积
    • 代码

    #include
    using namespace std;
    const double pi=3.1415926535897932385;
    //可以写成 pi=acos(-1);
    int main() {
        long long n;
        while(cin>>n)
            printf("%.12f\n", n * n * pi / 8.0 + n * n / 2.0);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    D.Birds in the tree

    • 题意

      • 给01字符串表示一棵树所有节点的颜色,以及树的边
      • 求有多少个连通子图使得度为1的节点颜色相同
    • 题解

      • 树形dp

      • f[u,0/1]表示以u为根的子树,u在联通块内,联通块内度数为1的颜色为0/1的联通块数量

      • 假设u点颜色为0(1一样)
        f [ u , 0 ] = ∏ v ∈ c h i l d u ( f [ v , 0 ] + 1 ) f [ u , 1 ] = ∏ v ∈ c h i l e u ( f [ v , 1 ] + 1 ) − ∑ v ∈ c h i l d u f [ v , 1 ] − 1 f[u,0]=\prod_{v\in child_{u}}(f[v,0]+1)\\ f[u,1]=\prod_{v\in chile_{u}}(f[v,1]+1)-\sum_{v\in child_{u}}f[v,1]-1 f[u,0]=vchildu(f[v,0]+1)f[u,1]=vchileu(f[v,1]+1)vchilduf[v,1]1

      • 状态转移方程解释:

        f[u,0],对于根节点u的所有孩子都可以选或者不选,即每个孩子对答案的贡献为f[v,0]+1,由乘法原理可以得到上式。并且因为u的颜色为0,所以即使所有孩子都不选联通块只剩u这一个1度的节点也是符合要求的。

        f[u,1],一样对于孩子节点可以选和不选然后乘法原理,但是要减去只剩自己的情况,因为颜色不对。同时排除有根节点只有1度的情况,因为根节点不符合颜色要求,所以-1*f[v,1],(排除如下图所示的情况)

      • a n s = ∑ i = 1 n ( f [ i , 1 ] + f [ i , 0 ] ) ,从底向上算, d f s 时实则是这样算 ans=\sum_{i=1}^{n}(f[i,1]+f[i,0]),从底向上算,dfs时实则是这样算 ans=i=1n(f[i,1]+f[i,0]),从底向上算,dfs时实则是这样算

    • 代码

    #include 
    #include 
    
    using namespace std;
    const int N=3e5+10,mod=1e9+7;
    typedef long long LL;
    
    char s[N];
    int n,pos[N];
    int h[N],e[2*N],ne[2*N],idx;//注意无向边的要开两倍范围
    LL ans,f[N][2];//答案,动态数组
    
    void add(int a,int b) {
        e[idx]=b,ne[idx]=h[a],h[a]=idx++;
    }
    
    void dfs(int u,int fa) {//因为加边会加入父亲节点,而题目要求的是子树,所以传入父节点用来排除计算了父节点
        f[u][0]=f[u][1]=1;//初始化为1,才能计算
        LL sum=0;
        for(int i=h[u];~i;i=ne[i]) {//遍历所有子节点
            int j=e[i];
            if(j!=fa) {
                dfs(j,u);//递归计算
                sum=(sum+f[j][pos[u]^1])%mod;
                f[u][0]=(f[u][0]*(1+f[j][0]))%mod;//乘法原理
                f[u][1]=(f[u][1]*(1+f[j][1]))%mod;
            }
        }
        f[u][pos[u]^1]--;//因为一开始预支了1给这个不符合的,所以再减回来
        ans=(ans+f[u][0]+f[u][1]-sum+ mod) % mod;//因为上面-1过,回溯回去计算的时候,相当于u这个子节点不选的方案数已经减掉了,所以不需要f[u][pos[u]^1]-sum,但是ans是全局答案,所以需要再减sum
    }
    
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(0);
        
        cin>>n;cin>>s+1;
        for(int i=1;i<=n;i++) pos[i]=s[i]-'0';//记录每个点的颜色
        int a,b;
        memset(h,-1,sizeof h);//初始化表头
        for(int i=1;i>a>>b;
            add(a,b);add(b,a);//无向边
        }
        
        dfs(1,1);//从根开始算
        cout<
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    C.Bit Transmission

    • 题意

      • 一个长度为n的01字符串,询问3n次问某个位置是否为1并得到答案
      • 回答中最多会出现一次错误的答案,问能否确定这个字符串
    • 题解

      • 对每一个位置考虑能否确定字符是什么,对于某位的YES和NO的数量分类讨论

        N\Y数量01>1
        0-11/-11
        10/-1-11
        >100-1
        1. 可确定字符四种情况

          Y=1,N>1;确定yes是说谎,所以字符为’0’

          Y>1,N=1;同上,字符为’1’

          Y>1,N=0;一定没有谎,所以答案为’1’

          Y=0,N>1;同上,字符为‘0’

        2. 不可确定字符的三种情况

          Y=0,N=0;没有任何信息,不可能确定

          Y=1,N=1;说谎了,不知道是哪个,不可能确定

          Y>1,N>1;谎言数量>1了,不符合题意,无法确定

        3. 可能可以确定的两种情况

          Y=1,N=0;如果所有位置判断完之后说有过谎,那么可以确定这位字符为’1’

          Y=0,N=1;同理

          这种情况没办法在线判断,只能完全判断之后再来判断。所以可以先认为能判断字符是什么,同时记录这种情况有没有出现,最终如果(没说过谎言&&出现过这种情况),无法确定字符

    • 代码

    #include 
     
    using namespace std;
    const int N=1e5+10;
    
    struct bit {
        int y, n;
    }a[N];
     
    bool ans[N];
     
    int main() {
        std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
        int n;
        cin >> n;
        int x;
        string s;
        for (int i = 1;i <= 3 * n;i++) {
            cin >> x >> s;
            if (s == "YES") a[x].y++;
            else a[x].n++;
        }
        
        bool fault = false,have=false;
        bool ok = true;
        for (int i = 0;i < n;i++) {
            if (a[i].y > 1 && a[i].n == 0) ans[i] = 1;
            else if (a[i].y == 0 && a[i].n > 1) ans[i] = 0;
            else if (!fault && a[i].y > 1 && a[i].n == 1) ans[i] = 1, fault = 1;
            else if (!fault && a[i].y == 1 && a[i].n > 1) ans[i] = 0, fault = 1;
            else if (a[i].y == 1 && a[i].n == 0) ans[i] = 1,have=true;
            else if (a[i].y == 0 && a[i].n == 1) ans[i] = 0,have=true;
            else ok = false;
        }
        if (!ok || !fault&&have) cout << -1;
        else for (int i = 0;i < n;i++) cout << ans[i];
        cout << '\n';
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    G.KFC Crazy Thursday

    • 题意

      • 回文串模版题
    • 题解

      • 板子如下,不会,捏吗
    • 代码

    #include
    #define MAXN 500010
    using namespace std;
    int n, tot = 1, last, cur, pos;
    int len[MAXN], num[MAXN], fail[MAXN], trie[MAXN][26], ans[4];
    char s[MAXN];
    int getfail(int now,int i){
    	while(i - len[now] - 1 < 0 || s[i-len[now]-1] != s[i]) now = fail[now];
    	return now;
    }
    int main(){
    	while(~scanf("%d%s", &n, s)){
    		memset(ans, 0, sizeof(ans));
    		memset(num, 0, sizeof(num));
    		memset(len, 0, sizeof(len));
    		memset(trie, 0, sizeof(trie));
    		memset(fail, 0, sizeof(fail));
    		tot = 1; last = 0; cur = 0; pos = 0;
    		fail[0] = 1; len[1] = -1;
    		for(int i = 0; i <= n - 1; i++){
    			pos = getfail(cur,i);
    			if(!trie[pos][s[i]-'a']){
    				fail[++tot] = trie[getfail(fail[pos],i)][s[i]-'a'];
    				trie[pos][s[i]-'a'] = tot;
    				len[tot] = len[pos] + 2;
    				num[tot] = num[fail[tot]] + 1;
    			}
    			cur = trie[pos][s[i]-'a'];
    			last = num[cur];
    			if(s[i] == 'k') ans[1] += last;
    			else if(s[i] == 'f') ans[2] += last;
    			else if(s[i] == 'c') ans[3] += last;
    		}
        printf("%d %d %d\n", ans[1], ans[2], ans[3]);
    	}
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
  • 相关阅读:
    Sql注入详解(原理篇)
    ZigBee 3.0理论教程-通用-1-03:协议架构-物理层(PHY)
    xpah的使用
    【Python&GIS】基于高德Api实现批量地址查询经纬度
    char 和 varcha 的区别是什么
    Java亿级流量架构之网关设计思路
    无线智慧城市业务方案建设
    阻塞队列--线程安全问题
    蓝桥杯物联网竞赛_STM32L071_18_长短按键检测
    优化器scipy.optimize参考指南
  • 原文地址:https://blog.csdn.net/m0_49843646/article/details/126146694