zoukankan      html  css  js  c++  java
  • test20190818 NOIP2019 模拟赛

    0+0+20=20,不给大样例,小数据又水,还没有题解的垃圾题。

    A 题

    问题描述:
    long long ago, Lxhgww 统治的国家里有 n 个城市,其中某一个城市是 capital (首都),这 n 个城市构成以 capital 为根的有向树。

    Lxhgww 会通过发送指令去派一些士兵去保卫这些城市。Lxhgww 发出的指令格式为 x,k,表示向 x 结点派送 k 个士兵,向 x 的子节点派送 k + 1 个士兵,向 x 的子节点的子节点派送 k + 2 个士兵,以此类推。

    现在考古学家通过考古,发现了 Lxhgww 的国家的部分信息。考古学家只得到了这棵树的 n - 1 条边,但并不知道这 n - 1 条边的方向,也不知道哪个城市才是 capital。考古学家也得到了所有 Lxhgww 发出的指令。

    考古学家们知道 Lxhgww 是一个非常聪明的人,所以他们认为:以 capital 为根,这些指令所派送的士兵总数一定是最少的。

    现在考古学家想让你告诉他们,这些指令所派送的士兵总数的最小值是多少,以及哪些点有可能是 capital。

    输入:
    第一行读入两个整数 n,m,分别表示城市的个数以及指令的数量。
    接下来 n - 1 行,每行读入两个数 ai, bi,表示 ai 与 bi 之间有一条边。(注意边是没有方向的)
    接下来 m 行,每行读入两个数 x, k。

    输出:
    第一行输出一个整数,表示完成这些指令所需的最小值。 第二行输出若干个
    数,表示可能是 capital 的节点,这些数按照从小到大的顺序输出。

    样例输入:
    5 2
    1 5
    1 3
    1 2
    2 4
    1 1
    3 1

    样例输出:
    6
    2 4

    数据范围:
    对于 10%的数据,1 <= n, m <= 300
    对于 40%的数据,1 <= n, m <= 5000
    对于 100%的数据,1 <= n, m <= 500000, 0 <= k <= 1000, 1 <= ai, bi, x <= n

    题解

    将贡献拆分,就是(k imes siz_x+sum_{y in subtree_x} dep_y-depx)

    注意到根从父亲变到自己的变动量只有父亲和自己,然后换根大力维护即可。时间复杂度(O(n))

    然后我没有注意到多次加到同一个点上时(sum dep)要翻倍,也没有注意到(k=0)的情况。此题爆0。

    #include<bits/stdc++.h>
    using namespace std; 
    template<class T> T read(){
    	T x=0;char c=getchar();
    	while(!isdigit(c)) c=getchar();
    	for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    	return x;
    }
    template<class T> T read(T&x){
    	return x=read<T>();
    }
    #define co const
    #define il inline
    #define int long long
    
    co int N=500000+10;
    int n,m,val[N],cnt[N]; // edit 1:cnt
    vector<int> to[N];
    
    int siz[N],dep[N],sd[N],sv[N];
    
    void dfs1(int x,int fa){
    	siz[x]=1,sd[x]=dep[x];
    	for(int i=0;i<(int)to[x].size();++i){
    		int y=to[x][i];
    		if(y==fa) continue;
    		dep[y]=dep[x]+1;
    		dfs1(y,x);
    		siz[x]+=siz[y],sd[x]+=sd[y],sv[x]+=sv[y];
    	}
    	if(cnt[x]) sv[x]+=val[x]*siz[x]+cnt[x]*(sd[x]-dep[x]*siz[x]); // edit 2:k=0
    }
    
    int ans=1e18;
    vector<int> sol;
    
    void dfs2(int x,int fa,int sumd,int sumv){
    	if(fa){
    		if(cnt[x]) sumv-=val[x]*siz[x]+cnt[x]*(sd[x]-dep[x]*siz[x]);
    		if(cnt[fa]) sumv-=val[fa]*n+cnt[fa]*sumd;
    		sumd-=sd[x]-dep[x]*siz[x]+siz[x];
    		sumd+=n-siz[x]+sd[x]-dep[x]*siz[x];
    		if(cnt[x]) sumv+=val[x]*n+cnt[x]*sumd;
    		if(cnt[fa]) sumv+=val[fa]*(n-siz[x])+cnt[fa]*(sumd-(sd[x]-dep[x]*siz[x])-(n-siz[x]));
    	}
    	if(sumv<ans) ans=sumv,sol.assign(1,x);
    	else if(sumv==ans) sol.push_back(x);
    	for(int i=0;i<(int)to[x].size();++i){
    		int y=to[x][i];
    		if(y==fa) continue;
    		dfs2(y,x,sumd,sumv);
    	}
    }
    
    signed main(){
    	freopen("A.in","r",stdin),freopen("A.out","w",stdout);
    	read(n),read(m);
    	for(int i=1;i<n;++i){
    		int x=read<int>(),y=read<int>();
    		to[x].push_back(y),to[y].push_back(x);
    	}
    	while(m--){
    		int x=read<int>();
    		val[x]+=read<int>(),++cnt[x];
    	}
    	dfs1(1,0);
    	dfs2(1,0,sd[1],sv[1]);
    	printf("%lld
    ",ans);
    	sort(sol.begin(),sol.end());
    	for(int i=0;i<(int)sol.size();++i) printf("%lld ",sol[i]);
    	return 0;
    }
    

    B 题

    问题描述:
    Falsy 是一个可爱的女孩,她十分喜欢数字,她的梦想就是成为一名数学老师。有一天当她在把玩她的数字的时候,她不小心碰到了一个变量 x。初始时 x 的值为 x0。她发现每触碰一次这个变量,x 的值就会变成(kx + b) mod P。现在 Falsy 想让这个变量的值变回初始的样子,请你告诉她最少需要触碰多少次这个变量,能使它的值从 x0 又变回 x0。

    输入:
    第一行包含一个整数 T,表示测试数据组数。
    每次测试数据包含四个整数:k,b,x0,P。

    输出:
    对于每组测试数据输出一行包含一个整数,表示对应的答案,若无解则输出-1。

    样例输入:
    2
    4 7 1 13
    11 4 2 12

    样例输出:
    6
    1

    数据范围:
    对于 20%的数据,1 <= P <= 106,1 <= T <= 10。
    对于 100%的数据,0 <= k,b,x0 < P,1 <= P <= 109 + 9,1 <= T <= 100。

    题解

    大力推式子,发现变动(n)次的结果是:

    [k^n x+bsum_{i=0}^{n-1}k^i=k^nx+bfrac{k^n-1}{k-1} ]

    开始解方程

    [k^nx+bfrac{k^n-1}{k-1}=x mod P\ (k^n-1)(x+frac{b}{k-1})=0 mod P ]

    我一开始直接通分化简得到

    [(k^n-1)[(k-1)x+b]=0 mod P ]

    谁知道这样做是错的。因为(k-1)不一定有逆元,所以从前一个推到这个没问题,但推回去就是错的。

    王贝贝写出了正确的方程,必须要考虑进去(P)的变动。

    [(k^n-1)(x+frac{b}{k-1})+mP=0\ (k^n-1)[(k-1)x+b]+m(k-1)P=0 ]

    以下用([])代替([(k-1)x+b])。这时如果将(k^n-1)(m)看成未知数去解方程

    [k^n-1=0 mod frac{(k-1)P}{gcd([],(k-1)P)} ]

    这样做的答案是正确的,但是时间复杂度(O(T P)),和暴力没有多大区别。

    (Q=frac{P}{gcd([],P)}),标程通过解方程

    [(k^n-1)frac{[]}{gcd([],P)}=0 mod Q\ k^n=1 mod Q ]

    然后通过矩阵求逆???构造出了满足

    [(k^n-1)frac{[]}{gcd([],P)}+m(k-1)Q=0 ]

    的答案。

    [egin{bmatrix} b & x end{bmatrix} cdot egin{bmatrix} 1 & 1\ 0 & k end{bmatrix} ]

    std的代码

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <cstdlib>
    #include <cmath>
    #include <algorithm>
    #include <queue>
    #include <map>
    #include <set>
    #include <vector>
    #include <string>
    #include <stack>
    #include <bitset>
    #define INF 0x3f3f3f3f
    #define eps 1e-8
    #define FI first
    #define SE second
    using namespace std;
    typedef long long LL;
    
    inline int phi(int n) {
        int ret = n, a = n;
        for(int i = 2; i * i <= a; ++i) {
            if(a % i) continue;
            ret = ret / i * (i - 1);
            while(a % i == 0) a /= i;
        }
        if(a > 1) ret = ret / a * (a - 1);
        return ret;
    }
    
    int pow_mod(LL a, int p, int mod) {
        LL c = 1;
        while(p) {
            if(p & 1) c = c * a % mod;
            p >>= 1;
            a = a * a % mod;
        }
        return c;
    }
    
    void mul(int a[][2], int b[][2], int mod) {
        int c[2][2] = { {0, 0}, {0, 0} };
        for(int i = 0; i < 2; ++i) {
            for(int j = 0; j < 2; ++j) {
                for(int k = 0; k < 2; ++k) {
                    c[i][j] = (c[i][j] + (LL)a[i][k] * b[k][j]) % mod;
                }
            }
        }
        memcpy(a, c, sizeof(c));
    }
    
    int k, b, x, p;
    int cal(int n, int mod) {
        if(n == 0) return 1;
        int A[2][2] = { {1, 1}, {0, k} };
        int C[2][2] = { {1, 0}, {0, 1} };
        while(n) {
            if(n & 1) mul(C, A, mod);
            n >>= 1;
            mul(A, A, mod);
        }
        return (1LL * k * C[0][1] + 1) % mod;
    }
    
    int main() {
        freopen("B.in", "r", stdin);
        freopen("B.out", "w", stdout);
        int T;
        scanf("%d", &T);
        while(T--) {
            scanf("%d%d%d%d", &k, &b, &x, &p);
            if(k == 0) {
                puts(x == b ? "1" : "-1");
                continue;
            }
            if(k == 1) {
                if(b == 0) {
                    puts("1");
                    continue;
                }
                printf("%d
    ", p / __gcd(p, b));
                continue;
            }
            int q = p / __gcd((LL)p, (LL)(k - 1) * x + b);
            if(q == 1) {
                puts("1");
                continue;
            }
            if(__gcd(k, q) != 1) {
                puts("-1");
                continue;
            }
            int f = phi(q), mi = f;
            for(int i = 1; i * i <= f; ++i) {
                if(f % i) continue;
                if(pow_mod(k, i, q) == 1) {
                    mi = i; break;
                }
                if(pow_mod(k, f / i, q) == 1) mi = f / i;
            }
            int ans = cal(mi - 1, q);
            ans = q / __gcd(ans, q) * mi;
            printf("%d
    ", ans);
        }
        fclose(stdin);
        fclose(stdout);
        return 0;
    }
    

    C 题

    问题描述:
    Fang Fang 是一个非常讨厌二进制数的人,尤其是 8 位的二进制数。某一天她遇到了 n 个 8 位二进制数,她决定要把它们全部消灭掉。Fang Fang 手上有 m 个武器。其中某些武器可以把所有 8 位二进制表示包含某个特定前缀的数全部消灭;某些武器可以把所有 8 位二进制表示包含某个特定后缀的数全部消灭。但是每使用一个武器都会消耗 Fang Fang 一定的 IQ值,现在你需要帮助她用最少的 IQ 值把所有数字消灭。

    输入:
    第一行读入两个整数 n,m,分别表示数字个数以及武器个数。
    第二行包含 n 个整数 ai。
    接下来 m 行,每行表示一个武器:
    P s w:你可以消灭所有 8 位二进制表示中包含前缀 s 的数,消耗 w 点 IQ。
    S s w:你可以消灭所有 8 位二进制表示中包含后缀 s 的数,消耗 w 点 IQ。

    输出:
    输出一行包含一个整数,表示对应的答案。若无法消灭所有的数,输出-1。

    样例输入:
    8 7
    0 1 2 3 4 5 6 7
    P 000001 1
    P 0000000 1
    S 10 1
    S 11 1
    S 00 1
    S 01 1
    P 0000001 3

    样例输出:
    4

    数据范围:
    对于 30%的数据,1 <= n <= 20,1 <= m <= 50;
    对于 100%的数据,1 <= n <= 256,1 <= m <= 500,0 <= ai <= 255,1 <= w <= 1000,s 是一个 01 串,1 <= |s| <= 8。

    题解

    建出前缀和后缀的Trie树,考虑最小割模型。

    把需要消灭的点用 INF 边连起来。

    有 P 就在 P 对应前缀Trie树上把对应点父亲到自己的边的流量设为权值,没有 P 就把流量设成 INF。S 的做法同理。

    然后跑最小割就行了。算是一道网络流好题。

  • 相关阅读:
    Linux命令大全
    Restframework 视图组件与序列号组件的应用.
    Linux常用命令
    数据结构
    MongoDB
    算法
    Flask 语音分析
    Flask Session ,pymysql ,wtforms组件 虚拟virtualenv venv
    Flask 视图,模板,蓝图.
    Flask初识
  • 原文地址:https://www.cnblogs.com/autoint/p/test20190818.html
Copyright © 2011-2022 走看看