zoukankan      html  css  js  c++  java
  • xj膜你赛24

    昆特牌(gwent)

    题目描述

    作为一个资深OIer,你被邀请到位于波兰的CDPR总部参观。但没想到你刚一到就遇到了麻烦。昆特牌的数据库发生了故障。原本昆特牌中有 种卡牌和 种阵营,为了平衡,每个阵营拥有的卡牌种数都是相等的,并且每个阵营的数据顺序排列。由于故障,卡牌数据被打乱了,每个阵营现在有 种卡牌。因为昆特牌即将迎来重大更新,每种牌的所属阵营并不重要,工程师只想尽快让每个阵营拥有相同数量的卡牌。由于数据库的结构原因,你每单位时间只能将一种牌向左边或右边相邻的一个阵营移动。作为OI选手,这自然是难不倒你,但作为一名卡牌游戏爱好者,你想知道最终的卡牌分布有多少种方案。两种方案不同当且仅当存在一种卡牌,它在两种方案中所属阵营不同。对 (998244353) 取模

    数据格式

    输入格式

    第一行一个整数 (T) ,表示数据组数。
    接下来每组数据,第一行一个整数 ,第二行 (n) 个数,第 (i) 个数为 (a_i) ,意义见题目描述。

    输出格式

    (T) 行,每行一个数表示答案。

    样例输入1

    3
    3
    2 1 3
    3
    1 2 3
    3
    3 2 1
    

    样例输出1

    3
    9
    9
    

    样例解释

    对于第一组数据,初始为{{1,2}{3}{4,5,6}}
    移动结束后为{{1,2}{3,4}{5,6}},{{1,2}{3,6}{4,5}},{{1,2}{3,5}{4,6}}

    样例输入2

    4
    3
    8 1 0
    4
    5 0 1 2
    4
    0 4 0 0
    4
    1 1 6 0
    

    样例输出2

    1120
    30
    24
    270
    

    数据范围

    (n|k,sum_{i=1}^n a_i=k,Tle 500,nle 1000000,kle 1000)

    分析

    我们已知每个阵营最终拥有的牌数必定为总牌数的平均数。
    我们从左往右枚举每个阵营,若阵营 (i) 牌数小于阵营 (i-1) 牌数,则牌应从 (i-1) 传到 (i) ,在节点 (i) 上打一个正数标记;否则牌应从 (i) 传到 (i-1) ,在节点 (i) 上打一个负数标记。
    对于阵营间方案总数的计算,我们以如下例子分析:
    设阵营1-4的传牌情况为:
    1 3 2
    1 <- 2 <- 3 -> 4
    即从3往左穿3张牌,往右传2张牌;从2再往左传2张牌。
    再设1-4的牌数为 (a_1~a_4)
    那么,节点3往左和往右的总方案数为 (C(a_3,3)* C(a_3-3,2))
    节点2由于收到了节点3的三张牌,自己还有 (a_2) 张,所以组合的总数为 (a_2+3) , 方案数: (C(a_2+3,1))
    所以总方案数就是上述两结果的积。

    Code

    #include<cstdio>
    #define mod 998244353
    using namespace std;
    typedef long long D;
    inline D L(D x) { //mod
    return x>=mod?x%mod:x;
    }
    D fac[1000002];
    int a[1002],dir[1002];
    D qpow(D x,D y,D p) {
    	D ans=1;
    	while(y) {
    		if(y&1)ans=L(ans*x);
    		x=L(x*x);
    		y>>=1;
    	}
    	return ans;
    }
    D Div(D x,D y) {
    	return L(x*qpow(y,mod-2,mod));
    }
    D C_(D n,D m) {
    	return Div(fac[n],L(fac[m]*fac[n-m]));
    }
    D C(D n,D m) { //lucas
    	if(!n||!m)return 1;
    	return C_(L(n),L(m))*C(n/mod,m/mod);
    }
    int main() {
    	fac[0]=1;
    	for(int i=1; i<=1000001; i++)fac[i]=L(fac[i-1]*i);
    	int T;
    	scanf("%d",&T);
    	while(T--) {
    		int n,ave=0;
    		scanf("%d",&n);
    		for(int i=1; i<=n; i++) {
    			scanf("%d",a+i);
    			ave+=a[i];
    		}
    		ave/=n;
    		int now=0;
    		for(int i=1; i<=n; i++) {
    			dir[i]=now;
    			now+=a[i]-ave;
    		}
    		D ans=1;
    		dir[1]=dir[n+1]=0;
    		for(int i=1; i<=n; i++) {
    			if(dir[i]<=0&&dir[i+1]>=0)ans=L(ans*L(C(a[i],-dir[i])*C(a[i]+dir[i],dir[i+1])));
    			else if(dir[i]>=0&&dir[i+1]>=0)ans=L(ans*C(dir[i]+a[i],dir[i+1]));
    			else if(dir[i]<=0&&dir[i+1]<=0)ans=L(ans*C(-dir[i+1]+a[i],-dir[i]));
    		}
    		printf("%lld
    ",ans);
    	}
    	return 0;
    }
    

    时空幻境(braid)

    Tim拥有控制时间的能力。他学会了BFS后,出了一道题:求出一张无向图中连通块的个数。他想请你做出这道题
    来。

    数据格式

    输入格式

    第一行一个整数 (T) ,表示数据组数。
    对每组数据, (n,m) 表示点数和边数。
    为防止输入数据过大,边的信息以种子形式给出。输入 (x,k) ,表示 (a_1=x,a_i=a_{i-1}* k mod n) ,点从 (0) 开始标号
    (i) 条边的端点为 (a_{2i-1},a{2i})

    输出格式

    (T) 行,每行一个数,表示联通块的个数。

    样例1

    输入样例1

    1
    998244353 4
    1 3
    

    输出样例1

    998244349
    

    样例解释

    边为{1,3},{9,27},{81,243},{729,2187},所以共有 (998244349) 个联通块。

    样例2

    输入样例2

    5
    998244353 9088
    709393585 591328017
    998244353 8408
    476368048 122172238
    998244353 217
    922587543 10
    998244353 2948991
    40846641 7
    998244353 2692315
    542601916 5
    

    输出样例2

    998235265
    998235945
    998244136
    995295362
    995552038
    

    数据范围

    对所有数据,(n=998244353,1le m,k,x<998244353,1le Tle 10000)

    分析

    (p=998244353)
    由于不停地取模,所以顶点最终一定会进入一个循环。
    本题是要寻找一个最小的 (a) ,使得: $$y=x* k^a$$ $$y≡x(%p)$$ a即为循环的周期。
    我们已知:

    费马小定理:若 (p) 为质数,且 (k,p) 互质,则 (k^{p-1}≡1(\%p))

    等式两边乘以 (x)(x* k^{p-1}≡x(\%p))
    所以存在一个 (y=x* k^{p-1}) ,但是,这个 (y) 不一定是最小的。所以, (a) 一定是 (998244352) 的因数。
    对于 (998244352) 的所有因子 (i) ,我们用快速幂依次检验 (k^i)是否 (≡1(\%p)) ,如果可以,就跳出,说明找到了最小的 (a=i)
    然后分类讨论 (a) 为奇数和偶数的情况即可。

    Code

    #include<cstdio>
    #include<algorithm>
    #define mod 998244353ll
    using namespace std;
    typedef long long D;
    namespace W{
        D a[97]={0,1,2,4,7,8,14,16,17,28,32,34,56,64,68,112,119,128,136,224,238,256,272,448,476,512,544,896,952,
        1024,1088,1792,1904,2048,2176,3584,3808,4096,4352,7168,7616,8192,8704,14336,15232,16384,17408,28672,30464,
        32768,34816,57344,60928,65536,69632,114688,121856,131072,139264,229376,243712,262144,278528,458752,487424,
        524288,557056,917504,974848,1048576,1114112,1835008,1949696,2097152,2228224,3670016,3899392,4194304,
        4456448,7340032,7798784,8388608,8912896,14680064,15597568,17825792,29360128,31195136,35651584,58720256,
        62390272,71303168,124780544,142606336,249561088,499122176,998244352};
        const D cnt=96;
        D qpow(D x,D y){
            D ans=1;
            while(y){
                if(y&1)ans=ans*x%mod;
                x=x*x%mod;
                y>>=1;
            }
            return ans;
        }
        D work(D k){
            for(D i=1;i<=cnt;i++){
                if(qpow(k,a[i])==1)return a[i];
            }
            return 0;
        }
    }
    signed main(){
        D T;
        scanf("%lld",&T);
        while(T--){
            D n,m,x,k;
            scanf("%lld%lld%lld%lld",&n,&m,&x,&k);
            D w=W::work(k);
            if(w&1){
                if(m<w)printf("%lld
    ",n-m);
                else printf("%lld
    ",n-(w-1));
            }
            else{
                if(m<=(w>>1))printf("%lld
    ",n-m);
                else printf("%lld
    ",n-(w>>1));
            }
        }
        return 0;
    }
    

    初音未来(miku)

    题目描述

    Hercier作为一位喜爱Hatsune Miku的OIer,痛下决心,将Vocaloid买回了家。打开之后,你发现界面是一个长为 (n) 的序列,代表音调,并形成了全排列。你看不懂日语,经过多次尝试,你只会用一个按钮:将一段区间按升序排序。不理解音乐的Hercier决定写一个脚本,进行 (m) 次操作,每次对一段区间进行操作。可惜Hercier不会写脚本,他找到了在机房里的你,请你模拟出最后的结果。

    数据格式

    输入格式

    第一行两个整数 (n,m,L,R)
    接下来一行 (n) 个数,表示每个位置的音调 (a_i) 。然后 (m) 行,每行两个数 (l_i,r_i) ,表示操作的区间为 ([l_i,r_i])

    输出格式

    一行 (R-L+1) 个数,表示最终序列的 (a_L...a_R)

    样例1

    输入数据

    5 2 1 5
    5 1 4 2 3
    1 3
    2 4
    

    输出数据

    1 2 4 5 3
    

    样例解释

    [514]23 -> [145]23
    1[452]3 -> 1[245]3

    数据范围

    (nle 1500,mle 1000000)

    分析

    对一个序列排序,最少的交换次数为这个序列的逆序对个数,具体操作方法为交换逆序对。
    我们先随便用一个数据结构(set)来存储相邻逆序对,若 (a[i]>a[i+1]) ,则把 (i) 插入set。
    然后模拟冒泡排序,对于每一个需要排序的区间,我们二分查找到第一个 (ge l) 的位置 (i) ,然后 (swap(a[i],a[i+1])) 消去一个逆序对,并且检查是否有新逆序对产生,如果有,再加入set,继续二分查找,直到找不到为止。

    Code

    #include<cstdio>
    #include<set>
    #define maxn 1505 
    using namespace std;
    int a[maxn];
    set<int> st;
    void bubblesort(int l,int r){
        set<int>::iterator t;
        int p;
        while(1){
            t=st.lower_bound(l);
            if(t==st.end()||*t>=r)break;
            p=*t;
            swap(a[p],a[p+1]);
            st.erase(t);
            if(a[p-1]>a[p])st.insert(p-1);
            if(a[p+1]>a[p+2])st.insert(p+1);
        }
    }
    int main(){
        int n,m,l,r;
        scanf("%d%d%d%d",&n,&m,&l,&r);
        for(int i=1;i<=n;i++){
            scanf("%d",a+i);
            if(i!=1&&a[i-1]>a[i])st.insert(i-1);
        }
        while(m--){
            int x,y;
            scanf("%d%d",&x,&y);
            bubblesort(x,y);
        }
        for(int i=l;i<=r;i++)printf("%d ",a[i]);
        return 0;
    } 
    
  • 相关阅读:
    梯度消失、爆炸原因及其解决方法(转)
    Learning to Rank for IR的评价指标—MAP,NDCG,MRR
    tensorflow中使用指定的GPU及GPU显存 CUDA_VISIBLE_DEVICES
    深度学习 weight initialization
    python 第三方包安装
    列表操作 -深拷贝与浅拷贝
    python排序 sorted()与list.sort() (转)
    Python 第三方库 cp27、cp35 等文件名的含义(转)
    Learning to Rank(转)
    Spring MVC异常处理
  • 原文地址:https://www.cnblogs.com/BlogOfchc1234567890/p/9897220.html
Copyright © 2011-2022 走看看