B.写信
题意:有n个信封和n封信,问全部装错有多少种可能
Solution
全错排问题,对于i=k的情况,我们可以从i=k-1和i=k-2转移过来
一种是k-1个全错排,然后从前面k-1个选出一个信封与第k个交换
另一种是任选一个j,有1<=j<=k-1放在k,这样除了k和j以外还有k-2个数进行全错位排列,
这样我们可以得到递推公式:
$$
D_n=(n-1)(D_{n-1}+D_{n-2})
$$
化简后就有:
$$
D_{n+1}=(n+1)D_n+(-1)^{n+1}
$$
这题数据量很大,我们采用分段打表的方式来计算
如果采用上面的公式的话要打两个表,采用下面的话只用打一个
int t[150]={ //t[0]=1
1,824182295,933713113,474482547,930651136,251064654,637937211,229643390,307311871,448853213,322273426,398890147,194914852,884947442,154199209,881788023,389699639,733217502,601739182,372305477,213823357,713959988,498202615,196342945,324300550,154001751,974475946,540773759,467881322,257531902,598680559,367927849,971346692,94577421,617165552,128327758,503709458,253566817,820144401,13965056,82358069,805941568,533047638,69430220,686678173,297170813,34546238,323435423,499126069,487532712,468899710,790590914,581347156,955359050,700529992,518280890,98592091,64544225,988209678,422603955,40661679,174468756,573631136,757555557,710709955,775098981,499158883,969149294,880429710,42564126,333697951,522067888,579797877,528967798,717694718,309384913,31308092,316850320,220854491,878646494,963974981,377654637,705101053,542246848,466289530,750036412,819636314,688721174,464087273,517164631,256789690,482685016,276682441,473333947,340221393,762927538,624766601,984537252,977632075,34192646,402182971,
};
void table() //打表
{
int y=1;
int next;
int xx=1e7;
for(int i=3;i<=1e9+5;i++)
{
next=i*y%mod;
if(i&1)next=(next-1+mod)%mod;
else next=(next+1)%mod;
if(i%xx==0)cout<<next<<",";
y=next;
}
}
void solve()
{
int n;cin>>n;
int tt=1e7;
int pos=n/tt;
int ans=t[pos];
for(int i=pos*tt+1;i<=n;i++)
{
ans=(ans*(i))%mod;
if(i&1)ans=(ans-1+mod)%mod;
else ans=(ans+1)%mod;
}
cout<<ans<<"\n";
table();
}
E.兔兔的金铲铲
题意:给出n个数,要求进行最少的交换操作,使得对于所有i(i为奇数),a[i]和a[i+1]差的绝对值为1
Solution
没啥难的,赛时卡别的题没做出来,直接离散化然后模拟一遍就行了
int a[N];
int cnt[N];
int p[N];
int vis[N];
void solve()
{
int n;cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<=n;i++)cnt[i]=a[i];
sort(cnt+1,cnt+n+1);
for(int i=1;i<n;i++)
{
if(cnt[i+1]-cnt[i]!=1)
{
cout<<"-1\n";
return;
}else i++;
}
for(int i=1;i<=n;i++)
{
a[i]=lower_bound(cnt+1,cnt+1+n,a[i])-cnt;
}
// for(int i=1;i<=n;i++)cout<<a[i]<<" ";
for(int i=1;i<=n;i++)p[a[i]]=i;
int ans=0;
for(int i=1;i<=n;i++)
{
int x=a[i]-1;
if(a[i]&1)x=a[i]+1;
if(a[i+1]==x)
{
i++;
continue;
}else
{
int pos=p[x];
swap(p[a[i+1]],p[x]);
swap(a[i+1],a[pos]);
ans++;
i++;
}
}
cout<<ans<<"\n";
}
F.图的动态染色
题意:
给出n个点和m条边,初始所有的点都为红色,进行q次操作:
1 x y
:给x和y加一条边(如果x和y已经有边或者x=y则直接无视)
2 x (r/g/b)
:将点x变为红/绿/蓝色
3 x (r/g/b)
:输出点x所在连通块中红/绿/蓝色的点个数和x相邻点中红/绿/蓝色的点个数
Solution
不会做,题解说是分治,把连边数大于一个值的点设为特殊点,给所有点加一个vector用来存周围的特殊点,每次操作时对特殊点进行更新,这样对特殊点查询时直接O(1)查询,对其他点就直接暴力,至于连通块,很容易想到并查集,剩下就好写了
int find(int x)
{
return t[x]==x?x:t[x]=find(t[x]);
}
void merge(int u,int v)
{
int a=find(u),b=find(v);
if(a!=b)
{
t[a]=b;
p[b][0]+=p[a][0];
p[b][1]+=p[a][1];
p[b][2]+=p[a][2];
}
}
void solve()
{
int n,m,q;cin>>n>>m>>q;
for(int i=1;i<=n;i++)
{
t[i]=i;
col[i]=0;
p[i][0]=1;
p[i][1]=p[i][2]=0;
}
int maxm=1000;
for(int i=1;i<=m;i++)
{
int u,v;cin>>u>>v;
e[u].push_back(v);
e[v].push_back(u);
num[u]++;
num[v]++;
st[u].insert(v);
st[v].insert(u);
merge(u,v);
}
for(int i=1;i<=n;i++)
{
for(auto it:e[i])
{
if(num[it]>=maxm)
{
k[i].push_back(it);
cnt[it][col[i]]++;
}
}
}
while(q--)
{
int op;cin>>op;
if(op==1)
{
int u,v;cin>>u>>v;
if(u==v)continue;
if(st[u].count(v))continue;
if(st[v].count(u))continue;
e[u].push_back(v);
e[v].push_back(u);
num[u]++;
num[v]++;
st[u].insert(v);
st[v].insert(u);
if(num[u]==maxm)
{
for(auto it:e[u])
{
k[it].push_back(u);
cnt[u][col[it]]++;
}
}else if(num[u]>maxm)
{
cnt[u][col[v]]++;
}
if(num[v]==maxm)
{
for(auto it:e[v])
{
k[it].push_back(v);
cnt[v][col[it]]++;
}
}else if(num[v]>maxm)
{
cnt[v][col[u]]++;
}
merge(u,v);
}else if(op==2)
{
int x;char c;cin>>x>>c;
int next=0;
if(c=='g')next=1;
else if(c=='b')next=2;
for(auto it:k[x])
{
cnt[it][col[x]]--;
cnt[it][next]++;
}
int z=find(x);
p[z][col[x]]--;
p[z][next]++;
col[x]=next;
}else if(op==3)
{
int x;char c;cin>>x>>c;
int next=0;
if(c=='g')next=1;
else if(c=='b')next=2;
int z=find(x);
int res=0;
int ans=p[z][next];
if(num[x]>=maxm)
{
cout<<p[z][next]<<" "<<cnt[x][next]<<"\n";
continue;
}
for(auto it:e[x])
{
if(col[it]==next)res++;
}
cout<<ans<<" "<<res<<"\n";
}
}
}
H.神奇石子
题意:有2n个数,分别为a1,a2,...,an和b1,b2,...,bn,对于i∈[1,n],从ai和bi中选一个数,得到一个长为n的数组,求选到的数中最大数和最小数之差的最小值
Solution
我们把ai和bi中小的数放在ai上,然后按ai升序排序,假设最开始只选a1,a2,...,an,那么答案为an-a1,之后每次把ai和bi交换,然后判断交换后的最大值和最小值之差和原答案的大小,这里用set存下选的值,因为set默认升序
int a[N];
int b[N];
struct node
{
int x,y;
bool operator < (const node &t) const&{
if(x != t.x)return x < t.x;
else return y < t.y;
}
}e[N];
void solve()
{
int n;cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<=n;i++)cin>>b[i];
multiset<int>st;
for(int i=1;i<=n;i++)
{
e[i].x=a[i];
e[i].y=b[i];
if(e[i].x>e[i].y)swap(e[i].x,e[i].y);
st.insert(e[i].x);
}
sort(e+1,e+1+n);
int ans=e[n].x-e[1].x;
for(int i=1;i<=n;i++)
{
auto it=st.find(e[i].x);
st.erase(it);
st.insert(e[i].y);
int last=*(st.rbegin());
int first=*(st.begin());
ans=min(ans,last-first);
}
cout<<ans<<"\n";
}
M.认真思考?抓紧时间!
题意:给出一颗树,可以进行3种操作
1 x y
:查寻节点x到y的路径上边权的平方和
2 x y z
:将节点x到节点y的路径所经过的边的边权增加z
3 x y z
:将节点x到节点y的路径所经过的边的边权修改为z
Solution
树链剖分+线段树,因为不会所以去oi wiki学了,然后和小e哥哥调了一晚上代码,最后发现初始化建树错了,太板子了我也不会讲了(
int fa[N],son[N],sz[N],top[N],dfn[N],dep[N];
vector<int>e[N];
map<int,int> a[N];
int b[N];
int n,m;
int idx;
void dfs1(int x,int pre) //第一次递归处理深度,父节点,树大小,重儿子
{
dep[x]=dep[pre]+1;
sz[x]=1;
fa[x]=pre;
int hson=0;
for(auto it:e[x])
{
if(it==pre)continue;
dfs1(it,x);
//b[it]=a[x][it];
sz[x]+=sz[it];
if(sz[it]>hson)
{
hson=sz[it];
son[x]=it;
}
}
}
void dfs2(int x,int t) //第二次递归处理重链顶部节点和dfs序(在线段树中的编号)
{
top[x]=t;
dfn[x]=++idx;
if(!son[x])return;
dfs2(son[x],t);
for(auto it:e[x])
{
if(it==fa[x]||it==son[x])continue;
dfs2(it,it);
}
}
int lz1[N<<2],lz2[N<<2],sum1[N<<2],sum2[N<<2];//区间加标记,区间赋值标记,区间和,区间平方和
/*int mo(int x)
{
return (x%p+p)%p;
}*/
/*void pushdown(int l, int r, int o)
{
int mid = (l + r) >> 1, ls = o << 1, rs = o << 1 | 1;
if(lz2[o] != -1)
{
int &w = lz2[o];
sum2[ls] = w * w % p * (mid - l + 1) % p;
sum2[rs] = w * w % p * (r - mid ) % p;
sum1[ls] = w * (mid - l + 1) % p;
sum1[rs] = w * (r - mid ) % p;
lz2[ls] = lz2[rs] = w;
lz1[ls] = lz1[rs] = 0;
w = -1;
}
if(lz1[o])
{
int &w = lz1[o];
sum2[ls] = mo(sum2[ls] + 2 * sum1[ls] * w % p + (mid - l + 1) * w % p * w % p);
sum2[rs] = mo(sum2[rs] + 2 * sum1[rs] * w % p + (r - mid ) * w % p * w % p);
sum1[ls] = mo(sum1[ls] + (mid - l + 1) * w % p);
sum1[rs] = mo(sum1[rs] + (r - mid ) * w % p);
lz1[ls] = mo(lz1[ls] + w);
lz1[rs] = mo(lz1[rs] + w);
w = 0;
}
}*/
void pushdown(int l,int r,int rt) //加标记<乘标记<覆盖标记,所以覆盖后要清空加标记
{
int mid=(l+r)>>1;
if(lz2[rt]!=-1)
{
sum1[rt<<1]=(mid-l+1)*lz2[rt]%mod;
sum1[(rt<<1)|1]=(r-mid)*lz2[rt]%mod;
sum2[rt<<1]=((mid-l+1)*lz2[rt]%mod)*lz2[rt]%mod;
sum2[(rt<<1)|1]=((r-mid)*lz2[rt]%mod)*lz2[rt]%mod;
lz2[rt<<1]=lz2[(rt<<1)|1]=lz2[rt];
lz2[rt]=-1;
lz1[rt<<1]=lz1[(rt<<1)|1]=0;
}
if(lz1[rt])
{
sum2[rt<<1]=((sum2[rt<<1]+(sum1[rt<<1]*2%mod*lz1[rt]%mod))%mod+((mid-l+1)*lz1[rt])%mod*lz1[rt]%mod)%mod;
sum2[(rt<<1)|1]=((sum2[(rt<<1)|1]+(sum1[(rt<<1)|1]*2%mod*lz1[rt]%mod))%mod+((r-mid)*lz1[rt])%mod*lz1[rt]%mod)%mod;
sum1[rt<<1]=(sum1[rt<<1]+(mid-l+1)%mod*lz1[rt]%mod)%mod;
sum1[(rt<<1)|1]=(sum1[(rt<<1)|1]+(r-mid)%mod*lz1[rt]%mod)%mod;
lz1[rt<<1]=(lz1[rt<<1]+lz1[rt])%mod;
lz1[(rt<<1)|1]=(lz1[(rt<<1)|1]+lz1[rt])%mod;
lz1[rt]=0;
}
}
void modify(int l,int r,int L,int R,int rt,int x,int op)
{
if(l>r)return;
if(L<=l&&r<=R)
{
if(op==2)
{
//sum2[rt] = mo(sum2[rt] + 2 * sum1[rt] * x % p + (r - l + 1) * x % p * x % p);
sum2[rt]=((sum2[rt]+sum1[rt]*2%mod*x%mod)%mod+x*x%mod*(r-l+1)%mod)%mod;
//sum1[rt] = mo(sum1[rt] + (r - l + 1) * x % p);
sum1[rt]=(sum1[rt]+(r-l+1)%mod*x%mod)%mod;
lz1[rt]=(lz1[rt]+x)%mod;
}else
{
sum1[rt]=(r-l+1)*x%mod;
sum2[rt]=x*x%mod*(r-l+1)%mod;
lz2[rt]=x%mod;
lz1[rt]=0;
}
return;
}
int mid=(l+r)>>1;
pushdown(l,r,rt);
if(L<=mid)modify(l,mid,L,R,rt<<1,x,op);
if(R>mid)modify(mid+1,r,L,R,(rt<<1)|1,x,op);
sum1[rt]=(sum1[rt<<1]+sum1[(rt<<1)|1])%mod;
sum2[rt]=(sum2[rt<<1]+sum2[(rt<<1)|1])%mod;
}
int query(int l,int r,int L,int R,int rt)
{
if(l>r)return 0;
if(L<=l&&r<=R)
{
return sum2[rt];
}
int mid=(l+r)>>1;
pushdown(l,r,rt);
int res=0;
if(L<=mid)res=(res+query(l,mid,L,R,rt<<1))%mod;
if(R>mid)res=(res+query(mid+1,r,L,R,(rt<<1)|1))%mod;
sum1[rt]=(sum1[rt<<1]+sum1[(rt<<1)|1])%mod;
sum2[rt]=(sum2[rt<<1]+sum2[(rt<<1)|1])%mod;
return res;
}
void solve()
{
cin>>n>>m;
memset(lz2,-1,sizeof(lz2));
for(int i=1;i<n;i++)
{
int u,v,w;cin>>u>>v>>w;
e[u].push_back(v);
e[v].push_back(u);
a[u][v]=a[v][u]=w;
}
dfs1(1,0);
dfs2(1,1);
for(int i=1;i<=n;i++)b[dfn[i]]=a[i][fa[i]];
/*for(int i=1;i<=n;i++)cout<<b[i]<<" ";
cout<<"\n";*/
for(int i=1;i<=n;i++)modify(1,n,i,i,1,b[i],3);
while(m--)
{
int op;cin>>op;
if(op==1)
{
int x,y;cin>>x>>y;
int res=0;
while(top[x]!=top[y])
{
if(dep[top[x]]<dep[top[y]])swap(x,y);
res=(res+query(1,n,dfn[top[x]],dfn[x],1))%mod;
x=fa[top[x]];
}
if(dep[x]<dep[y])swap(x,y);
res=(res+query(1,n,dfn[y]+1,dfn[x],1))%mod;
cout<<res<<"\n";
}else
{
int x,y,z;cin>>x>>y>>z;
while(top[x]!=top[y])
{
if(dep[top[x]]<dep[top[y]])swap(x,y);
modify(1,n,dfn[top[x]],dfn[x],1,z,op);
x=fa[top[x]];
}
if(dep[x]<dep[y])swap(x,y);
modify(1,n,dfn[y]+1,dfn[x],1,z,op);
}
}
/*for(int i=1;i<=n;i++)
{
cout<<"x="<<i<<" top[x]="<<top[i]<<"\n";
cout<<query(1,n,dfn[top[i]]+1,dfn[i],1)<<"\n";
}*/
}
标签:int,华中农业大学,back,next,++,num,补题,2023,push
From: https://www.cnblogs.com/HikariFears/p/17339765.html