0x33 同余
同余类+剩余系+费马小定理+欧拉定理及推论
最幸运的数字
现在给定一个正整数L,请问至少多少个8连在一起组成的正整数
(即最小幸运数字)是L的倍数。
输入格式
输入包含多组测试用例。
每组测试用例占一行,包含一个整数L。
当输入用例L=0时,表示输入终止,该用例无需处理。
输出格式
每组测试用例输出结果占一行。
结果为“Case 1: ”+一个整数N,N代表满足条件的最小幸运数字的位数。
如果满足条件的幸运数字不存在,则N=0。
数据范围
1≤L≤2∗109
输入样例:
8
11
16
0
输出样例:
Case 1: 1
Case 2: 2
题解
这次的代码很多东西:欧拉函数快速求解,gcd,快速乘,各种定理,建议当模板背
10LL 转换成长整型
快速乘+快速幂(取模)模板
技巧:试除法可以一半一半求,减少遍历次数,两半都用稠密的一半遍历(因子是成对的)
#include"stdio.h"
#include"string.h"
#include"math.h"
#include"algorithm"
using namespace std;
typedef long long ll;
ll L;
ll gcd(ll a,ll b)
{
if(a < b)
{
ll t = a; a = b; b = t;
}
if(b == 0)
return a;
return gcd(b,a%b);
}
ll multi(ll a,ll b,ll mod)
{
ll ans = 0;
while(b)
{
if(b & 1)
ans = (ans + a) % mod;
a = (a << 1) % mod;
b = b >> 1;
}
return ans;
}
ll Ola(ll n)
{
ll sum = n;
for(int i = 2; i * i<= n; i ++)
{
if(n % i)
continue;
sum = sum / i * (i - 1);
while(n % i == 0)
n = n / i;
}
if(n != 1)
sum = sum / n * (n - 1);
return sum;
}
int Quick(ll a,ll b,ll mod)
///a:10LL b:欧拉函数的因子,也就是次数 mod:9L/d
///这个就是快速幂,套了快乘的快速幂(因为mod可能很大)
{
ll ans = 1; a %= mod;///a与n互质
///a %= mod;不加也能过
while(b)
{
if(b & 1)
ans = multi(ans,a,mod);
b >>= 1;
a = multi(a,a,mod);
}
if(ans == 1)
return 1;
return 0;
}
ll solve()
{
ll g = L / gcd(L,8LL) * 9;
///LL转换,g是9L除以d
if(gcd(10LL,g) != 1) return 0;
///a,n互质才有解
ll sum = 1;
ll num = Ola(g); ///把g的欧拉函数求出来
///下面这个for是试除法求因子
for(int i = 1; i * (ll)i <= num; i ++)
{
if(num % i) continue;
///是因子就看看能不能余g
///这个quick有个10LL的参数应该也是模板
if(Quick(10LL,(ll)i,g) == 1)
return i;
}
///依旧是试除法求因子,不过求的是大于根号的这一半(因为小于根号这一半较小嘛,更好求)
ll m = sqrt(num);
for(int i = m; i >= 1; i --)
{
if(num % i == 0 && Quick(10LL,num / i,g) == 1)
return num / i;
}
return 0;
}
int main()
{
int cnt = 1;
while(~scanf("%lld",&L))
{
if(L == 0) break;
printf("Case %d: %lld\n",cnt ++,solve());
}
}
扩展欧几里得算法
ax+by=gcd(a,b)存在至少一组整数解xy
乘法逆元
sumdiv
新的逆元做法,下次再做吧
线性同余方程
同余方程
求关于x的同余方程 ax ≡ 1(mod b) 的最小正整数解。
输入格式
输入只有一行,包含两个正整数a,b,用一个空格隔开。
输出格式
输出只有一行,包含一个正整数x,表示最小正整数解。
输入数据保证一定有解。
数据范围
2≤a,b≤2∗109
输入样例:
3 10
输出样例:
7
就是exgcd的模板题
#include <iostream>
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll a,b,x,y;
ll exgcd(ll a,ll b,ll &x,ll &y)
{
if(b==0){
x=1,y=0;
return a;
}
int d=exgcd(b,a%b,x,y);
int z=x;x=y;y=z-y*(a/b);
return d;
}
int main()
{
cin>>a>>b;
exgcd(a,b,x,y);
cout<<(x%b+b)%b<<endl;
return 0;
}
中国剩余定理
表达整数的奇怪方式
给定 2n 个整数a1,a2,…,an和m1,m2,…,mn,求一个最小的非负整数 x,满足∀i∈[1,n],x≡mi(mod ai)。
输入格式
第1 行包含整数 n。
第 2..n+1行:每 i+1 行包含两个整数ai和mi,数之间用空格隔开。
输出格式
输出最小非负整数 x,如果 x 不存在,则输出 −1。
如果存在 x,则数据保证 x 一定在64位整数范围内。
数据范围
1≤ai≤231−1,
0≤mi<ai
1≤n≤25
输入样例:
2
8 7
11 9
输出样例:
31
思路
看题解更好些
没法一口气求解,只能一个一个往上增加
#include <iostream>
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int n;
LL exgcd(LL a, LL b, LL &x, LL &y){
///exgcd
if(b == 0){
x = 1, y = 0;
return a;
}
LL d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
LL inline mod(LL a, LL b){
///防止负数的取模(最小化到0~b-1)
return ((a % b) + b) % b;
}
int main()
{
cin>>n;
LL a1,m1;
cin>>a1>>m1;
for(int i=1;i<n;i++)
///每输入一组都要合并
{
LL a2,m2,k1,k2;
cin>>a2>>m2;
LL d=exgcd(a1,-a2,k1,k2);
///
if((m2-m1)%d){
///不是因数(不能整除),无解
cout<<-1<<endl;
return 0;
}
k1=mod(k1*(m2-m1)/d,abs(a2/d));
m1=k1*a1+m1;///产生新的a1和m1
a1=abs(a1/d*a2);
}
cout<<m1<<endl;
return 0;
}
0x41并查集
这个东西很适合搞图的连通性==“擅长维护传递性”
模板
int fa[maxn];
void init()
{
for(int i=0;i<maxn;i++)
{
fa[maxn]=i;
}
}
int get(int x)
{
if(x==fa[x])return x;
return fa[x]=get(fa[x]);
}
int mergefa(int x,int y)
{
fa[get(x)]=get(y);
}
程序自动分析
标准的并查集题
不想离散化,所以map版并查集,但会有一组TLE
#include <iostream>
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define maxn 1000005
map<int,int> fa;
int n;
int xi[maxn];
int yi[maxn];
int ei[maxn];
int get(int x)
{
if(fa[x]==0||x==fa[x])
///这个if应该是短路运算符
///不过不加fa[x]==0||也行,已经把赋初值拆分了
///map没赋过值的为0
{
fa[x]=x;///给没赋过值的准备的
return x;
}
return fa[x]=get(fa[x]);
}
void mergefa(int x,int y)
{
fa[get(x)]=get(y);
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
cin>>t;
while(t--)
{
fa.clear();///初始化要做好
int n;
cin>>n;
for(int i=0;i<n;i++)
{
cin>>xi[i]>>yi[i]>>ei[i];
}
///先算一遍等于号的
for(int i=0;i<n;i++)
{
int x=xi[i];
int y=yi[i];
if(ei[i]==1)
{
if(fa[x]==0)
{
fa[x]=x;///赋初值拆分进来
}
if(fa[y]==0)
{
fa[y]=y;///赋初值拆分进来
}
//
mergefa(x,y);
}
}
///不等于的
int flag=1;
for(int i=0;i<n;i++)
{
int x=xi[i];
int y=yi[i];
if(ei[i]==0)
{
if(fa[x]==0)
{
fa[x]=x;///赋初值拆分进来
}
if(fa[y]==0)
{
fa[y]=y;///赋初值拆分进来
}
if(get(x)==get(y))
{
flag=0;
break;
}
else{
continue;
}
}
}
if(flag)
{
cout<<"YES"<<endl;
}
else{
cout<<"NO"<<endl;
}
}
return 0;
}
在map基础上,把相等的和不等的分开存储,空间*2,时间/2,牛客能过了,acwing多过了几条数据,但还是7组
#include <iostream>
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define maxn 1000005
map<int,int> fa;
int n;
int xi[maxn];
int yi[maxn];
int xp[maxn];
int yp[maxn];
int get(int x)
{
if(x==fa[x])
///这个if应该是短路运算符
///不过不加fa[x]==0||也行,已经把赋初值拆分了
///map没赋过值的为0
{
fa[x]=x;///给没赋过值的准备的
return x;
}
return fa[x]=get(fa[x]);
}
void mergefa(int x,int y)
{
fa[get(x)]=get(y);
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
cin>>t;
while(t--)
{
fa.clear();///初始化要做好
int n;
cin>>n;
int e;
int bufx,bufy,bufe;
int cnt1,cnt0;
cnt1=cnt0=0;
for(int i=0;i<n;i++)
{
cin>>bufx>>bufy>>bufe;
if(bufe==1)
{
xi[cnt1]=bufx;
yi[cnt1++]=bufy;
}
if(bufe==0)
{
xp[cnt0]=bufx;
yp[cnt0++]=bufy;
}
}
for(int i=0;i<cnt1;i++)
{
int x=xi[i];
int y=yi[i];
if(fa[x]==0)
{
fa[x]=x;///赋初值拆分进来
}
if(fa[y]==0)
{
fa[y]=y;///赋初值拆分进来
}
//
mergefa(x,y);
}
///不等于的
int flag=1;
for(int i=0;i<cnt0;i++)
{
int x=xp[i];
int y=yp[i];
if(fa[x]==0)
{
fa[x]=x;///赋初值拆分进来
}
if(fa[y]==0)
{
fa[y]=y;///赋初值拆分进来
}
if(get(x)==get(y))
{
flag=0;
break;
}
else{
continue;
}
}
if(flag)
{
cout<<"YES"<<endl;
}
else{
cout<<"NO"<<endl;
}
}
return 0;
}
我透了,改个unordered_map就能过,或者开O2(O2不能过)
2021.3.7复习解法(能过前九个):有个很不错的结构体写法
#include <iostream>
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define maxn 1000005
int fa[maxn];
struct uneq{
int a;
int b;
}uneqs[maxn];
void initfa()
{
for(int i=0;i<maxn;i++)
{
fa[i]=i;
}
}
int getfa(int x)
{
if(fa[x]==x)return fa[x];
return fa[x]= getfa(fa[x]);
}
int mergefa(int a,int b)
{
fa[getfa(a)]=getfa(b);
}
int main()
{
int t;
cin>>t;
while(t--)
{
initfa();
int n;
cin>>n;
int cntuneq=0;
int bufa,bufb,bufc;
while(n--)
{
cin>>bufa>>bufb>>bufc;
if(bufc==1)
{
mergefa(bufa,bufb);
}
if(bufc==0)
{
uneqs[cntuneq++]={bufa,bufb};///新的结构体写法很有趣
}
}
// for(int i=0;i<cntuneq;i++)
// {
// cout<< uneqs[i].a<<uneqs[i].b;
// }
int flag=1;
for(int i=0;i<cntuneq;i++)
{
if(getfa(uneqs[i].a)==getfa(uneqs[i].b))
{
flag=0;
break;
}
}
if(flag)
{
cout<<"YES"<<endl;
}
else{
cout<<"NO"<<endl;
}
}
return 0;
}
supermarket
0x17
扩展域与边带权的并查集
银河英雄传说
虽然dx的变化有一半是写在get里面的,但如果不merge,fa[x]指向的是当前根节点,根节点dx为0,dx+=d【fa【x】】不会发生改变-----------每一次merge,对每个节点只+1次,且不会影响其他同集合的节点
带图的题解
另外有的标准size是关键字,不能用,不过编译
#include <bits/stdc++.h>
using namespace std;
const int N=31000+10;
int fa[N],n,t,i,j,d[N],size[N];//size就是记录个数
int get(int x)
{
if (x==fa[x])
return x;
int root=get(fa[x]);
d[x]+=d[fa[x]];//往下推进
return fa[x]=root;
}
void merge(int x,int y)
{
x=get(x),y=get(y);
fa[x]=y,d[x]=size[y];
size[y]+=size[x];//顺带记录
}
int main()
{
scanf("%d\n",&t);
for(i=1;i<=30000;i++)
fa[i]=i,size[i]=1;
while(t--)
{
char ch=getchar();
scanf("%d %d\n",&i,&j);
if (ch=='M')
{
merge(i,j);
}
else
{
if (get(i)==get(j))
cout<<abs(d[i]-d[j])-1;
else
cout<<"-1";
cout<<endl;
}
}
return 0;
}
银河英雄传说—边权集2021.3.8复习—有注释!
#include <iostream>
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define maxn 100005
int fa[maxn];
int d[maxn];///记录战舰x与fa[x]之间边的权值///初值为0
int sizefa[maxn];/// 记录树的大小,初值1
void initfa()
{
for(int i=0;i<maxn;i++)
{
fa[i]=i;
}
}
int getfa(int x)
{
if(fa[x]==x)return x;
return fa[x]=getfa(fa[x]);
}
int newgetfa(int x)
{
if(fa[x]==x)return x;
int root=getfa(fa[x]);///先存出来,集合代表
d[x]+=d[fa[x]]///树根变成了新的树根,----对边权求和
///到维护前树根的距离+维护前树根到新树根的距离
return fa[x]=root;
}
void mergefa(int a,int b)
{
fa[getfa(a)]=getfa(b);
}
void mergefa(int a,int b)///之前提到了需要+维护前树根到新树根的距离
{ ///那么这个距离需要两个步骤
d[getfa(a)]=fasize(getfa(b));///1.接到尾部,旧树根的边权d[x](到新树根距离)就是目标树的size(边数)
fasize[get(fa(b))]+=fasize[getfa(b)];///2.成为新树,size变大
fa[getfa(a)]=getfa(b);
}
int main()
{
return 0;
}
2021.4.17研究出来的模板
#include <iostream>
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define maxn 1005
int fa[maxn];
int d[maxn];
void initfa()
{
for(int i=1;i<=n;i++)
{
fa[i]=i;
}
}
int getfa(int x)
{
if(fa[x]==x)return x;
///这两行就是维护权值数组
int root=fa[x];
d[x]+=d[fa[x]];
return fa[x]=root;
}
void mergefa(int x,int y)
{
///这行是方便使用
rx=getfa(x);ry=getfa(y);
///这两行是维护权值数组
d[rx]=d[ry]+1;//d[rx]=size(y);
sizem[y]+=sizem[x];
///这行是原来的
fa[x]=ry;
}
}
int main()
{
return 0;
}
奇偶游戏
边权集解法
先将正常数组改成前缀和,变成两个点的奇偶性,这样“权”就可以变成d[x](x与根之间),奇偶性的话,就用抑或来计算,如果已经有了,那么通过dx来计算x与y之间奇偶性,如果没有联通,那么要通过已知x与y之间奇偶性,倒推两个根节点之间的奇偶性,合并,另外数很稀疏,考虑离散化
acwing秦淮河大佬的复现书上代码,思路很棒
#include<bits/stdc++.h>
using namespace std;
const int N=10010<<1;
struct node
{
int l,r,ans;
} q[N];
int a[N],fa[N],d[N],n,m,t_n;
int get(int x)
{
if (x==fa[x])
return x;
int root=get(fa[x]);
d[x]^=d[fa[x]];//异或
return fa[x]=root;
}
inline int read_init()//离散化
{
cin>>n>>m;
for(int i=1;i<=m;i++)
{
char str[5];
scanf("%d%d%s",&q[i].l,&q[i].r,str);
q[i].ans=(str[0]=='e'?0:1);
a[++t_n]=q[i].l-1;
a[++t_n]=q[i].r;
}
sort(a+1,a+1+t_n);
n=unique(a+1,a+1+t_n)-a-1;//去重
}
inline int work()
{
read_init();
for(int i=1;i<=n;i++)
fa[i]=i;
for(int i=1;i<=m;i++)
{
int x=lower_bound(a+1,a+1+n,q[i].l-1)-a;//离散化后要找数
int y=lower_bound(a+1,a+1+n,q[i].r)-a;
int p=get(x),q2=get(y);
if (p==q2)
{
if ((d[x]^d[y])!=q[i].ans)//变量要相等,但是却不相等了.
{
cout<<i-1<<endl;
return 0;
}
}
else
{
fa[p]=q2;//合并merge.两方代码就懒得写函数了,见谅
d[p]^=d[x]^d[y]^q[i].ans;//统统异或
}
}
cout<<m;//数据过于优秀,一个问题都没有
}
int main()
{
work();
return 0;
}
扩展域解法(这个很简单,维护两个域,互相也是联通的,不用算权值了)
//Wan Hong 3.0
//notebook
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
typedef long long ll;
typedef std::pair<ll,ll> pll;
ll read()
{
ll x=0,f=1;
char c=getchar();
while(c<'0'||c>'9')
{
if(c=='-')f=-1;
else c=getchar();
}
while(c>='0'&&c<='9')x=x*10+c-'0',c=getchar();
return f*x;
}
ll min(ll a,ll b)
{
return a<b?a:b;
}
ll max(ll a,ll b)
{
return a>b?a:b;
}
bool umin(ll &a,ll b)
{
if(b<a)return a=b,1;
return 0;
}
bool umax(ll &a,ll b)
{
if(b>a)return a=b,1;
return 0;
}
ll abs(ll x)
{
return x>0?x:-x;
}
/**********/
#define MAXN 100011
struct ufs
{
ll fa[MAXN];
void build(ll n)
{
for(ll i=0;i<=n;++i)fa[i]=i;
}
ll find(ll x)
{
if(fa[x]==x)return x;
return fa[x]=find(fa[x]);
}
void uni(ll u,ll v)
{
u=find(u),v=find(v);
fa[u]=v;
}
bool same(ll u,ll v)
{
return find(u)==find(v);
}
}s;
struct query
{
ll l,r,k;
query(){}
query(ll _l,ll _r,ll _k)
{
l=_l,r=_r,k=_k;
}
}q[MAXN];
ll a[MAXN];
ll cnt=0;
void disc()
{
std::sort(a+1,a+cnt+1);
cnt=std::unique(a+1,a+cnt+1)-(a+1);
}
ll place(ll x)
{
return std::lower_bound(a+1,a+cnt+1,x)-a;
}
int main()
{
ll n=read(),m=read();
for(ll i=1;i<=m;++i)
{
ll l=read()-1,r=read();
char c=getchar();
while(c!='e'&&c!='o')c=getchar();
q[i]=query(l,r,c=='o');
a[++cnt]=l,a[++cnt]=r;
}
disc();
s.build(cnt<<1);//[1,cnt]:x_even;[cnt+1,2cnt]:x_odd
for(ll i=1;i<=m;++i)
{
ll l=place(q[i].l),r=place(q[i].r),k=q[i].k;
ll l_even=l,l_odd=l+cnt,r_even=r,r_odd=r+cnt;
if(!k)
{
if(s.same(l_odd,r_even)||s.same(l_even,r_odd))
{
printf("%lld\n",i-1);
return 0;
}
else s.uni(l_odd,r_odd),s.uni(l_even,r_even);
}
else
{
if(s.same(l_even,r_even)||s.same(l_odd,r_odd))
{
printf("%lld\n",i-1);
return 0;
}
else s.uni(l_even,r_odd),s.uni(l_odd,r_even);
}
}
printf("%lld",m);
return 0;
}
作者:whsstory
链接:https://www.acwing.com/solution/content/4338/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
扩展域解法1.不要忘了初始化2.不要忘了-1 3.扩展域一定要放一个数组里 ------------2021.3.8复习
#include <iostream>
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define maxn 100005
int fa[maxn];///扩展域应该放在同一个数组里使用,x,x+n,x+n+n,x+n+n+n,......这样才能测试是否联通
int d[maxn];
int sizefa[maxn];
void initfa()
{
for(int i=0;i<maxn;i++)
{
fa[i]=i;
}
}
int getfa(int x)
{
if(fa[x]==x)return x;
return fa[x]=getfa(fa[x]);
}
void mergefa(int a,int b)
{
fa[getfa(a)]=getfa(b);
}
int main()
{
initfa();
int n;
cin>>n;
int t;
cin>>t;
int t2=t;
int a,b,op;
while(t--)
{
cin>>a>>b>>op;
if(op==0)///even
{
if(getfa(a-1)==getfa(b+n))///is odd--矛盾
{
cout<<t2-t<<'*';
return 0;
}
mergefa(a-1,b);
mergefa(a-1+n,b+n);
}
else{
if(getfa(a-1)==getfa(b))
{
cout<<t2-t<<"#";
return 0;
}
mergefa(a-1,b+n);
mergefa(a-1+n,b);
}
}
return 0;
}
食物链
拓展域
三个域
//这里我们将三个域,分别转化为了n,n+n,n+n+n.因为读入方面特别烦.
#include <bits/stdc++.h>
using namespace std;
int fa[200000];
int n,m,k,x,y,ans;
int get(int x)
{
if(x==fa[x])
return x;
return fa[x]=get(fa[x]);
}
void merge(int x,int y)
{
fa[get(x)]=get(y);
}
int main()
{
cin>>n>>m;
for(int i=1;i<=3*n;i++)
fa[i]=i;
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&k,&x,&y);
if(x>n || y>n)
ans++;///假话总数+1
else if(k==1)///同类
{
if(get(x)==get(y+n) || get(x)==get(y+n+n))
//如果x,y是同类,但是x是y的捕食中的动物,或者x是y天敌中的动物,那么错误.
ans++;///假话总数+1
else
{
merge(x,y);
merge(x+n,y+n);
merge(x+n+n,y+n+n);
}
}
else///X捕食Y
{
if(x==y || get(x)==get(y) || get(x)==get(y+n))
//x就是y,或者他们是同类,再或者是y的同类中有x
ans++;//都是假话
else
{
merge(x,y+n+n);//y的天敌域加入x
merge(x+n,y);//x的捕食域加入y
merge(x+n+n,y+n);//x的天敌域是y的捕食域.
}
}
}
cout<<ans<<endl;
}
//x是同类域.
//x+n是捕食域
//x+n+n是天敌域
2021.3.8复习:这里的捕食域,同类域,天敌域,通过三个域表达出了方向关系(X被Y捕食,Y是X的天敌):通过X的Xself 与 Y的 Yeat 联通
边带权
把吃,被吃,同类设为 1,2,0,每次维护就好
#include<bits/stdc++.h>
using namespace std;
const int maxn = 5e4 + 233;
int fa[maxn], d[maxn];
int ff(int x)
{
if(fa[x] == x) return x;
int r = ff(fa[x]);
d[x] += d[fa[x]];
return fa[x] = r;
}
int main()
{
int n,k; cin >> n >> k;
for(int i = 0; i <= n; i++) fa[i] = i;
int ans = 0;
for(int i = 1; i <= k; i++)
{
int t, a, b;
scanf("%d%d%d", &t, &a, &b);
if(a > n || b > n) {ans ++; continue;}
else if(t == 2 && a == b) {ans++; continue;}
else
{
int rel;
if(t == 2) rel = 1;
else rel = 0;
int x = ff(a), y = ff(b);
if(x == y)
{
if((((d[a] - d[b]) % 3) + 3) % 3 != rel)
ans++;
}
else
{
fa[x] = y;
d[x] = d[b] - (d[a] - rel);
}
}
}
cout<< ans;
}
作者:这个显卡不太冷
链接:https://www.acwing.com/solution/content/1357/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。