标签:double turn ems swap www load 重复 trie win32
贝祖定理可以推广到n个,n>=2
ax+by+cz+...=k
if(k%gcd(a,b,c,...)==0)
该式子有整数解
else
没有整数解
int n;
const int N=3e7+10;
int v[N],p[N],tot_p;
long long sum;
inline void shai(){
rep(i,2,n){
if(v[i]==0){
v[i]=i;
p[++tot_p]=i;
}
rep(j,1,tot_p){
if(v[i]<p[j] || p[j]>N/i)break;
v[i*p[j]]=p[j];
}
sum+=(long long)v[i];
}
}
int main(){
#ifdef WIN32
freopen("a.txt","r",stdin);
#endif
rd(n);
shai();
// rep(i,1,n)printf("%d ",sum[i]);
printf("%lld\n",sum);
// printf("%.2lf",(double)sizeof(v)/(1<<20));
return 0;
}
刚刚学到的众所周知的科技可以 O(n+log?p)离线求出任意 n个数在模 p意义下的逆元。
首先计算 n个数的前缀积,记为 \(s_i\),然后使用快速幂或扩展欧几里得法计算 \(s_n\) 的逆元,记为 \(sv_n\)。
因为\(sv_n\) 是 n 个数的积的逆元,所以当我们把它乘上 \(a_n\) 时,就会和 \(a_n\) 的逆元抵消,于是就得到了 a1 到\(a_{n-1}\)的积逆元,记为 \(sv_{n-1}\)。
同理我们可以依次计算出所有的 \(sv_i\)。
于是 \(a_i^{-1}\) 就可以用 \(s_{i-1}\)×\(sv_i\) 求得。
int n,m;
const int N=5e6+10,mod=1e9+7;
int inv[N],sum[N],a[N];
inline int ksm(int a,int k,int mod){
int res=1;
for(;k;k>>=1){
if(k&1)res=(res*a)%mod;
a=(a*a)%mod;
}
return res%mod;
}
#undef int
int main(){
#define int long long
#ifdef WIN32
freopen("c.txt","r",stdin);
#endif
rd(n);
rep(i,1,n)rd(a[i]);
sum[0]=1;
rep(i,1,n)sum[i]=sum[i-1]*a[i]%mod;
inv[n]=ksm(sum[n],mod-2,mod);
dwn(i,n-1,1)
inv[i]=inv[i+1]*a[i]%mod;
int ans=0;
rep(i,1,n)
ans=(ans*998244353%mod+inv[i]%mod*sum[i-1]%mod)%mod;
printf("%lld\n",ans);
return 0;
}
inline int exgcd(int a,int b,int &x,int &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-(a/b)*y;
return d;
}
int A1=a[1],B1=b[1];
rep(i,2,n){
int A2=a[i],B2=b[i];
int deltaA=((A2-A1)%B2+B2)%B2;
int d=exgcd(B1,B2,x,y);
x=ksc(x,deltaA/d,B2);
A1+=B1*x;
B1*=B2/d;
(A1+=B1)%=B1;
}
?
线性基
设数集T的值域范围为[1,\(2^n\)?1]
T的线性基是T的一个子集A={a1,a2,a3,...,an}
A中元素互相xor所形成的异或集合,等价于原数集T的元素互相xor形成的异或集合。
可以理解为将原数集进行了压缩。
1.设线性基的异或集合中不存在0。
2.线性基的异或集合中每个元素的异或方案唯一,其实这个跟性质1是等价的。
3.线性基二进制最高位互不相同。
4.如果线性基是满的,它的异或集合为[1,\(2^n\)?1]
5.线性基中元素互相异或,异或集合不变。
根据性质3。
我们要将线性基改造成每一位相互独立。
具体操作就是如果j<i,ai的第j位是1,就将ai异或上aj。
经过一系列操作之后,对于二进制的某一位i。只有ai的这一位是1,其他都是0。
所以查询的时候将k二进制拆分,对于1的位,就异或上对应的线性基。
最终得出的答案就是k小值。
const int I=50+5;
int a[50+5],tmp[50+5];
int n,x;
bool flag;//判断能不能表示出0
inline void insert(int x){
dwn(i,I,0)
if(x&(1ll<<i))
if(!a[i]){a[i]=x;return;}
else x^=a[i];
flag=true;
}
inline bool check(int x){
dwn(i,I,0)
if(x&(1ll<<i))
if(!a[i])return false;
else x^=a[i];
return true;
}
inline int qmax(){
int res=0;
dwn(i,I,0)
res=max(res,res^a[i]);
return res;
}
inline int qmin(){
if(flag)return 0;
rep(i,0,I)
if(a[i])return a[i];
}
inline int query(int k){
int cnt=0;
k-=flag;
if(!k)return 0;
rep(i,0,I){
dwn(j,i-1,0)
if(a[i]&(1ll<<j))a[i]^=a[j];
if(a[i])tmp[cnt++]=a[i];
}
if(k>=(1ll<<cnt))return -1;
int res=0;
rep(i,0,cnt-1)
if(k&(1<<i))res^=tmp[i];
return res;
}
#undef int
int main(){
#define int long long
rd(n);
rep(i,1,n){
rd(x);insert(x);
}
printf("%lld\n",qmax());
return 0;
}
可以说是树上路径最大异或和模板题。
考虑到两个序列合并的线性基就是它们的线性基的合并,这给我们解决问题带来了新的思路,通过这个思路可以解决区间线性基的问题。这里对于一棵树,我们对它进行树链剖分,用线段树维护一段区间的线性基,查询时合并这些线性基。
经过合并后可以得到树上路径线性基,求它的最大值即可。不过这种方法复杂度很高,树剖和线段树都是\(logn\),线性基合并是\(log^2n\),达到\(O(nlog^4n)\)的复杂度。
好像还可以用点分治,复杂度比较低,不过我不会。
/*
reference:
https://www.luogu.org/blog/JustinRochester/solution-p2158
translation:
solution:
trigger:
gcd(a,b)==1 才能被看见,记录gcd(a,b)==1 的点对个数,直接用gcd会t,枚举每个横坐标,
线性求纵坐标与之互素的欧拉函数,累加答案即可。
note:
*欧拉筛、线性筛素数、分治
date:
2019.08.10
*/
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define rep(i,a,b) for(ll i=a;i<=b;++i)
#define dwn(i,a,b) for(ll i=a;i>=b;--i)
template <typename T> inline void rd(T &x){x=0;char c=getchar();int f=0;while(!isdigit(c)){f|=c=='-';c=getchar();}while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}x=f?-x:x;}
#define mem(a,b) memset(a,b,sizeof(a))
#define N 40010
bool ip[N];
int n;
int p[N],phi[N],sum[N];
void Euler(){
mem(ip,1);
ip[1]=0;
phi[1]=1;
sum[1]=1;
int tot=0;
rep(i,2,N){
if(ip[i]){
p[++tot]=i;
phi[i]=i-1;
}
for(int j=1;j<=tot && i*p[j]<=N;++j){
ip[i*p[j]]=0;
if(!(i%p[j])){
phi[i*p[j]]=phi[i]*p[j];
break;
}
phi[i*p[j]]=phi[i]*phi[p[j]];
}
sum[i]=sum[i-1]+phi[i];
}
}
int main(){
Euler();
rd(n);
if(n==1)printf("0\n");
else printf("%d\n",2*sum[n-1]+1);//加的“1”是(2,2)
return 0;
}
这种方法可以找到路径。
inline void work(){
dfs(1,0);//0号节点是虚点,编号要从1开始编
int x=1;
rep(i,2,n)
if(deep[i]>deep[x])
x=i;
dfs(x,0);
int y=1;
rep(i,2,n)
if(deep[i]>deep[y])
y=i;
printf("%d %d",x,y);//直径的俩端点
printf("%d",deep[y]);//直径
}
类似于圆的圆心,树的中心是该点到其他点的最远距离最小
首先通过上一段代码求出树的直径,再写一段lca(用树剖好 了)。
int work(){
int deep_lca=deep[lca(u,v)];
int len=deep[x]+deep[y]-2*deep_lca;
if(deep[x]-deep_lca>len/2){
rep(i,1,len/2)u=fa[u];
return u;
}
else{
rep(i,1,len-len/2)v=fa[v];
return v;
}
}
int len=0;
inline int dfs(int u,int fa){
int max1=0,max2=0;
ee(i,u){
int v=e[i].v;
int tmp=dfs(v,u);
if(tmp>max1)max2=max1,max1=tmp;
else if(tmp>max2)max2=tmp;
}
len=max(len,max1+max2);
return max1;
}
? 首先认识什么是重心。
? 对于一棵无根树,我们需要选一个点作为根节点将其转化为有根树。如果以某一个点为根可以使得其最大的子树结点数最小,那么这个点称为树的重心。重心可以有多个。
? 以重心为根可以得到尽可能平衡的有根树,下面是重心的其它性质:
树中所有点到某个点的距离和中,到重心的距离和是最小的。有两个重心时,它们的距离和一样。
把两个树通过一条边相连得到一个新的树,新的树的重心在连接原来两个树的重心的路径上。
把一个树添加或删除一个叶子,那么它的重心最多只移动一条边的距离。
\846. 树的重心 表示重心的所有的子树中最大的子树的结点数目。
int n,ans=INT_MAX;
int siz[N],son[N];
inline void dfs(int u,int fa){
siz[u]=1;
ee(i,u){
int v=e[i].v;
if(v==fa)continue;
dfs(v,u);
siz[u]+=siz[v];
if(siz[v]>siz[son[u]])
son[u]=v;
}
int max_size=max(siz[son[u]],n-siz[u]);
ans=min(ans,max_size);//求重心具体是谁:如果ans被更新了就记录一下u
}
int main(){
#ifdef WIN32
freopen("b.txt","r",stdin);
#endif
rd(n);
rep(i,1,n-1){
int u,v;rd(u),rd(v);
adde(v,u),adde(u,v);
}
dfs(1,0);
printf("%d",ans);
return 0;
}
考虑每条边的贡献:e[i].w*abs(siz[v]-(n-siz[v]));
? 当a与p互质时,可以用扩展欧几里得算法求出一个特解x、y使得ax+bp=1ax+bp=1,也就是ax≡1(mod p)ax≡1(mod p),这样x就是逆元。本方法只需要a与p互质(这也是逆元存在的条件),缺点是求出的x可能为负数。
if(s1[i]==s2[j] && s1[i+1]==s2[j-1])
int ans;
char s1[233],s2[233];
int main(){
scanf("%s",s1+1);
scanf("%s",s2+1);
int len=strlen(s1+1);
rep(i,1,len-1)
rep(j,2,len)
if(s1[i]==s2[j] && s1[i+1]==s2[j-1])
ans++;
printf("%d",1<<ans);
return 0;
}
n个城市之间有m条双向路。每条路要耗费一定的油量。
每个城市的油价是固定并且已经给出的。有q个询问,表示从城市s走到e,油箱的容量为c,求最便宜的方案。
SOL:
dis(i,j)表示走到城市i,剩余油量为j的最小花费。然后用优先队列更新,优先更新花费小的状态。
==【信压】使用分层最短路的问题:一个状态可以转化为多种状态并且其中一种或几种贡献为0,一种或几种有贡献,并求贡献的属性(Min/Max...)==
一看到dis[i] [j]这样的状态表示,马上想到分层图最短路,有点类似DP的思想
按油量分层,dis[i] [j]表示到节点i还有j个油的最小花费(不是最短路)
两种决策,加一个油或者直接走
关于运算符的重载:
struct point{
int x;
int y;
int times;
friend bool operator < (point a, point b)
{
return a.times > b.times; //重载小于号使得小的先出队列
}
};
在此处定义一个优先队列priority_queue
如果要按照以times进行从小到大排列,操作如上。
进行重载<操作符。
意思是如果a.times > b.times成立,那么结构体point a < point b成立。由于优先队列是按照从大到小排列,所以结构体b会排列到a之前,然而b.times是最小的,所以实现了按照times的从小到大排序,其实用一句话说就是要想b更大那么b.times在结构体中比较时需要进行运算符的重载(重载<),在不需要结构体时:
priority_queue<int,vector<int>,less<int>>s;//定义优先级队列s,less表示按照递减(从大到小)的顺序插入元素
priority_queue<int,vector<int>,greater<int>>s;//定义优先级队列s,greater表示按照递增(从小到大)的顺序插入元素
const int N=1e3+10,M=1e4+10,V=1e2+10;
int dis[N][V];//dis[i][j]到i号点还剩j升油的最小花费
bool vis[N][V];//判断此点有没有更新过
int n,m,q;
int p[N];
struct edge{
int v,w,next;
}e[M<<1];
int head[N],edge_num;
inline void adde(int u,int v,int w){
e[++edge_num].v=v;
e[edge_num].w=w;
e[edge_num].next=head[u];
head[u]=edge_num;
}
struct node{
int id,cost,fuel;
bool operator <(const node &rhs)const{
return cost>rhs.cost;
}
};
inline void dij(int c,int s,int t){
mem(dis,0x3f);
mem(vis,0);
priority_queue<node>q;
dis[s][0]=0;
q.push((node){s,0,0});
while(!q.empty()){
node st=q.top();q.pop();
int u=st.id,fuel=st.fuel,cost=st.cost;
if(vis[u][fuel])continue;
vis[u][fuel]=1;
if(u==t){
printf("%d\n",cost);
return ;
}
//加一升油
if(fuel+1<=c && dis[u][fuel+1]>dis[u][fuel]+p[u]){
dis[u][fuel+1]=dis[u][fuel]+p[u];
q.push((node){u,dis[u][fuel+1],fuel+1});
}
//不加油,直接开到下一条边。
ee(i,u){
int v=e[i].v,w=e[i].w;
if(fuel>=w && dis[v][fuel-w]>cost){
dis[v][fuel-w]=cost;
q.push((node){v,cost,fuel-w});
}
}
}
printf("impossible\n");
}
int main(){
#ifdef WIN32
freopen("b.txt","r",stdin);
#endif
rd(n),rd(m);
rep(i,0,n-1)rd(p[i]);//注意点是从0开始的!阿伟debug了好久
rep(i,1,m){
int u,v,w;rd(u),rd(v),rd(w);
adde(u,v,w),adde(v,u,w);
}
rd(q);
while(q--){
int c,s,t;rd(c),rd(s),rd(t);
dij(c,s,t);
}
return 0;
}
#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define dwn(i,a,b) for(int i=(a);i>=(b);--i)
template <typename T>inline void rd(T &x){
x=0;char c=getchar();int flag=0;
while(!isdigit(c)){flag|=c=='-';c=getchar();}
while(isdigit(c)){x=x*10+(c^48);c=getchar();}
x=flag?-x:x;
}
#define mem(a,b) memset(a,b,sizeof(a))
#define ee(i,u) for(int i=head[u];i;i=e[i].next)
/******************************************** Header Template **********************************************/
int n,m,ans;
const int N=110,M=1e4+10;
int s[N],sg[M];
inline int SG(int x){
if(sg[x]!=-1)return sg[x];
unordered_set<int>S;
rep(i,1,m){
if(x>=s[i])
S.insert(SG(x-s[i]));
}
for(int i=0; ;++i)
if(!S.count(i))
return sg[x]=i;
}
int main(){
rd(m);
rep(i,1,m)rd(s[i]);
rd(n);
mem(sg,-1);
rep(i,1,n){
int x;rd(x);
ans^=SG(x);
}
if(ans==0)printf("No");
else printf("Yes");
return 0;
}
const int N=110;
const double eps=1e-6;
int n;
int a[N][N];
int main(){
rd(n);
rep(i,1,n)
rep(j,1,n+1)
rd(a[i][j]);
rep(i,1,n){
int max=i;
rep(j,i+1,n)
if(a[j][i])
max=j;
rep(k,1,n+1)
swap(a[max][k],a[i][k]);
rep(j,1,n)if(i!=j){
if(a[j][i]){
rep(k,i,n+1)
a[j][k]^=a[i][k];
}
}
}
rep(i,1,n){
if(a[i][i]==0){
if(a[i][n+1]){
puts("No solution");
exit(0);
}
else{
puts("Multiple sets of solutions");
exit(0);
}
}
}/*
rep(i,1,n){
rep(j,1,n+1)
printf("%d ",a[i][j]);
puts("");
}*/
rep(i,1,n)
printf("%d\n",a[i][n+1]);
return 0;
}
int n;
int a[110][110];
int main(){
rd(n);
rep(i,1,n)
rep(j,1,n+1)
rd(a[i][j]);
rep(i,1,n){
int max=i;
rep(j,i+1,n)
if(a[j][i]==1)
max=j;
rep(k,1,n+1)
swap(a[max][k],a[i][k]);
rep(j,1,n)if(i!=j){
if(a[j][i])//当前元对a[i][i]有影响
rep(k,i,n+1)
a[j][k]^=a[i][k];//对j的这组方程做逆运算,^的逆运算就是^
}
}
rep(i,1,n){
if(a[i][i]==0){
if(a[i][n+1]==1){
puts("No solution");
exit(0);
}
else {
puts("Multiple sets of solutions");
exit(0);
}
}
}
rep(i,1,n)
printf("%d\n",a[i][n+1]);//不用写/a[i][i]。因为a[i][i]==0的时候上面已经判断过了
return 0;
}
const int N=110;
int sg[N],a[N];
int n;
inline int SG(int x){
if(sg[x]!=-1)return sg[x];
unordered_set<int>S;//开一个哈希表
rep(i,0,x-1)
rep(j,0,i)
S.insert(SG(i)^SG(j));//当前可能变成两个局面,这俩局面先异或一下
for(int i=0; ;++i)
if(!S.count(i))
return sg[x]=i;
}
int main(){
rd(n);
int ans=0;
mem(sg,-1);
rep(i,1,n){
int x;rd(x);
ans^=SG(x);
}
if(ans)puts("Yes");
else puts("No");
}
必胜态:2
必败态:1
先手要确保自己能够率先拿到2,那就必须确保后手能够分出偶数来。
至此,我们已经分析出来了,先手如果能保证后手每次都拿到奇数(说明先手手里的数是偶数,他就会拆成奇数+奇数)就会胜利,==先手创造一个必败态给后手,而后手没有办法创造一个必败态给先==手(先手一定会选划分出来的偶数态)
==这个模型就是在攻占偶数态。==
答案就是
if(n&1)后手胜
else 先手胜
单个数时间复杂度O(\(\sqrt{x}\))
bool is_prime(int x){
if(x==1)return 0;
if(x==2 || x==3)return 1;
if(x%6!=1 && x%6!=5)return 0;
for(int i=5;i*i<=x;i+=6){
if(x%i==0 || x%(i+2)==0)return 0;
}
return 1;
}
一开始我还在算a<=2e9,一共有约1e8个质数,p[] 和 c[]的数组怎么开啊……难道要用unordered_map?哎呀,其实边分解就可以边输出啦。笨笨。
inline void divide(int x){
for(int i=2;i*i<=x;++i){
if(x%i==0){
int cnt=0;
while(x%i==0){
x/=i;
cnt++;
}
printf("%d %d\n",i,cnt);
}
}
if(x>1)printf("%d %d\n",x,1);
}
这不是欧拉函数啊,姐姐,phi[i]是1~i-1的所有与i互质的数,而不是质数,姐姐。
直接输出tot_p就得了
==不开long long见祖宗==
n<=1e6
对于要输出1和本身的情况,一定不能写while(x%i==0)x/=i;因为i ==1的时候会死循环,因为为了节省时间复杂度,i和x/i是一起求的,所以还是要sort一遍再输出。
int n;
const int N=1e6+10;
int p[N];
inline void divide(int x){
mem(p,0);
int cnt=0;
for(int i=1;i*i<=x;++i){
if(x%i==0){
p[++cnt]=i;
if(i*i!=x)
p[++cnt]=x/i;
}
}
sort(p+1,p+cnt+1);
rep(i,1,cnt)printf("%d ",p[i]);
}
int main(){
rd(n);
rep(i,1,n){
int x;rd(x);
divide(x);
puts("");
}
return 0;
}
int n;
const int mod=1e9+7;
map<int,int>p;
inline void divide(int x){
for(int i=2;i*i<=x;++i){
while(x%i==0){
p[i]++;
x/=i;
}
}
if(x>1)p[x]++;
}
#undef int
int main(){
#define int long long
rd(n);
rep(i,1,n){
int x;rd(x);
divide(x);
}
int ans=1;
for(map<int,int>::iterator it=p.begin();it!=p.end();++it){
(ans*=(it->second+1))%=mod;
}
printf("%lld\n",ans);
return 0;
}
秦久韶推公式的时候自己手推几组小样例。
int n;
const int mod=1e9+7;
map<int,int>p;
inline void divide(int x){
for(int i=2;i*i<=x;++i){
while(x%i==0){
p[i]++;
x/=i;
}
}
if(x>1)p[x]++;
}
#undef int
int main(){
#define int long long
rd(n);
rep(i,1,n){
int x;rd(x);
divide(x);
}
int res=1;
for(map<int,int>::iterator it=p.begin();it!=p.end();++it){
int pp=it->first;
int c=it->second;
int t=1;//存当前质数pp的贡献。
while(c--){
t=(t*pp+1)%mod;
}
res=(res*t)%mod;
}
printf("%lld\n",res);
return 0;
}
a[i] 在mod p意义下没有逆元的情况:
Q:请问最后的答案为什么模了m?
A:题目中要求“输出答案必须在int范围之内”,对m取模后可以保证答案在int内。
int n,x,y;
inline int exgcd(int a,int b,int &x,int &y){
if(b==0){x=1,y=0;return a;}
int d=exgcd(b,a%b,y,x);
y-=(a/b)*x;
return d;
}
#undef int
int main(){
#define int long long
rd(n);
rep(i,1,n){
int a,b,m;rd(a),rd(b),rd(m);
int d=exgcd(a,m,x,y);
if(b%d)printf("impossible\n");
else
printf("%lld\n",x*(b/d)%m);//模到m范围内
}
return 0;
}
==无解的情况:exgcd的时候deltaA/d不为0==
const int N=30+5;
int n,x,y;
int a[N],b[N];
inline int exgcd(int a,int b,int &x,int &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-(a/b)*y;
return d;
}
inline int ksc(int a,int k,int mod){
int res=0;
for(;k;k>>=1){
if(k&1)res=(res+a)%mod;
a=(a+a)%mod;
}
return res;
}
#undef int
int main(){
#define int long long
rd(n);
rep(i,1,n){
rd(b[i]),rd(a[i]);
}
int A1=a[1],B1=b[1];
rep(i,2,n){
int A2=a[i],B2=b[i];
int deltaA=((A2-A1)%B2+B2)%B2;
int d=exgcd(B1,B2,x,y);
x=ksc(x,deltaA/d,B2);
if(deltaA%d){
printf("-1");
exit(0);
}
A1+=B1*x;
B1*=(B2/d);
(A1+=B1)%=B1;
}
printf("%lld",A1);
return 0;
}
1≤n≤100001≤n≤10000,
1≤b≤a≤2000
SOL:递推求解
int n;
const int N=2e3,mod=1e9+7;
int c[N+10][N+10];
int main(){
rd(n);
rep(i,1,N)c[i][0]=c[i][i]=1;
rep(i,1,N)
rep(j,1,i-1)//c[i-1][j]的时候要j<=i-1
c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
rep(i,1,n){
int a,b;rd(a),rd(b);
printf("%d\n",c[a][b]);
}
return 0;
}
## 求组合数 II
1≤n≤10000
1≤b≤a≤1e5
SOL:
公式法。
预处理出fac[]和invfac[]
\(O(n+log(N))\)线性求阶乘的逆元
int n;
const int N=1e5,mod=1e9+7;
int fac[N+10];
int invfac[N+10];
inline int ksm(int a,int k,int mod){
int res=1;
for(;k;k>>=1){
if(k&1)res=res*a%mod;
a=a*a%mod;
}
return res;
}
#undef int
int main(){
#define int long long
rd(n);
fac[0]=invfac[0]=1;
rep(i,1,N){
fac[i]=(fac[i-1]*i)%mod;
}
invfac[N]=ksm(fac[N],mod-2,mod);
dwn(i,N-1,1)
invfac[i]=invfac[i+1]*(i+1)%mod;
rep(i,1,n){
int a,b;rd(a),rd(b);
printf("%lld\n",fac[a]*invfac[a-b]%mod*invfac[b]%mod);
}
return 0;
}
1≤n≤10^5
Cat(2n,n)=C(2n,n)-C(2n,n-1)=C(2n,n)/(n+1)
const int N=2e5;
const int mod=1e9+7;
int fac[N+10],invfac[N+10];
int n;
inline int ksm(int a,int k,int mod){
int res=1;
for(;k;k>>=1){
if(k&1)res=res*a%mod;
a=a*a%mod;
}
return res;
}
#undef int
int main(){
#define int long long
rd(n);
fac[0]=invfac[0]=1;
rep(i,1,2*n)
fac[i]=fac[i-1]*i%mod;
invfac[2*n]=ksm(fac[2*n],mod-2,mod);
dwn(i,2*n-1,1)
invfac[i]=invfac[i+1]*(i+1)%mod;
printf("%lld",fac[2*n]*invfac[n]%mod*invfac[n]%mod*ksm(n+1,mod-2,mod)%mod);
return 0;
}
用二进制数枚举所有可能的组合方案数
由于
$ \sum\limits_{i=0}^nC_n^i=2^n$
所以
\(\sum\limits_{i=1}^n C_n^i\ =\ 2^n -1\)
const int N=20+5;
int n,m;
int p[N];
#undef int
int main(){
#define int long long
rd(n),rd(m);
rep(i,0,m-1)rd(p[i]);
int res=0;
for(int i=1;i< 1<<m;++i){
int t=1,cnt=0;
rep(j,0,m-1){
if(i>>j&1){
cnt++;
t*=p[j];
if(t>n){
t=-1;
break;
}
}
}
if(t!=-1){
if(cnt&1)res+=n/t;
else res-=n/t;
}
}
printf("%lld",res);
return 0;
}
\(k \times C_n^k = n \times C_{n-1}^{k-1}\)
\(C_k^n \times C_m^k\ =\ C_m^n \times C_{m-n}^{m-k}\) \((m-k<m-n)\)
\(\sum \limits_{k=0}^n(-1)^k \times \ C_n^k\ =\ 0\)
\(\sum \limits_{k=1}^m\ C_{n+k}^k\ =\ C_{n+m+1}^{m}\)
支持插入查询的trie
const int N=1e5+10,M=3e6+10;
int n;
int a[N];
int tree[M][2];
int idx;
inline void insert(int x){
int p=0;
dwn(i,30,0){
int &son=tree[p][x>>i&1];
if(!son)son=++idx;
p=son;
}
}
inline int query(int x){
int res=0,p=0;
dwn(i,30,0){
int now=x>>i&1;
if(tree[p][!now]){
res+=1<<i;
p=tree[p][!now];
}
else
p=tree[p][now];
}
return res;
}
int main(){
rd(n);
rep(i,1,n){
rd(a[i]);
insert(a[i]);
}
int ans;
rep(i,1,n){
ans=max(ans,query(a[i]));
}
printf("%d\n",ans);
return 0;
}
const int N=1e5+10;
int tree[N][26];
char str[N];
int cnt[N];
int idx;
inline void insert(char s[]){
int p=0;
for(int i=0;s[i];++i){
int id=s[i]-'a';
int &son=tree[p][id];
if(!son)son=++idx;
p=son;
}
cnt[p]++;
}
inline int query(char s[]){
int p=0;
for(int i=0;s[i];++i){
int id=s[i]-'a';
if(!tree[p][id])return 0;
p=tree[p][id];
}
return cnt[p];
}
int main(){
int q;rd(q);
while(q--){
char op;cin>>op;
scanf("%s",str);
if(op=='I')insert(str);
else printf("%d\n",query(str));
}
return 0;
}
const int N=1e6+10;
int len1,len2;
char t[N];//模式串
char p[N];//模板串
int nxt[N];
inline void getnext(){
int i=0,j=-1;
nxt[0]=-1;
while(i<len2){
if(j==-1 || p[i]==p[j])
nxt[++i]=++j;
else j=nxt[j];
}
}
inline void kmp(){
int i=0,j=0;
while(i<len1){
if(j==-1 || t[i]==p[j])
i++,j++;
else j=nxt[j];
if(j==len2){
printf("%d ",i-len2);
j=nxt[j];
}
}
}
int main(){
rd(len2);
scanf("%s",p);
rd(len1);
scanf("%s",t);
getnext();
kmp();
}
const int N=1e5+10;
int n,cnt;
struct qujian{
int l,r;
friend bool operator <(const qujian a,const qujian b){
return a.l<b.l;
}
}range[N];
int main(){
rd(n);
rep(i,1,n)
rd(range[i].l),rd(range[i].r);
sort(range+1,range+n+1);
int l=range[1].l,r=range[1].r;
rep(i,2,n){
if(r<range[i].l){
// printf("%d %d\n",l,r);
cnt++;
l=range[i].l,r=range[i].r;
}
r=max(r,range[i].r);
}
cnt++;
printf("%d\n",cnt);
// printf("%d %d\n",l,r);
return 0;
}
(残品)选学霸
#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define dwn(i,a,b) for(int i=(a);i>=(b);--i)
template <typename T>inline void rd(T &x){
x=0;char c=getchar();int flag=0;
while(!isdigit(c)){flag|=c=='-';c=getchar();}
while(isdigit(c)){x=x*10+(c^48);c=getchar();}
x=flag?-x:x;
}
#define mem(a,b) memset(a,b,sizeof(a))
#define ee(i,u) for(int i=head[u];i;i=e[i].next)
/******************************************** Header Template **********************************************/
const int N=110;
int n,rb,cnt,sum;
int a[N],buck[N*N];
int f[N*N];
int minn=INT_MAX;
int main(){
freopen("c.txt","r",stdin);
rd(n);
rep(kase,1,n){
mem(f,0);
mem(a,0);
cnt=0,sum=0;
while(scanf("%d",&a[++cnt]) && a[cnt]!=-1)sum+=a[cnt];
printf("%d 's sum=%d",kase,sum);
puts("");
cnt--;
rb=max(sum,rb);
f[0]=1;
rep(i,1,cnt)
dwn(j,sum,a[i]){
if(f[j-a[i]] && !f[j]){
f[j]=1;
buck[j]++;
}
}
rep(i,1,sum)printf("%d ",buck[i]);
puts("");
}
dwn(i,rb,1)
if(buck[i]==n){
printf("%d\n",i);
return 0;
}
printf("%d",rb);
return 0;
}
既然是两个互不相交的序列,那么也就是说选取一个断开点,让该断开点左边的数形成一个序列,右边的数形成另外一个序列。
就可以用一种DP的思想,用l[i]记录i左边的最长的可延伸长度(不一定和第i个数有关),r[i]同理。
这样就可以枚举每一个断开点,求它左边的最长长度加上右边的最长长度的和的最大值。
const int N=5e4+10;
int n,k;
int a[N],l[N],r[N];
int main(){
rd(n),rd(k);
rep(i,1,n)rd(a[i]);
sort(a+1,a+n+1);
l[1]=1;
for(int i=2,j=1;i<=n;++i){
while(j<i && a[i]-a[j]>k){
j++;
}
l[i]=max(l[i-1],i-j+1);
}
r[n]=1;
for(int i=n-1,j=n;i;--i){
while(i<j && a[j]-a[i]>k){
j--;
}
r[i]=max(r[i+1],j-i+1);
}
int ans=0;
rep(i,1,n-1)
ans=max(ans,l[i]+r[i+1]);//断开
printf("%d\n",ans);
return 0;
}
每次维护siz只用维护树根的siz,合并的时候把作为新的树根节点的siz+=合并的siz就好。
int n,m;
const int N=1e5+10;
int fa[N],siz[N];
inline int find(int x){
return fa[x]==x?x:fa[x]=find(fa[x]);
}
#undef int
int main(){
#define int long long
rd(n),rd(m);
rep(i,1,n){
fa[i]=i;
siz[i]=1;
}
while(m--){
string op;cin>>op;
int a,b;
if(op=="C"){
rd(a),rd(b);
int fx=find(a),fy=find(b);
if(fx==fy)continue;//如果俩元素已经在同一个集合里面了的话,siz就会翻倍,所以直接continue掉
fa[fx]=fy;
siz[fy]+=siz[fx];
}
else if(op=="Q1"){
rd(a),rd(b);
int fx=find(a),fy=find(b);
if(fx==fy)puts("Yes");
else puts("No");
}
else if(op=="Q2"){
rd(a);
printf("%lld\n",siz[find(a)]);
}
}
return 0;
}
既然是substr,肯定不是char s[]类型的啊……是string 类型的啦
substr(pos,len):从s[pos]开始复制(从0开始存的),复制的长度为len
string s;
int n,m;
int main(){
rd(n),rd(m);
cin>>s;
while(m--){
int l1,r1,l2,r2;
rd(l1),rd(r1),rd(l2),rd(r2);
string s1,s2;
s1=s.substr(l1-1,r1-l1+1);
s2=s.substr(l2-1,r2-l2+1);
if(s1==s2)printf("Yes\n");
else printf("No\n");
}
return 0;
}
*note:关键在于siz[] 和 dis[]的维护
siz[u]以u为树根的集合大小
dis[u]:u到fa[u]的距离
const int N=3e4+10;
int fa[N],dis[N],T,siz[N];
inline int find(int x){
if(x==fa[x])return x;
int k=fa[x];
fa[x]=find(fa[x]);
dis[x]+=dis[k];
return fa[x];
}
inline int find(int x){
if(x==fa[x])return x;
int k=fa[x];
fa[x]=find(fa[x]);
dis[x]+=dis[k];
return fa[x];
}
inline void merge(int x,int y){
int fx=find(x),fy=find(y);
if(fx==fy)return ;
fa[fx]=fy;
dis[fx]=siz[fy];
siz[fy]+=siz[fx];
}
inline int query(int x,int y){
int fx=find(x),fy=find(y);
if(fx!=fy)return -1;
return abs(dis[x]-dis[y])-1;
}
int main(){
#ifdef WIN32
freopen("a.txt","r",stdin);
#endif
rd(T);
rep(i,1,N){
fa[i]=i;
siz[i]=1;
}
while(T--){
char c;cin>>c;
int x,y;rd(x),rd(y);
if(c=='M')
merge(x,y);
else
printf("%d\n",query(x,y));
}
return 0;
}
/*
4
M 2 3
C 1 2
M 2 4
C 4 2
*/
//-1
//1
const int N=4e5+10;
int fa[N],q[N],cnt;
int ans[N];
int n,k,m;
bool vis[N];
struct edge{
int u,v,next;
}e[N<<1];
int head[N],edge_num;
inline void adde(int u,int v){
e[++edge_num].v=v;
e[edge_num].u=u;
e[edge_num].next=head[u];
head[u]=edge_num;
}
inline int find(int x){
return x==fa[x]?x:fa[x]=find(fa[x]);
}
inline void merge(int x,int y){
int fx=find(x),fy=find(y);
if(fx==fy)return;
fa[fx]=fy;
}
int main(){
rd(n),rd(m);
rep(i,0,n-1)fa[i]=i;
rep(i,1,m){
int u,v;rd(u),rd(v);
adde(u,v),adde(v,u);
}
rd(k);
cnt=n-k;//假设原来没有摧毁的每个点都不连通
mem(vis,1);
rep(i,1,k){
rd(q[i]);
vis[q[i]]=0;//还没修复
}
rep(i,1,2*m){//判断原来没有摧毁的点有没有连通的,这里直接选择以遍历边的形式来判断,方便快捷
if(vis[e[i].u] && vis[e[i].v] && find(e[i].u)!=find(e[i].v)){
cnt--;
merge(e[i].u,e[i].v);
}
}
ans[k+1]=cnt;
dwn(i,k,1){//倒着加点
int u=q[i];
cnt++;//假设此点不与其他点连通,连通块加一
vis[u]=1;
ee(i,u){
int v=e[i].v;
if(vis[v] && find(u)!=find(v)){
cnt--;
merge(u,v);
}
}
ans[i]=cnt;
}
rep(i,1,k+1)
printf("%d\n",ans[i]);
return 0;
}
链接:https://ac.nowcoder.com/acm/contest/1093/A来源:牛客网
给出一个字符串S,定义字符串的价值为字符串的长度*字符串中不同字母的个数。但是这里我们将字符的规则改了一下,我们规定字符不区分大小写,即‘A‘=‘a‘,‘B‘=‘b‘...‘Z‘=‘z‘。我们还有一个额外的约束,字母A和字母B是相等的,如字母A=‘c‘,字母B=‘d‘,那么‘c‘,‘C‘,‘d‘,‘D‘也全是等价的,即‘c‘=‘C‘=‘D‘=‘d‘。添加了这些约束后,你能算出字符串的总价值吗?(我答不能你能让我AC吗?)
输入描述:
第一行输入一行一个字符串S第二行两个字母A B,A、B之间用一个空格分隔。
输出描述:
输出一行一个整数,表示字符串的总价值。
示例1
输入
AbC b c
输出
6
备注:
对于所有数据字符串长度1<=|S|<=1000,字符串中只包含大写字母和小写字母。20% 字符串S中不包含小写字母,或不包含大写字母,且A==B60% 字符串S中不包含小写字母,或不包含大写字母100% 无附加约束
tolower是一种函数,功能是把字母字符转换成小写,非字母字符不做出处理。和函数int _ tolower( int c )功能一样,但是_tolower在VC6.0中头文件要用ctype.h。
char s[1010];
bool vis[26];
int cnt;
int main(){
scanf("%s",s);
int len=strlen(s);
char op1,op2;
cin>>op1>>op2;
op1=tolower(op1);
op2=tolower(op2);
for(int i=0;s[i];++i){
s[i]=tolower(s[i]);
if(s[i]==op2)s[i]=op1;
if(!vis[s[i]-'a']){
vis[s[i]-'a']=1;
cnt++;
}
}
printf("%d",len*cnt);
return 0;
}
int n,k;
const int N=1e5+10;
int a[N];
int main(){
#ifdef WIN32
freopen("c.txt","r",stdin);
#endif
rd(n),rd(k);
rep(i,1,n)rd(a[i]);
sort(a+1,a+n+1);
int l=1,r=n;
while(k--){
if(a[l+1]-a[l]>a[r]-a[r-1]){
l++;
}
else
r--;
}
printf("%d",a[r]-a[l]);
return 0;
}
int n,r,Q,mod;
const int N=1e5;
int fac[N+10],invfac[N+10];
inline int ksm(int a,int k,int mod){
int res=1;
for(;k;k>>=1){
if(k&1)
res=res*a%mod;
a=a*a%mod;
}
return res;
}
#undef int
int main(){
#define int long long
#ifdef WIN32
freopen("c.txt","r",stdin);
#endif
rd(Q),rd(mod);
fac[0]=invfac[0]=1;
rep(i,1,N)
fac[i]=fac[i-1]*i%mod;
invfac[N]=ksm(fac[N],mod-2,mod);
dwn(i,N-1,1)
invfac[i]=invfac[i+1]*(i+1)%mod;
while(Q--){
rd(n),rd(r);
printf("%lld\n",fac[n]*invfac[r]%mod);
}
return 0;
}
链接:https://ac.nowcoder.com/acm/contest/1093/B来源:牛客网
给出一个长度为n的数组,你可以去掉数组中最多k个元素,也可以不去掉。你需要通过去掉最多k个元素后使得数组中的最大值-最小值最小化。输出这个值是多少。
输入描述:
第一行两个整数n,k。接下来n个整数a1,a2...an表示数组每个元素的值。
输出描述:
输出一行一个整数表示答案。
示例1
输入
3 1 1 2 4
输出
1
说明
去掉元素4,最小差为2-1=1
备注:
对于所有数据1<=n<=100000,1<=ai<=100000,0<=k<n。10% 保证数组中所有元素的值相等。40% 保证所有元素是一个排列。100% 无附加约束。
首先排序,我们能在两端总共选k个去掉,枚举左边去掉的长度,就可以O(1)求出右边去掉的长度,O(k)扫一遍,用ans记录最小值。
int n,k;
const int N=1e5+10;
int a[N];
int main(){
rd(n),rd(k);
rep(i,1,n)rd(a[i]);
sort(a+1,a+n+1);
int ans=INT_MAX;
rep(len,0,k){
ans=min(ans,a[n-(k-len)]-a[1+len]);
}
printf("%d",ans);
return 0;
}
询问俩点的祖先关系,可以直接树剖一遍,看另一个点在不在自己的子树范围之内。
const int N=1e5+10;
int n,m,rt;
struct edge{
int v,next;
}e[N<<1];
int head[N],edge_num;
inline void adde(int u,int v){
e[++edge_num].v=v;
e[edge_num].next=head[u];
head[u]=edge_num;
}
int siz[N],fa[N],son[N],deep[N];
inline void dfs1(int u,int f){
fa[u]=f;
siz[u]=1;
deep[u]=deep[f]+1;
ee(i,u){
int v=e[i].v;
if(v==f)continue;
dfs1(v,u);
siz[u]+=siz[v];
if(siz[v]>siz[son[u]])
son[u]=v;
}
}
int top[N],id[N],chuo=0;
inline void dfs2(int u,int topf){
top[u]=topf;
id[u]=++chuo;
if(!son[u])return;
dfs2(son[u],topf);
ee(i,u){
int v=e[i].v;
if(v==fa[u] || v==son[u])continue;
dfs2(v,v);
}
}
int main(){
#ifdef WIN32
freopen("a.txt","r",stdin);
#endif
rd(n);
rep(i,1,n){
int u,v;rd(u),rd(v);
if(v==-1)rt=u;
else adde(u,v),adde(v,u);
}
dfs1(rt,0);
dfs2(rt,rt);
rd(m);
rep(i,1,m){
int u,v;rd(u),rd(v);
if(id[v]>id[u] && id[v]<=id[u]+siz[u]-1)
puts("1");
else if(id[u]>id[v] && id[u]<=id[v]+siz[v]-1)
puts("2");
else
puts("0");
}
return 0;
}
inline void BF(){
mem(dis,0x3f);
dis[1]=0;
while(k--){
memcpy(backup,dis,sizeof(dis));
rep(i,1,m){
int u=e[i].u,v=e[i].v,w=e[i].w;
if(dis[v]>backup[u]+w)
dis[v]=backup[u]+w;
}
}
if(dis[n]>0x3f3f3f3f/2)puts("impossible");
else printf("%d\n",dis[n]);
}
vis[]的意义是防止queue离存储重复的点
while(!q.empty()){
int u=q.front();q.pop();
vis[u]=0;
ee(i,u){
int v=e[i].v,w=e[i].w;
if(dis[v]>dis[u]+w){
dis[v]=dis[u]+w;
if(!vis[v]){
vis[v]=1;
q.push(v);
}
}
}
}
queue<int>q;
inline int spfa(){
mem(dis,0x3f);
mem(vis,0);
rep(i,1,n){
vis[i]=1;
q.push(i);//由于不是要判断是否有从1到达的负环,而是有没有负环,所以初始的时候直接加入所有的点
}
while(!q.empty()){
int u=q.front();q.pop();
vis[u]=0;
ee(i,u){
int v=e[i].v,w=e[i].w;
if(dis[v]>dis[u]+w){
dis[v]=dis[u]+w;
cnt[v]=cnt[u]+1;//边数++,
if(cnt[v]>=n)return -1;//边数>=n,说明经过的点数>=n+1,根据容斥原理,有负权环
if(!vis[v]){
vis[v]=1;
q.push(v);
}
}
}
}
}
reference: https://www.luogu.org/blog/lightningUZ/solution-p2412
int n,m;
const int N=5e4+10;
string f[N][25+5];
string max(string a,string b){
string tmp1=a,tmp2=b;
for(int i=0;i<a.size();++i)
a[i]=tolower(a[i]);
for(int i=0;i<b.size();++i)
b[i]=tolower(b[i]);
return a>b?tmp1:tmp2;
}
int main(){
#ifdef WIN32
freopen("a.txt","r",stdin);
#endif
rd(n),rd(m);
rep(i,1,n){
cin>>f[i][0];
}
int k=log2(n);
rep(j,0,k)
for(int i=1;i+(1<<j)-1<=n;++i)
f[i][j+1]=max(f[i][j],f[i+(1<<j)][j]);
rep(i,1,m){
int l,r;rd(l),rd(r);
int len=log2(r-l+1);
cout<<max(f[l][len],f[r-(1<<len)+1][len])<<endl;
}
return 0;
}
标签:double turn ems swap www load 重复 trie win32
原文地址:https://www.cnblogs.com/sjsjsj-minus-Si/p/11785196.html