20240726模拟赛订正题笔记
(T1)lnsyoj2212 刷数组
考场上切掉了,所以来说说考场上的做法。
首先看数据范围,线段树并不能拿满分,所以考虑在数组上操作。
如何处理区间覆盖:记录一下区间覆盖的数,然后记录第几次覆盖,单点修改或增加时查看次数被覆盖了几次,若与正常覆盖次数不符,则将此数设为新的覆盖数。
考场代码如下:
#include <cstdio>
#define int long
using namespace std;
int ans;
int a[10000005];
int qtcs[10000005];
int qtshu;
int yqtcs;
int n,q;
signed main(){
scanf("%ld%ld",&n,&q);
for(int i=1;i<=q;i++){
int opt;
scanf("%ld",&opt);
if(opt==1){
int x,y;
scanf("%ld%ld",&x,&y);
if(qtcs[x]<yqtcs){
a[x]=qtshu;
qtcs[x]=yqtcs;
}
ans+=y-a[x];
a[x]=y;
}
else if(opt==2){
int x,y;
scanf("%ld%ld",&x,&y);
if(qtcs[x]<yqtcs){
a[x]=qtshu;
qtcs[x]=yqtcs;
}
ans+=y;
a[x]+=y;
}
else if(opt==3){
int y;
scanf("%ld",&y);
qtshu=y;
ans=y*n;
yqtcs++;
}
printf("%ld\n",ans);
}
return 0;
}
(T2)lnsyoj2213 LCA 的统计
(考场上还想以为有规律呢,最后提交时还忘了记忆化,真是服了)
首先设\(f_{i}\)为当根节点的权值为i时所有LCA的和,\(g_{i}\)为当根节点的权值为i时树的节点数。
转移为:\(f_{i}=\begin{cases}2f_{i/2}+i(g_{i}^{2}-2g_{i/2}^2)&i\%2==0\\f_{i/2}+f_{i/2+1}+i(g_{i}^{2}-g_{i/2}^2-g_{i/2+1}^2)&i\%2==1\end{cases}\)
\(g_{i}=\begin{cases}2g_{i/2}+1&i\%2==0\\g_{i/2}+g_{i/2+1}+1&i\%2==1\end{cases}\)
可以通过打表找规律得知\(g_{i}=2i-1\)。
原因:因为一个根节点为i的树的左右子树都为神奇的树,所以肯定将左右两个树的权值进行合并,然后需要考虑一下几种情况:
1.左子树与根节点的LCA总和,因为左子树与根节点的LCA一定为根节点,所以权值都采用根节点的。
2.右子树与根节点的LCA总和,与左子树相似。
3.左子树与右子树的LCA总和,因为左子树与右子树的LCA显然为根节点,所以权值都采用根节点的。
4.根节点与根节点的LCA总和,直接加一吧。
所以会发现除了左右子树内的贡献不采用根节点,其余的均采用根节点的权值。
dp时进行记忆化搜索,记忆化时用map或unordered_map维护,时间复杂度\(O(Tlog_{n})\),取模细节超级多,说不定哪就挂了。。。。。。
代码如下:
#include <cstdio>
#include <algorithm>
#include <unordered_map>
#define int long long
#define mod 1000000007
using namespace std;
unordered_map<int,int> um;
inline int g(int i){
return (2*i%mod-1+mod)%mod;
}
int dfs1(int i){
if(um[i]!=0){
return um[i];
}
if(i&1){
um[i]=((dfs1(i/2+1)+dfs1(i/2))%mod+i%mod*(((g(i)*g(i)%mod-g(i/2+1)*g(i/2+1)%mod+mod)%mod-g(i/2)*g(i/2)%mod+mod)%mod)%mod)%mod;
return um[i];
}
else{
um[i]=((dfs1(i/2)*2)%mod+i%mod*(((g(i)*g(i)%mod-g(i/2)*g(i/2)%mod+mod)%mod-g(i/2)*g(i/2)%mod+mod)%mod)%mod)%mod;
return um[i];
}
}
int T;
signed main(){
um[1]=1;
scanf("%lld",&T);
for(int i=1;i<=T;i++){
int n;
scanf("%lld",&n);
printf("%lld\n",dfs1(n));
}
return 0;
}
(T3)lnsyoj2214 设置
对于此问,只需要将最少效果值的k的解求出,再异或即可,具体如下:
首先将每个零件的所有设置的效果值从小到大排序,时间复杂度\(O(nmlogm)\)。
再将所有零件按照多关键字(第i个设置效果值与第i-1个设置效果值的差)进行从小到大排序,时间复杂度\(O(nmlogn)\)。
显然1,1,1,......是最优解,[2,1,1,......]是次优解。
然后可以用小根堆(堆按照所存状态的和进行排序)进行维护,先将[2,1,1,......]状态(可以开个结构体维护:1.该状态的权值和(\(val\))2.上个状态修改了的零件到该状态的零件编号(\(lb\))3.上个状态修改某个零件到该状态后该零件的设置编号(\(zb\)))放入堆中,然后每次操作时考虑该状态修改哪个零件可以使新状态只次一点
1.当\(zb<m\)时,可以将\(lb\)号零件设置为编号为\(zb+1\)号的设置。
2.当\(lb<n\)时,可以将\(lb+1\)号零件设置为编号为\(2\)号的设置。
3.当\(lb<n\)且\(zb=2\)时,可以将\(lb\)号零件设置为编号为\(1\)号的设置,并将\(lb+1\)号零件设置为编号为\(2\)号的设置。
取k堆顶然后异或起来即可。
代码如下:
#include <cstdio>
#include <queue>
#include <algorithm>
#include <vector>
#define int long long
using namespace std;
int n,m,k;
vector<int> yj[300005];
int px[300005];
bool cmp1(int a,int b){
return a<b;
}
bool cmp2(vector<int> a,vector<int> b){
for(int i=2;i<m;i++){
if(a[i]-a[i-1]==b[i]-b[i-1]){
continue;
}
return a[i]-a[i-1]<b[i]-b[i-1];
}
return a[m]-a[m-1]<b[m]-b[m-1];
}
priority_queue<pair<int,pair<int,int> >,vector<pair<int,pair<int,int> > >,greater<pair<int,pair<int,int> > > > pq;
int ans;
signed main(){
scanf("%lld%lld%lld",&n,&m,&k);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
scanf("%lld",&px[j]);
}
sort(px+1,px+m+1,cmp1);
yj[i].push_back(0);
for(int j=1;j<=m;j++){
yj[i].push_back(px[j]);
}
}
sort(yj+1,yj+n+1,cmp2);
int val=0;
for(int i=1;i<=n;i++){
val+=yj[i][1];
}
ans=val;
//printf("ans:%lld\n",val);
if(k==1){
printf("%lld\n",ans);
return 0;
}
val=val-yj[1][1]+yj[1][2];
pq.push({val,{1,2}});
for(int i=2;i<=k;i++){
pair<int,pair<int,int> > tmp=pq.top();
//printf("ans:%lld\n",tmp.first);
pq.pop();
ans^=tmp.first;
if(tmp.second.first<n){
pq.push({tmp.first-yj[tmp.second.first+1][1]+yj[tmp.second.first+1][2],\
{tmp.second.first+1,2}});
}
if(tmp.second.first<n && tmp.second.second==2){
pq.push({tmp.first-yj[tmp.second.first][2]+yj[tmp.second.first][1]-yj[tmp.second.first+1][1]+yj[tmp.second.first+1][2],\
{tmp.second.first+1,2}});
}
if(tmp.second.second<m){
pq.push({tmp.first-yj[tmp.second.first][tmp.second.second]+yj[tmp.second.first][tmp.second.second+1],\
{tmp.second.first,tmp.second.second+1}});
}
}
printf("%lld\n",ans);
}
/*
3 2 4
3 5
5 5
5 4
*/