8.3考试总结(NOIP模拟19)[最长不下降子序列·完全背包问题·最近公共祖先]
一定要保护自己的梦想,即使牺牲一切。
前言
把人给考没了。。。
看出来 T1 是一个周期性的东西了,先是打了一个暴力,想着打完 T2 T3 暴力就回来打。。
然后,就看着 T2 上头了,后来发现是看错题了,码完暴力就已经 2.5h 了
接下来就会开始看 T3 看到了部分分非常令人欣喜(码起主席树根本停不下来)。
一直到考试结束都没想起我那 T1 。
T1 最长不下降子序列
解题思路
对于比较小的数据可以直接 \(mathcal{O(nlogn)}\) 求出来(洗提 30 pts)
发现对于同一递推式,同一膜数,一定会有一个不大于膜数的周期(抽屉原理)
当出现一个与之前相同的数字时,后面的数其实也是一样的。
然后整个序列就成了三部分:最前面的散的+可以被划分成几个周期的区间+不完整的周期
接下来先对于第一部分以及它后面的一个周期可以直接暴力求出来。
并且以此类推直接将后面的区间中周期数加上就好了。
对于第三部分的转移不可以只向前倒一个周期,这样是不对的。
所有可以向前倒 周期个周期 这样就可以完美地解决这个问题了。
边界问题需要卡一下。
最后一个问题,如果运算的答案比正确答案少 1 怎么办??加上不就好了,有问题么,没有问题。(反正我的边界是这么卡出来的)
code(调试信息未删)
#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Pass"<<endl
using namespace std;
inline int read()
{
int x=0,f=1;
char ch=getchar();
while(ch>'9'||ch<'0')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*f;
}
const int N=1e6+10,INF=1e18;
int n,a,b,c,d,T,t0,beg,len,ans,f[N],s[N],pos[N];
int cnt,lsh[N],tre[N];
int lowbit(int x)
{
return x&(-x);
}
int ask(int x)
{
int temp=0;
for(int i=x;i<=cnt+1;i+=lowbit(i))
temp=max(temp,tre[i]);//,cout<<i<<endl;
return temp;
}
void add(int x,int num)
{
for(int i=x;i;i-=lowbit(i))
tre[i]=max(tre[i],num);//,cout<<i<<endl;
}
void Special_Judge()
{
lsh[++cnt]=-INF;
lsh[++cnt]=s[1]=t0;
for(int i=2;i<=n;i++)
lsh[++cnt]=s[i]=(a*s[i-1]%d*s[i-1]%d+b*s[i-1]%d+c)%d;
sort(lsh+1,lsh+cnt+1);
cnt=unique(lsh+1,lsh+cnt+1)-lsh-1;
for(int i=1;i<=n;i++)
s[i]=lower_bound(lsh+1,lsh+cnt+1,s[i])-lsh;
// for(int i=1;i<=cnt;i++) cout<<lsh[i]<<' ';
for(int i=1;i<=n;i++)
{
f[i]=ask(cnt+1-s[i])+1;
// cout<<f[i]<<' ';
add(cnt+1-s[i],f[i]);
ans=max(ans,f[i]);
}
printf("%lld",ans);
// cout<<endl;for(int i=1;i<=n;i++) cout<<s[i]<<' ';
exit(0);
}
signed main()
{
n=read();
t0=read();
a=read();
b=read();
c=read();
d=read();
s[1]=t0;
for(int i=2;i<=min(n,2*d);i++)
{
s[i]=(a*s[i-1]%d*s[i-1]%d+b*s[i-1]%d+c)%d;//,cout<<s[i]<<' ';
// cout<<s[i]<<endl;
}
// f();
for(int i=1;i<=min(n,2*d);i++)
{
// cout<<i<<' '<<s[i]<<endl;
if(pos[s[i]])
{
// cout<<s[i]<<' '<<pos[s[i]]<<endl;
T=i-pos[s[i]];
beg=pos[s[i]];
break;
}
else pos[s[i]]=i;
}
if(!beg||T*T+beg-1>=n||n<=1e6) Special_Judge();
// f();
// cout<<T<<' '<<beg<<endl;
cnt=1000;
for(int i=1;i<=beg+T-1;i++)
{
// f();
// cout<<s[i]<<' '<<cnt<<endl;
f[i]=ask(cnt-s[i]+1)+1;
// if(f[i]) f();
add(cnt-s[i]+1,f[i]);
}
// f();
// cout<<(n-beg+1)/T<<endl;
for(int i=1;i<=T;i++)
{
// f();
f[i]=f[i+beg-1]+(n-beg+1)/T-T;
ans=max(f[i],ans);
// cout<<f[i]<<endl;
}
// for(int i=1;i<=10;i++) cout<<s[i]<<' ';
// for(int i=beg;i<=beg+T-1;i++) cout<<s[i]<<' ';cout<<endl;
len=(n-beg+1)%T;
s[0]=s[beg+T-1];
for(int i=1;i<=T*T+len-1;i++)
s[i]=(a*s[i-1]%d*s[i-1]%d+b*s[i-1]%d+c)%d;
// for(int i=1;i<=T+len-1;i++) cout<<s[i]<<' ';
memset(tre,0,sizeof(tre));
/*
for(int i=1;i<=T;i++)
{
// cout<<f[i]<<' ';
f[i]=max(f[i],ask(cnt-s[i]+1)+1);
// cout<<ask(cnt-s[i]+1)+1<<endl;
// cout<<f[i]<<endl;
add(cnt-s[i]+1,f[i]);
}
// */
// cout<<T<<' '<<beg<<' '<<len<<endl;
for(int i=1;i<=T;i++)
{
memset(tre,0,sizeof(tre));
for(int j=i;j<=T;j++)
add(cnt-s[j]+1,f[j]);
for(int j=T+1;j<=((len>0)?T*T+len-1:T*T);j++)
{
// cout<<s[j]<<' ';
int temp=ask(cnt-s[j]+1)+1;
// cout<<temp<<endl;
ans=max(ans,temp);
add(cnt-s[j]+1,temp);
}
}
printf("%lld",ans);
return 0;
}
T2 完全背包问题
解题思路
有一个新知识:同余最短路。
通俗来讲就是其它的数对于整个序列中最小的数字(这样时间复杂度较小)取膜后。
所得到的余数,对于更大的数只要与序列最小数取膜后余数也是这个,那么就一定可以通过序列中某些数字的组合得到。
然后发现这个题的 DP 可以对于前几个物品,选择了几个有限制的物品,以及对于序列最小数取膜之后的值进行维护。
有限制的物品比较好考虑,主要是没有限制的。
如果枚举的话,可以枚举到正无穷,显然这样是不可以的。
因此考虑上面提到的同余最短路,若干个相同数字的加和取膜最终一定会得到之前取膜得到过的数。
对于这个性质,我们可以直接跑最短路,但是也可以对于没一个“环”进行维护。
每加入一个数,就更新相同数的不同余数的最小值,可以递归实现。
然后应用到此题上就是对于每一个加入的没有限制的数,直接通过两个循环分别找到这个“环”并且进行更新就好了。
对于所有的都有限制的数据进行特殊处理就好了(题库好像并没有这样的数据)
code
#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Pass"<<endl
using namespace std;
inline int read()
{
int x=0,f=1;
char ch=getchar();
while(ch>'9'||ch<'0')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*f;
}
const int N=1e4+10,M=3e5+10,INF=4557430888798830399ll;
int n,m,l,c,s[60],f[60][N];
bool vis[60][N];
bitset<M> bj[60];
void solve1()
{
bj[0][0]=true;
for(int i=1;i<=n;i++)
for(int j=1;j<=c;j++)
bj[j]|=bj[j-1]<<s[i];
for(int i=1,x;i<=m;i++)
{
bool jud=false;
x=read();
for(int j=0;j<=c;j++)
if(bj[j][x])
{
jud=true;
break;
}
if(jud) printf("Yes\n");
else printf("No\n");
}
}
void solve2()
{
memset(f,0x3f,sizeof(f));
f[0][0]=0;
for(int i=2;i<=n;i++)
if(s[i]<l)
{
memset(vis,false,sizeof(vis));
for(int j=0;j<=c;j++)
for(int k=0;k<s[1];k++)
if(!vis[j][k])
{
int temp=k,minn=INF,id,now;
while(!vis[j][temp])
{
vis[j][temp]=true;
if(minn>f[j][temp])
{
minn=f[j][temp];
id=temp;
}
temp=(temp+s[i])%s[1];
}
now=(id+s[i])%s[1];
while(now!=id)
{
f[j][now]=min(f[j][now],minn+s[i]);
minn=f[j][now];
now=(now+s[i])%s[1];
}
}
}
else
for(int j=1;j<=c;j++)
for(int k=0;k<s[1];k++)
f[j][k]=min(f[j][k],f[j-1][((k-s[i])%s[1]+s[1])%s[1]]+s[i]);
for(int i=1,x;i<=m;i++)
{
x=read();
bool jud=false;
for(int j=0;j<=c;j++)
if(f[j][x%s[1]]<=x&&f[j][x%s[1]]!=INF)
{
jud=true;
break;
}
if(jud) printf("Yes\n");
else printf("No\n");
}
}
signed main()
{
n=read();
m=read();
for(int i=1;i<=n;i++)
s[i]=read();
l=read();
c=read();
sort(s+1,s+n+1);
if(s[1]>=l) solve1();
else solve2();
return 0;
}
T3 最近公共祖先
解题思路
可以说是本次考试中最水的题了。
不难发现对于每一个新加入的黑点,造成影响的其实只有以下几部分:
以该黑色节点为根节点的子树。
该黑色节点的所有祖先以及各级祖先除了该节点所在子树的子树。
然后直接在 DFS 序上维护线段树,区间修改单点查询,然后在已经更改过的节点打上标记就好了。
考场是看上了那个部分分,然后码了棵主席树,然后我就炸了。。
code
正解
#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Pass"<<endl
#define ls x<<1
#define rs x<<1|1
using namespace std;
inline int read()
{
int x=0,f=1;
char ch=getchar();
while(ch>'9'||ch<'0')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*f;
}
const int N=2e5+10;
int n,m,s[N],top,sta[N];
int tim,id[N],fa[N],siz[N],dfn[N],dep[N];
int all,root[N];
int tot=1,head[N],nxt[N],ver[N];
int vis[N],flag;
string ch;
void add(int x,int y)
{
ver[++tot]=y;
nxt[tot]=head[x];
head[x]=tot;
}
struct Segment_Tree
{
int dat,laz;
}tre[N<<2];
void dfs(int x)
{
siz[x]=1;
dfn[x]=++tim;
id[tim]=x;
for(int i=head[x];i;i=nxt[i])
{
int to=ver[i];
if(to==fa[x]) continue;
fa[to]=x;
dep[to]=dep[x]+1;
dfs(to);
siz[x]+=siz[to];
}
}
void push_down(int x)
{
if(!tre[x].laz) return ;
tre[ls].laz=max(tre[ls].laz,tre[x].laz);
tre[rs].laz=max(tre[rs].laz,tre[x].laz);
tre[ls].dat=max(tre[ls].dat,tre[x].laz);
tre[rs].dat=max(tre[rs].dat,tre[x].laz);
tre[x].laz=0;
}
void push_up(int x)
{
tre[x].dat=max(tre[ls].dat,tre[rs].dat);
}
void update(int x,int l,int r,int L,int R,int num)
{
if(L>R) return ;
if(L<=l&&r<=R)
{
tre[x].dat=max(tre[x].dat,num);
tre[x].laz=max(tre[x].laz,num);
return ;
}
push_down(x);
int mid=(l+r)>>1;
if(L<=mid) update(ls,l,mid,L,R,num);
if(R>mid) update(rs,mid+1,r,L,R,num);
push_up(x);
}
int query(int x,int l,int r,int pos)
{
if(l==r) return tre[x].dat;
push_down(x);
int mid=(l+r)>>1;
if(pos<=mid) return query(ls,l,mid,pos);
return query(rs,mid+1,r,pos);
}
void change(int x)
{
update(1,1,tim,dfn[x],dfn[x]+siz[x]-1,s[x]);
while(x&&!vis[x])
{
vis[x]++;
update(1,1,tim,dfn[fa[x]],dfn[x]-1,s[fa[x]]);
update(1,1,tim,dfn[x]+siz[x],dfn[fa[x]]+siz[fa[x]]-1,s[fa[x]]);
x=fa[x];
}
}
signed main()
{
vis[1]=true;
n=read();
m=read();
for(int i=1;i<=n;i++)
s[i]=read();
for(int i=1,x,y;i<n;i++)
{
x=read();
y=read();
add(x,y);
add(y,x);
}
dfs(1);
for(int i=1,opt,x;i<=m;i++)
{
cin>>ch>>x;
if(ch[0]=='M')
{
flag=true;
change(x);
}
else if(!flag) printf("%d\n",-1);
else printf("%lld\n",query(1,1,tim,dfn[x]));
}
return 0;
}
35pts 主席树
#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Pass"<<endl
#define ls tre[x].l
#define rs tre[x].r
using namespace std;
inline int read()
{
int x=0,f=1;
char ch=getchar();
while(ch>'9'||ch<'0')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*f;
}
const int N=2e5+10;
int n,m,s[N],top,sta[N];
int tim,fa[N],siz[N],dfn[N],dep[N],topp[N],son[N];
int all,root[N];
int cnt,lsh[N];
int tot=1,head[N],nxt[N],ver[N];
bool flag=false;
int vis[N];
void add(int x,int y)
{
ver[++tot]=y;
nxt[tot]=head[x];
head[x]=tot;
}
struct Node
{
int opt,x;
}q[N];
struct Segment_Tree
{
int l,r,siz;
}tre[N*80];
int insert(int pre,int l,int r,int pos)
{
int x=++all;
tre[x]=tre[pre];
tre[x].siz++;
if(l==r) return x;
int mid=(l+r)>>1;
if(pos<=mid) ls=insert(tre[pre].l,l,mid,pos);
else rs=insert(tre[pre].r,mid+1,r,pos);
return x;
}
void dfs(int x)
{
if(vis[x]>=2)
{
for(int i=head[x];i;i=nxt[i])
if(fa[ver[i]]==x)
{
root[ver[i]]=insert(root[x],1,cnt,s[x]);
dfs(ver[i]);
}
}
else if(vis[x]==1)
{
// f();
// cout<<"Check: "<<x<<endl;
for(int i=head[x];i;i=nxt[i])
if(fa[ver[i]]==x)
{
if(!vis[ver[i]]) root[ver[i]]=insert(root[x],1,cnt,s[x]);
else root[ver[i]]=root[x];
dfs(ver[i]);
}
}
else
{
for(int i=head[x];i;i=nxt[i])
if(fa[ver[i]]==x)
{
root[ver[i]]=root[x];
dfs(ver[i]);
}
}
}
void dfs3(int x)
{
// f();
// cout<<x<<endl;
if(vis[x]) vis[x]=2;
for(int i=head[x];i;i=nxt[i])
{
int to=ver[i];
if(to==fa[x]) continue;
dfs3(to);
vis[x]+=(vis[to]!=0);
}
// cout<<"Check: "<<x<<' '<<vis[x]<<endl;
}
int query(int pre,int x,int l,int r)
{
if(l==r) return l;
int mid=(l+r)>>1,rsum=tre[rs].siz-tre[tre[pre].r].siz;
if(rsum) return query(tre[pre].r,rs,mid+1,r);
return query(tre[pre].l,ls,l,mid);
}
bool b[N];
void Special_Judge()
{
add(0,1);
int tmp=0;
for(int i=1;i<=m;i++)
if(!q[i].opt)
vis[q[i].x]=b[q[i].x]=1,tmp++;
sort(lsh+1,lsh+cnt+1);
cnt=unique(lsh+1,lsh+cnt+1)-lsh-1;
for(int i=1;i<=n;i++)
s[i]=lower_bound(lsh+1,lsh+cnt+1,s[i])-lsh;
dfs3(1);
dfs(0);
for(int i=1,ans;i<=m;i++)
{
if(!q[i].opt) continue;
if(b[q[i].x]) ans=lsh[s[q[i].x]];
else ans=0;
for(int j=head[q[i].x];j;j=nxt[j])
if(vis[ver[j]])
{
ans=max(ans,lsh[s[q[i].x]]);
break;
}
if(!tmp) printf("%lld\n",-1ll);
else printf("%lld\n",max(ans,lsh[query(root[0],root[q[i].x],1,cnt)]));
}
exit(0);
}
void dfs1(int x)
{
siz[x]=1;
for(int i=head[x];i;i=nxt[i])
{
int to=ver[i];
if(siz[to]) continue;
fa[to]=x;
dep[to]=dep[x]+1;
dfs1(to);
siz[x]+=siz[to];
if(siz[to]>siz[son[x]])
son[x]=to;
}
}
void dfs2(int x,int tp)
{
dfn[x]=++tim;
topp[x]=tp;
if(son[x]) dfs2(son[x],tp);
for(int i=head[x];i;i=nxt[i])
if(!dfn[ver[i]])
dfs2(ver[i],ver[i]);
}
int LCA(int x,int y)
{
if(!x||!y) return 0;
while(topp[x]^topp[y])
{
if(dep[topp[x]]<dep[topp[y]])
swap(x,y);
x=fa[topp[x]];
}
if(dep[x]>dep[y])
swap(x,y);
return x;
}
signed main()
{
n=read();
m=read();
for(int i=1;i<=n;i++)
lsh[++cnt]=s[i]=read();
for(int i=1,x,y;i<n;i++)
{
x=read();
y=read();
add(x,y);
add(y,x);
}
for(int i=1;i<=m;i++)
{
string ch;
cin>>ch;
q[i].x=read();
if(ch[0]=='Q') q[i].opt=1;
else q[i].opt=0;
if(!q[i].opt&&q[i-1].opt)
flag=true;
}
dfs1(1);
dfs2(1,1);
if(!flag) Special_Judge();
for(int i=1;i<=m;i++)
if(!q[i].opt) sta[++top]=q[i].x;
else if(!top) printf("%lld\n",-1ll);
else
{
int maxn=0;
for(int j=1;j<=top;j++)
maxn=max(maxn,s[LCA(sta[j],q[i].x)]);
printf("%lld\n",maxn);
}
return 0;
}
8.3考试总结(NOIP模拟19)[最长不下降子序列·完全背包问题·最近公共祖先]的更多相关文章
- [考试总结]noip模拟19
连挂3场 \(\color{green}{\huge{\text{菜}}}\) 真 . 挂分王 ... 没什么好说的了,菜就是了. \(T1\) 一波手推想到了性质 \(1\),然后因为数组原因挂成比 ...
- 6.17考试总结(NOIP模拟8)[星际旅行·砍树·超级树·求和]
6.17考试总结(NOIP模拟8) 背景 考得不咋样,有一个非常遗憾的地方:最后一题少取膜了,\(100pts->40pts\),改了这么多年的错还是头一回看见以下的情景... T1星际旅行 前 ...
- 5.23考试总结(NOIP模拟2)
5.23考试总结(NOIP模拟2) 洛谷题单 看第一题第一眼,不好打呀;看第一题样例又一眼,诶,我直接一手小阶乘走人 然后就急忙去干T2T3了 后来考完一看,只有\(T1\)骗到了\(15pts\)[ ...
- 5.22考试总结(NOIP模拟1)
5.22考试总结(NOIP模拟1) 改题记录 T1 序列 题解 暴力思路很好想,分数也很好想\(QAQ\) (反正我只拿了5pts) 正解的话: 先用欧拉筛把1-n的素数筛出来 void get_Pr ...
- 2021.8.3考试总结[NOIP模拟29]
T1 最长不下降子序列 数据范围$1e18$很不妙,但模数$d$只有$150$,考虑从这里突破. 计算的式子是个二次函数,结果只与上一个值有关,而模$d$情况下值最多只有$150$个,就证明序列会出现 ...
- 2021.9.17考试总结[NOIP模拟55]
有的考试表面上自称NOIP模拟,背地里却是绍兴一中NOI模拟 吓得我直接文件打错 T1 Skip 设状态$f_i$为最后一次选$i$在$i$时的最优解.有$f_i=max_{j<i}[f_j+a ...
- [考试总结]noip模拟23
因为考试过多,所以学校的博客就暂时咕掉了,放到家里来写 不过话说,vscode的markdown编辑器还是真的很好用 先把 \(noip\) 模拟 \(23\) 的总结写了吧.. 俗话说:" ...
- [考试总结]noip模拟41
发现长时间鸽博客会导致 rp--,所以今天来补一补 这个题目其实不是很毒瘤,然而是非常毒瘤... 题目不说请就是非常非常的烦人 首先 \(T1\) 就整整有两个歧义的地方,也就是说我们一共有 \(4\ ...
- [考试反思]NOIP模拟测试19:洗礼
[]260 []230[]210 []200[8]170[9]160 这套题一般,数据很弱,T1T2暴力都能A,而且都是一些思维题,想不到就爆0. 原因不明,很多一直很强的人在这一次滑铁卢了,于是我个 ...
随机推荐
- Binding(三):资源和ValueConverter
这节讲资源和值转换器(ValueConverter). 资源 在XAML中,我们想要使用外部的数据或者类,需要引入其命名空间,然后将其定义为XAML页面的资源,供给控件使用,或者我们需要封装一个共用的 ...
- Linux云计算-02_CentOS Linux 7.X系统管理
Linux系统安装完毕,需要对Linux系统进行管理和维护,让Linux服务器能真正应用于企业中. 本章介绍Linux系统32位与64位区别.内核命名规则.引导原理.启动流程.TCP/IP协议概述.I ...
- Ubuntu 更换内核
Ubuntu 更换内核步骤: 下载内核源码,例如wget https://git.kernel.org/torvalds/t/linux-4.17-rc2.tar.gz 按照需要的环境,sudo ap ...
- Java程序设计(2021春)——第一章课后题(选择题+编程题)答案与详解
Java程序设计(2021春)--第一章课后题(选择题+编程题)答案与详解 目录 Java程序设计(2021春)--第一章课后题(选择题+编程题)答案与详解 第一章选择题 1.1 Java与面向对象程 ...
- 资源:docker-compose下载路径
docker-compose下载路径: compose所有版本:https://github.com/docker/compose/releases
- XCTF re-100
一.无壳并拉入ida64静态调试(注释说的很明白了) 二.confuseKey是个关键函数,进入看看 发现就是将我们所输入的字符串分割,并把顺序调换了,调回来就是我们的flag. 三.flag: 提交 ...
- maevn的nexus私库搭建
使用nexus搭建maven仓库(本地私服) 前言:我们在使用maven的时候,对于项目所依赖的jar包,maven默认会在中央仓库下载jar包,到本地的磁盘目录(如果没有配置则是用户目录下/.m2/ ...
- git使用---安装,提交,回退,修改,分支,标签等
下面是对git的各种使用及命令的基础使用,来自廖雪峰老师的git教程,这个收录下,作为git的使用总结. github上面地址为:https://github.com/Zhangguoliu/lear ...
- dp 套 dp扯谈
1.[扯谈概念] \(dp\) 套 \(dp\) 其实也就是 \(dp\) . 这里就定义下面两个概念: 内层 \(dp\) 表示的是被套在里面的那个 \(dp\) 外层 \(dp\) 表示的是最外面 ...
- C语言typedef的用法详解
C语言允许为一个数据类型起一个新的别名,就像给人起"绰号"一样. 起别名的目的不是为了提高程序运行效率,而是为了编码方便.例如有一个结构体的名字是 stu,要想定义一个结构体变量就 ...