$$ \newcommand{\seq}[2]{{#1}_{1},{#1}_{2},\cdots,{#1}_{#2}} \newcommand{\num}[1]{1,2,\cdots,#1} \newcommand{\stra}[2]{\begin{bmatrix}#1 \\ #2\end{bmatrix}} \newcommand{\strb}[2]{\begin{Bmatrix}#1 \\ #2\end{Bmatrix}} \newcommand{\dw}[1]{\underline{#1}} \newcommand{\up}[1]{\overline{#1}} $$

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,T\le 500,n\le 1000000,k\le 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\n",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,1\le m,k,x<998244353,1\le T\le 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",n-m);
            else printf("%lld\n",n-(w-1));
        }
        else{
            if(m<=(w>>1))printf("%lld\n",n-m);
            else printf("%lld\n",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

数据范围

\(n\le 1500,m\le 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;
} 
posted @ 2018-11-02 17:00  chc_1234567890  阅读(364)  评论(0编辑  收藏  举报