20211109NOIP模拟赛
前言:
2021.11.11 没想到我居然回来更新了!!
题外话:
考前一天晚上,由于过分装逼而被两位省二巨佬调戏。
在寝室里面反复鞭尸,导致考前。
我方本着公平公正的原则意识,只为证明自己是某谷最菜绿名这一事实,宣告自己只会抄tj,但两位巨佬却罔顾事实只凭一面之词污蔑我方。
为了证明清白,我立下誓言,这次考试上50我【该内容已被删除】,上100我【该内容已被删除】
(为保护当事人隐私采取必要措施)
结果:110,总之就是非常后悔。
正题
T1:
得分:
下面是我考场上的思路:
考场上,看着题,我先想了一个的暴力应对的情况(写挂调了40分钟),然后左看看,右看看,感觉不会了(蒟蒻水平有目共睹)。
在看看数据,看到了的情况感觉打表可以找规律。
10分钟后,表打出来了,这时已经开考一个小时了(蒟蒻水平可见一斑)。
显得蛋疼又把的情况打出来了,发现了规律。50分到手。
上个厕所,蹲坑时突发奇想要不把所有的都打一遍??
然后打着发现,一定,时,都是一样的,而与的值有关。
然后就开始想着,怎么构造序列,我的搜索有点奇怪,每次的输出有规律,然后我就按照规律来模拟,过了(跑的飞快)。
代码:
点击查看代码
#include<bits/stdc++.h>
#define in read()
#define int long long
using namespace std;
inline int read(){
static char ch;
int res=0,sign=1;
while((ch=getchar())<'0'||ch>'9'){
if(ch=='-'){
sign=-1;
}
}
res=ch-'0';
while((ch=getchar())>='0'&&ch<='9'){
res=res*10+ch-'0';
}
return res*sign;
}
int n,k;
int a[100001],ans;
int qz[100001];
int check(int a[]){
int res=0;
for(int i=1;i<=n;i++){
qz[i]=qz[i-1]+a[i];
}
for(int i=1;i<=n;i++){
for(int j=i;j<=n;j++){
if((qz[j]-qz[i-1])%2==1){
res++;
}
}
}
return res;
}
int ans2[100001];
void dfs(int pos,int num,int *a){
if(pos==n+1){
if(num==k+1){
int tmp=check(a);
ans=max(ans,tmp);
if(ans==tmp){
for(int i=1;i<=n;i++){
ans2[i]=a[i];
}
}
}
return ;
}
dfs(pos+1,num,a);
a[pos]=1;
dfs(pos+1,num+1,a);
a[pos]=0;
}
void solve1(){
ans=0;
dfs(1,1,a);
printf("%lld\n",check(ans2));
for(int i=1;i<=n;i++){
cout<<ans2[i]<<' ';
}
cout<<endl;
return ;
}
signed main()
{
// freopen("string.in","r",stdin);
// freopen("string.out","w",stdout);
n=in,k=in;
if(k==0){
cout<<0<<endl;
return 0;
}
if(n<=10){
solve1();
}else if(k==1){
if(n%2==0){
printf("%lld\n",(n/2)*(n/2+1));
a[n/2]=1;
for(int i=1;i<=n;i++){
cout<<a[i]<<' ';
}
cout<<endl;
}else{
printf("%lld\n",((n+1)/2)*((n+1)/2));
a[n/2+1]=1;
for(int i=1;i<=n;i++){
cout<<a[i]<<' ';
}
cout<<endl;
}
}else if(k==2){
if(n%2==0){
printf("%lld\n",(n+2)*n/4);
a[1]=1;
a[n/2+1]=1;
for(int i=1;i<=n;i++){
cout<<a[i]<<' ';
}
cout<<endl;
}else{
printf("%lld\n",(n+1)*(n+3)/4-(n+1)/2);
a[1]=1;
a[n/2+2]=1;
for(int i=1;i<=n;i++){
cout<<a[i]<<' ';
}
cout<<endl;
}
}else{
if(n%2==0){
printf("%lld\n",(n/2)*(n/2+1));
for(int i=1;i<=k-1;i++){
a[i]=1;
}
if((n-k+1)%2==0){
a[(n-k+1)/2+k-1]=1;
}else{
a[(n-k+1)/2+k]=1;
}
for(int i=1;i<=n;i++){
cout<<a[i]<<' ';
}
cout<<endl;
}else{
printf("%lld\n",((n+1)/2)*((n+1)/2));
for(int i=1;i<=k-1;i++){
a[i]=1;
}
if((n-k+1)%2==0){
a[(n-k+1)/2+k]=1;
}else{
a[(n-k+1)/2+k]=1;
}
for(int i=1;i<=n;i++){
cout<<a[i]<<' ';
}
cout<<endl;
}
}
return 0;
}
T2:
得分:
没办法,把T1做出来都10:30了,我还在那里手造数据测了半天。
看看题,想打一发主席树,但是过了一天就忘完了,只好打一个的暴力水了10分;
正解:
有点不好讲:
主要是维护一个lst[],存储当前的值上一次出现的位置;
一个区间不重复只能是,该区间所有的lst的最大值小于左端点,这就变成了线段树单点修改,区间询问最大值了。蒟蒻太菜了.
还有一个重点就是,在更新一个节点后,要把他影响的所有区间都判断,这里就要对区间进行建树,把区间从大到小双关键字排序,然后存一个栈,枚举到一个区间就退栈找到他的父亲然后建立关系,这里排序前要存L,R,还要记录id,pos的映射关系。
在树上check时递归即可:
代码:
重口味:
点击查看代码
#include<bits/stdc++.h>
#define re register
using namespace std;
char rbuf[30000002];
int pt=-1;
inline int read(){
re int t=0;re char v=rbuf[++pt];
while(v<'0')v=rbuf[++pt];
while(v>='0')t=(t<<3)+(t<<1)+v-48,v=rbuf[++pt];
return t;
}
set<int>S[500002];
set<int>::iterator it;
int n,m,q,mn[2000002],X,Y,nxt[500002],lst[500002],c[500002],rd[500002],head[500002],cnt,fa[500002],tp,ans[500002],T,L[500002],R[500002],pos[500002],pre[500002];
struct node{int x,y,id;bool operator <(const node A)const{return x==A.x?y>A.y:x<A.x;};}p[500002],stk[500002];
inline void cg(re int p,re int l,re int r){
if(l==r){mn[p]=Y;return;}
re int mid=l+r>>1;
if(X<=mid)cg(p<<1,l,mid);
else cg(p<<1|1,mid+1,r);
mn[p]=max(mn[p<<1],mn[p<<1|1]);
}
inline int ask(re int p,re int l,re int r,re int x,re int y){
if(l>=x&&r<=y)return mn[p];
re int s=0,mid=l+r>>1;
if(x<=mid)s=max(s,ask(p<<1,l,mid,x,y));
if(y>mid)s=max(s,ask(p<<1|1,mid+1,r,x,y));
return s;
}
inline void build(re int p,re int l,re int r){
if(l==r){mn[p]=lst[l];return;}
re int mid=l+r>>1;
build(p<<1,l,mid),build(p<<1|1,mid+1,r);
mn[p]=max(mn[p<<1],mn[p<<1|1]);
}
inline void cg(re int x,re int y){
if(nxt[x])X=nxt[x],Y=lst[x],cg(1,1,n);//单点修改
nxt[lst[x]]=nxt[x],lst[nxt[x]]=lst[x],S[c[x]].erase(x),c[x]=y;//对原来值的前后关系的修改
re int o=lst[x];
it=S[y].upper_bound(x),lst[x]=nxt[x]=0;
if(it!=S[y].end())X=*it,nxt[x]=X,Y=x,cg(1,1,n);//后面的y前缀修改
if(it!=S[y].begin())--it,lst[x]=*it;
S[y].insert(x),lst[nxt[x]]=nxt[lst[x]]=x,X=x,Y=lst[x];
if(o^Y)cg(1,1,n);//改现在这个点的前缀
}
set<node>s;
set<node>::iterator it1;
inline void ck(re int x){
if(ask(1,1,n,L[x],R[x])<L[x]){
s.erase((node){L[x],R[x],x});
ans[x]=T;
if(fa[x]&&(!(--rd[fa[x]])))rd[fa[x]]=-1,s.insert(p[pos[fa[x]]]),ck(fa[x]);
}
}
int main(){
freopen("artist4.in","r",stdin);
fread(rbuf,1,30000000,stdin),n=read(),m=read(),q=read();
for(re int i=1;i<=n;++i)c[i]=read();
for(re int i=1;i<=n;++i){
lst[i]=pre[c[i]],nxt[pre[c[i]]]=i;//lst记录c[i]上一次出现的位置 ,nxt是下一次出现的位置
pre[c[i]]=i,S[c[i]].insert(S[c[i]].end(),i);//set维护没个值出现的位置
}
build(1,1,n);
for(re int i=1;i<=m;++i)p[i].x=L[i]=read(),p[i].y=R[i]=read(),ans[i]=i+m,p[i].id=i;
sort(p+1,p+m+1);//对边排序
for(re int i=1;i<=m;++i){//对区间建树
if(p[i].x==p[i].y||ask(1,1,n,p[i].x,p[i].y)<p[i].x){ans[p[i].id]=0,rd[p[i].id]=-1;continue;}//满足条件,所有在该区间出现的值的上一次都在区间外
pos[p[i].id]=i;//顺序
while(tp&&stk[tp].y<p[i].x)--tp;//退栈找到父亲
fa[p[i].id]=stk[tp].id,stk[++tp]=p[i],++rd[fa[p[i].id]];//父子建立关系
}
for(re int i=1;i<=m;++i)if(!rd[p[i].id])s.insert(s.end(),p[i]);//放入set
for(re int i=1;i<=q;++i){//修改
re int x=read(),y=read();
T=i,cg(x,y);//序列修改
it1=s.upper_bound((node){x,0,0});//找儿子
if(it1!=s.begin())--it1,ck((*it1).id);
}
re int s=0;
for(re int i=1;i<=m;++i)s^=ans[i];
printf("%d",s);
}
小清新:
点击查看代码
#include<bits/stdc++.h>
#define in read()
#define re register
using namespace std;
int n,m,q,X,Y;
int maxn[500002<<2];
int nxt[500002],lst[500002],c[500002],rd[500002],head[500002],cnt,fa[500002],tp,ans[500002],T,L[500002],R[500002],pos[500002],pre[500002];
inline int read(){
static char ch;
int res=0;
while((ch=getchar())<'0'||ch>'9'){
}
res=ch-'0';
while((ch=getchar())>='0'&&ch<='9'){
res=res*10+ch-'0';
}
return res;
}
struct node{
int l,r,id;
bool operator <(const node A)const{return l==A.l?r>A.r:l<A.l;};
}p[500002],stk[500002];
// 线段树
//==============================================================================
void build(int k,int l,int r){
if(l==r){
maxn[k]=lst[l];
return ;
}
re int mid=l+r>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
maxn[k]=max(maxn[k<<1],maxn[k<<1|1]);
}
inline void change(int k,int l,int r){
if(l==r){
maxn[k]=Y;
return ;
}
re int mid=l+r>>1;
if(X<=mid){
change(k<<1,l,mid);
}else{
change(k<<1|1,mid+1,r);
}
maxn[k]=max(maxn[k<<1],maxn[k<<1|1]);
}
inline int query(int k,int l,int r,int x,int y){
if(l>=x&&r<=y){
return maxn[k];
}
re int res=-1;
re int mid=l+r>>1;
if(x<=mid){
res=max(res,query(k<<1,l,mid,x,y));
}
if(mid<y){
res=max(res,query(k<<1|1,mid+1,r,x,y));
}
return res;
}
//==============================================================================
set<node> s;//叶子区间set
set<node>::iterator it1;
set<int> S[500002];
set<int>::iterator it;//每一个值
inline void cg(int x,int y){
if(nxt[x]){
X=nxt[x],Y=lst[x];
change(1,1,n);
}//单点修改
lst[nxt[x]]=lst[x];
nxt[lst[x]]=nxt[x];//对原来值的前后关系的修改
S[c[x]].erase(x);
c[x]=y;
re int o=lst[x];
it=S[y].upper_bound(x),lst[x]=nxt[x]=0;
if(it!=S[y].end()) X=*it,nxt[x]=X,Y=x,change(1,1,n);//后面的y前缀修改,更新现在的后缀
if(it!=S[y].begin()) --it,lst[x]=*it;//更新现在的前缀
S[y].insert(x);
lst[nxt[x]]=nxt[lst[x]]=x;//前后缀
X=x,Y=lst[x];
if(Y^o) change(1,1,n);//改现在这个点的前缀
}
void ck(int x){//递归查
if(query(1,1,n,L[x],R[x])<L[x]){//该区间的左右端点
s.erase((node){L[x],R[x],x});
ans[x]=T;
if(fa[x]&&(!(--rd[fa[x]]))) rd[fa[x]]=-1,s.insert(p[pos[fa[x]]]),ck(fa[x]);//儿子查完了,父亲变成儿子再查
}
}
signed main(){
//freopen("artist4.in","r",stdin);
n=in,m=in,q=in;
for(re int i=1;i<=n;i++){
c[i]=in;
}
for(re int i=1;i<=n;i++){
lst[i]=pre[c[i]];
nxt[pre[c[i]]]=i;
pre[c[i]]=i;
S[c[i]].insert(S[c[i]].end(),i);
}
build(1,1,n);
for(re int i=1;i<=m;i++){
p[i].l=L[i]=in;
p[i].r=R[i]=in;
p[i].id=i;
ans[i]=i+m;
}
sort(p+1,p+m+1);
for(re int i=1;i<=m;i++){
if(p[i].l==p[i].r||query(1,1,n,p[i].l,p[i].r)<p[i].l){//开始时就有序
ans[p[i].id]=0;
rd[p[i].id]=-1;
continue;
}
pos[p[i].id]=i;
while(tp&&stk[tp].r<p[i].l) tp--;
fa[p[i].id]=stk[tp].id;
stk[++tp]=p[i];
++rd[fa[p[i].id]];
}
for(re int i=1;i<=m;i++){
if(rd[p[i].id]!=-1){
s.insert(s.end(),p[i]);
}
}
int x,y;
for(re int i=1;i<=q;i++){
x=in,y=in;
T=i;
cg(x,y);
it1=s.upper_bound((node){x,0,0});//该顶点影响的区间
if(it1!=s.begin()){
--it1;
ck((*it1).id);
}
}
int s=0;
for(re int i=1;i<=m;++i)s^=ans[i];
printf("%d",s);
return 0;
}
T3:
咕咕咕~~
不咕咕咕了!!
对一整颗树来说,先找出他的直径还有每个点到直径两个端点的两个距离。
用快速幂来求整棵树的情况种数,枚举答案d。注意d如果是某个点到一个端点的最短距离则退出,因为他作为答案不合法了。
枚举到一个答案,减去出现改答案的情况数,由于减去的情况可以反色所以要加一后快速幂。
先放代码:
点击查看代码
#include<bitsdc++.h>
using namespace std;
#define ll long long
const int N=1e6+10,mod=1e9+7;
int dis[N][2],n;
vector<int>g[N];
int x,y;
inline void Dfs(int u,int f,int ki){//搜索整棵树
dis[u][ki]=dis[f][ki]+1;
for(int i=0;i<g[u].size();i++)if(f^g[u][i])Dfs(g[u][i],u,ki);
}
int c[N];
bool vis[N];
inline int ksm(int a,int b){//快速幂
int res=1;
while(b){
if(b&1)res=(ll)res*a%mod;
a=(ll)a*a%mod;
b>>=1;
}
return res;
}
int main(){
scanf("%d",&n);
int u,v;
for(int i=1;i<n;i++)scanf("%d%d",&u,&v),g[u].push_back(v),g[v].push_back(u);
dis[0][0]=dis[0][1]=-1;
Dfs(1,0,0);//找到直径的一个端点
int mx=0;
for(int i=1;i<=n;i++)
if(dis[i][0]>mx){
x=i;
mx=dis[i][0];
}
Dfs(x,0,0);//找到直径的另一个端点
mx=0;
for(int i=1;i<=n;i++)
if(dis[i][0]>mx){
y=i;
mx=dis[i][0];
}
Dfs(y,0,1);//记录每个点到两个端点的距离
for(int i=1;i<=n;i++)
for(int ki=0;ki<2;ki++)
c[dis[i][ki]]++;//每个距离出现的次数
for(int i=1;i<=n;i++)vis[min(dis[i][0],dis[i][1])]=true; //最小距离
int sum=ksm(2,n),now,rest=n+1;//sum总情况数,rest是每一步减去的情况数,由于可以反色所加一
int mxd=dis[x][1];//上界
int ans=0;
for(int d=mxd;~d;d--){
if(vis[d]){//d的下界,再往下d就不合法,就不是最大距离了
ans=(ans+(ll)sum*d)%mod;
cout<<ans;
return 0;
}
rest-=c[d];//减去该长度的次数
now=ksm(2,rest);//需要减去的范围情况
ans=(ans+((ll)(sum-now+mod)%mod)*d%mod)%mod;//加上外围的确定的情况
sum=now;
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效