国庆训练10.2

mac2022-06-30  14

国庆训练10.2

开荒第一题题面题解源码 开荒第二题题面题解源码

开荒第一题

tag:简单,数论,费马小定理

题面

1917: [HNOI2008]越狱 时间限制:1秒 内存限制:162MB

题目描述   监狱有连续编号为1…N的N个房间,每个房间关押一个犯人,有M种宗教,每个犯人可能信仰其中一种。如果 相邻房间的犯人的宗教相同,就可能发生越狱,求有多少种状态可能发生越狱

输入   输入两个整数M,N.1<=M<=108,1<=N<=1012

输出   可能越狱的状态数,模100003取余    样例输入

2 3

样例输出

6

提示   6种状态为(000)(001)(011)(100)(110)(111)

题解

每个房间的一个人信仰M个宗教中的一种,则N个房间共有MN种状态,其中不可越狱的状态:第一个房间的人可从M个宗教中选一,剩余N-1个房间的人不能和前一个人的宗教相同(否则会越狱),有(M-1)个宗教选择,共M*(M-1)(N-1)种状态。则最终可越狱的状态:MN- M*(M-1)(N-1)。最终答案:((MN- M*(M-1)(N-1)))%mod。运用费马小定理(ab%p=ab%(p-1)%p)进行降幂,并运用各种求余公式整理得:    (MN%(mod-1)%mod- M%mod*((M-1)(N-1)%(mod-1)%mod)+mod)%mod 最后,对其进行计算即可。

源码

#include <iostream> using namespace std; typedef long long ll; const ll mod=1e5+3; ll fun(ll a,ll b){ ll i; ll ans=1; for(i=0;i<b;i++){ ans*=a; ans%=mod; } return ans; } int main(int argc, char** argv) { ll n,m,a,b,c,d,e; cin>>m>>n; a=n%(mod-1); b=fun(m,a); c=(n-1)%(mod-1); d=fun(m-1,c); m%=mod; d*=m; d%=mod; e=(b-d+mod)%mod; cout<<e; return 0; }

开荒第二题

tag:位运算,矩阵快速幂,状态压缩

题面

5758: [Jsoi2016]位运算 时间限制:20秒 内存限制:512MB

题目描述   JYY最近在研究位运算。他发现位运算中最有趣的就是异或(xor)运算。对于两个数的异或运算,JYY发现了一个结论:两个数的异或值为0当且仅当他们相等。于是JYY又开始思考,对于N个数的异或值会有什么性质呢? 【问题描述】   JYY想知道,如果在0到R-1的范围内,选出N个不同的整数,并使得这N个整数的异或值为0,那么一共有多少种选择的方法呢?(选择的不同次序并不作重复统计,请参见样例)JYY是一个计算机科学家,所以他脑海里的R非常非常大。为了能够方便的表达,如果我们将R写成一个0-1串,那么R是由一个较短的0-1串S重复K次得到的。比如,若S=“101”,K=2,那么R的2进制表示则为“101101”。由于计算的结果会非常大,JYY只需要你告诉他选择的总数对10^9+7取模的结果即可。 输入 第一行包含两个正整数N和K。 接下来一行包含一个由0和1组成的字符串S 我们保证S的第一个字符一定为1。 3<=N<=7,1<=k<=10^5,1<=|S|<=50 输出 一行一个整数,表示选择的方案数对10^9+7取模的值。 样例输入

3 1 100

样例输出

1

提示 对于第一个样例, 唯一的一种选择方法是选择 {1, 2, 3}。

题解

我推出了规律:所有N个数的第i位中‘1’的个数必须为偶数,才能使最终结果为0,但是接下来就没有思路了……   在网上搜了题解:题解链接,但是题解过于简单,代码也没啥注释,也许是wtcl并没有看明白,又研究了好久才搞懂,并把代码加上了注释。   大体思路是这样的:首先,定义了一个结构体用来定义矩阵,其中包括矩阵的初始化(类似于Java的构造方法);然后定义了两个函数(方法):矩阵的乘法和矩阵幂;以上都是辅助性质的。以下为主要内容:为实现N个数不相同,规定A1<A2<……<AN,用trans函数(方法)实现,然后进行状态压缩用lim的二进制值来表示N个数每一位的取值(0 or 1),最后求出一个|s|的结果,再运用矩阵快速幂求出k个|s|的结果。

源码

#include <iostream> #include <cstring> using namespace std; const int mod=1e9+7; int n,k,len,lim,f[55][130]; char s[55]; typedef unsigned long long ull; struct matrix {//矩阵结构体定义 ull a[130][130]; matrix(int t=0) {//初始化一个对角线为t,其他位置为0的矩阵 memset(a,0,sizeof(a)); for(int i=0;i<=lim;i++) a[i][i]=t; } }A; matrix fun(matrix a,matrix b) {//矩阵乘法 matrix c(0); for(int i=0;i<=lim;i++) for(int k=0;k<=i;k++) if(a.a[i][k]) for(int j=0;j<=k;j++) ((c.a[i][j]+=(ull)a.a[i][k]*b.a[k][j]) >= mod) ? c.a[i][j]%=mod : 0; for(int i=0;i<=lim;i++) for(int j=0;j<=i;j++) (c.a[i][j]>=mod) && (c.a[i][j]%=mod); return c; } matrix power(matrix a,int b){//矩阵快速幂 matrix rs(1); for(;b;b>>=1, a=fun(a,a)) if(b&1) rs=fun(rs,a); return rs; } int trans(int pre,int now,int pos) {//判断每一位的N个数是否按递增排序 int c=0; for(int j=n-1;j;j--) if((pre>>j)&1) { int a=(now>>j)&1, b=(now>>(j-1))&1; if(a>b) return -1;//此时~-1=0 if(a==b) c|=(1<<j); } if(pre&1) { int tl=0; while(tl<n && (pre>>tl)&1) if((now>>tl)&1 && s[pos]=='0') return -1;//此时~-1=0 else ++tl; if((now&1)==s[pos]-'0') c|=1; } return c; } int main(int argc, char** argv) { cin>>n>>k>>(s+1); len=strlen(s+1); lim=(1<<n)-1;//状态压缩:用来表示n个数每一位的取值 for(int i=0;i<=lim;i++) { memset(f,0,sizeof(f)); f[0][i]=1; for(int j=1;j<=len;++j) for(int pre=0;pre<=lim;++pre) //之前的状态 for(int now=0;now<=lim;++now) { //现在要放的状态 if(__builtin_popcount(now)&1) continue; // __builtin_popcount获取now的二进制数中1的个数,只有1的个数为偶数时才可能取异或为0 int t=trans(pre,now,j);//判断N个数的第j位是否递增 if(~t) f[j][t]=(f[j][t]+f[j-1][pre])%mod;//t=-1时,~t=0,不进行 } for(int j=0;j<=lim;j++)//获取最终结果 A.a[i][j]=f[len][j]; } matrix B=power(A,k);//进行矩阵快速幂 int ans=B.a[lim][0]%mod;//得出最后结果 cout<<ans; return 0; }
最新回复(0)