目录
A 、The power of Fibonacci (循环节+中国剩余定理)
B 、Quadratic equation (二次剩余)
D 、Knapsack Cryptosystem (折半搜索)
E 、All men are brothers (并查集)
题意:
分析:
很容易想到循环节。
但是难点是循环节太大了,所以导致不好计算。
但是
根据同余得性质
, ,
那么可以将mod(1e9)拆分,
同时:
不单是斐波那契,循环节都可以拆成两个互质的循环节,再通过中国剩余定理合并(取模前提下),因为%pq的循环节可以通过%p和%q的循环节推导。
#include
#define mm(a,b) memset(a,b,sizeof(a))
#define ACCELERATE (ios::sync_with_stdio(false),cin.tie(0))
#define pii pair
#define pid pair
#define pil pair
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define rush() int T;scanf("%d",&T);while(T--)
#define sc(a) scanf("%d",&a)
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
#define PI acos(-1.0)
#define E exp(1.0)
//#define io
using namespace std;
const int inf=0x3f3f3f3f;
const ll INF=0x3f3f3f3f3f3f3f3f;
//#define gc getchar
char buf[1<<21],*p1=buf,*p2=buf;
inline int gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
inline int read(){
int ret=0,f=0;char c=gc();
while(!isdigit(c)){if(c=='-')f=1;c=gc();}
while(isdigit(c)){ret=ret*10+c-48;c=gc();}
if(f)return -ret;return ret;
}
const ll mod2=512;
const ll mod5=1953125;
const ll xun2=768;
const ll xun5=7812500;
ll f2[xun2+10];
ll f5[xun5+10];
ll quick_pow(ll a,ll b,ll mod){
ll ans=1;
while(b){
if(b&1) ans=ans*a%mod;
b>>=1;
a=a*a%mod;
}
return ans;
}
void exgcd(ll a,ll b,ll &x,ll &y){
if(b==0){
x=1;
y=0;
return;
}
ll tx,ty;
exgcd(b,a%b,tx,ty);
ll tmp=tx;
x=ty;
y=tmp-(a/b)*ty;
}
//中国剩余定理
ll CRT(ll a[],ll m[],ll n){
ll M=1;
ll ans=0;
for(ll i=1;i<=n;i++)
M*=m[i];
for(ll i=1;i<=n;i++){
ll x,y;
ll Mi=M/m[i];
exgcd(Mi,m[i],x,y);
ans=(ans+Mi*x*a[i])%M; //Mi*x%m[i]==1.
}
if(ans<0) ans+=M;
return (ans+M)%M;
}
int main(){
#ifdef io
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
ll n,m;
scanf("%lld%lld",&n,&m);
f2[0]=0,f2[1]=1;
f5[0]=0,f5[1]=1;
for(int i=2;i
题意:
给出 计算
分析:
将上式化简得 所以只要计算
对n是否为mod p的二次剩余的判断,可以用欧拉准则。
然后直接套模板即可。
#include
using namespace std;
const int mod=1e9+7;
typedef long long ll;
int k;
ll a,p,w;
struct T{ll x,y;};
T mul_two(T a,T b,ll p){
T ans;
ans.x=(a.x*b.x%p+a.y*b.y%p*w%p)%p;
ans.y=(a.x*b.y%p+a.y*b.x%p)%p;
return ans;
}
T qpow_two(T a,ll n,ll p){
T ans; ans.x=1; ans.y=0;
while(n){
if(n&1) ans=mul_two(ans,a,p);
n>>=1;
a=mul_two(a,a,p);
}
return ans;
}
ll qpow(ll a,ll n,ll p){
ll ans=1; a%=p;
while(n){
if(n&1) ans=ans*a%p;
n>>=1;a=a*a%p;
}
return ans%p;
}
ll Legendre(ll a,ll p){
return qpow(a,(p-1)>>1,p);
}
int solve(ll n,ll p){
n%=p;
if(p==2) return 1;
if(Legendre(n,p)+1==p) return -1;
else if(n==0) return 0;
ll a=1;
while(Legendre((a*a-n+p)%p,p)+1!=p) a=rand()%p;
T tmp; tmp.x=a; tmp.y=1; w=(a*a-n+p)%p;//W
T ans=qpow_two(tmp,(p+1)>>1,p);
return ans.x;
}
int main(){
scanf("%d",&k);
while(k--){
ll b,c;
scanf("%lld%lld",&b,&c);
ll t=((b*b-c*4)%mod+mod)%mod;
ll d=solve(t,mod);
if(d==-1){
printf("-1 -1\n");
continue;
}
ll x=(b+d)*qpow(2ll,mod-2,mod)%mod,y=(b-x+mod)%mod;
if(x>y) swap(x,y);
printf("%lld %lld\n",x,y);
}
return 0;
}
题意:
给出n个数,最大是36,然后给出一个和,计算这些数中哪些数可以构成这个和,然后输出对应的二进制序列。
分析:
直接进行枚举范围太大,可以将数分为两半,然后枚举两边的所有和,存起来,用和减去左边的然后在右边进行查找即可。
#include
#define mm(a,b) memset(a,b,sizeof(a))
#define ACCELERATE (ios::sync_with_stdio(false),cin.tie(0))
#define pii pair
#define pil pair
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
#define PI acos(-1.0)
#define E exp(1.0)
//#define io
using namespace std;
const int inf=0x3f3f3f3f;
const ll INF=0x3f3f3f3f3f3f3f3f;
map ma1;
map ma2;
ll a[50];
int main(){
#ifdef io
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
int n,m;
ll sum;
scanf("%d%lld",&n,&sum);
m=n>>1;
for(int i=1;i<=n;i++){
scanf("%lld",&a[i]);
}
for(int i=1;i<(1<>=1;
}
cnt=m;
tmp=ma2[sum-res];
while(cnt--){
if(tmp&1) printf("1");
else printf("0");
tmp>>=1;
}
printf("\n");
break;
}
}
return 0;
}
题意:
给出n个数字,刚开始每个数字在一个集合,每次操作后计算当前每个集合中取一个人一共取4个人的不同的情况数。
分析:
举个例子,用6个人来看,合并前后的情况(将1 6合并)为:
1 2 3 4 | 1 2 3 4 |
1 2 3 5 | 1 2 3 5 |
1 2 3 6 | 1 2 4 5 |
1 2 4 5 | 1 3 4 5 |
1 2 4 6 |
2 3 4 5 |
1 3 4 5 | |
1 3 4 6 | |
1 3 5 6 | |
1 4 5 6 | |
2 3 4 5 | |
2 3 4 6 | |
2 3 5 6 | |
2 4 5 6 | |
3 4 5 6 | |
1 2 5 6 |
那么明显可以发现的是减少的列就是左边既包含1又包含6的列。那么将这部分剪掉就好了。
剪掉的部分是他俩的乘积乘上其余任意两部分乘积的和,
直接计算其余各部分的任意两部分的和可能不好计算,那么我们考虑,事先计算好每两个的乘积然后每次进行维护就好了。
再列一个前后两两乘积的表就会很好的发现, 在这里就不咧了。
#include
#define mm(a,b) memset(a,b,sizeof(a))
#define ACCELERATE (ios::sync_with_stdio(false),cin.tie(0))
#define pii pair
#define pil pair
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
#define PI acos(-1.0)
#define E exp(1.0)
//#define io
using namespace std;
const int inf=0x3f3f3f3f;
const ll INF=0x3f3f3f3f3f3f3f3f;
const int maxn=1e5+10;
int f[maxn];
ll num[maxn];
int find(int x){
return x==f[x]?x:f[x]=find(f[x]);
}
int main(){
#ifdef io
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
int n,m;
scanf("%d%d",&n,&m);
ll ans=1ll*n*(n-1)/4*(n-2)/3*(n-3)/2;
printf("%lld\n",ans);
for(int i=0;i<=n;i++){
f[i]=i;num[i]=1ll;
}
ll sum=1ll*n*(n-1)/2;
while(m--){
int a,b;
scanf("%d%d",&a,&b);
int fa=find(a),fb=find(b);
if(fa!=fb){
int tot=n-num[fa]-num[fb];
ans-=1ll*num[fa]*num[fb]*(sum-tot*num[fa]-tot*num[fb]-num[fa]*num[fb]);
sum-=num[fa]*num[fb];
f[fa]=fb;
num[fb]+=num[fa];
}
printf("%lld\n",max(0ll,ans));
}
return 0;
}