CSP-S模拟赛三(仍然是难度远超CSP-S)
前言
现在我将以最悲伤的心情,记录下我的第一次爆 000 记录。
T1
题面:
(我恨 MLE。)
对于 30%30\%30% 的数据,我们可以用 ST 表然后 O(n2)O(n^2)O(n2) 暴力枚举(我因为开了 long long
,空间复杂度太高然后 MLE 了……)。
对于 100%100\%100% 的数据,考虑使用 O(n)O(n)O(n) 的做法。
我们美剧一个同学,把他作为一段区间中最矮的同学,然后看看以他为最矮的,最远能延伸到哪里。
但是如果按照上面的方法直接暴力做的话,时间复杂度就是 O(n2)O(n^2)O(n2) 的。有没有一种办法可以把里面的循环直接去掉呢?
既然要把里面的循环去掉,就说明查找这个区间所遍历的范围是固定的,那就定个很简单的值:相邻的两个。所以说当相邻的两个同学身高比中间这个同学的身高要高的话,就可以拓宽范围。
但如果我相邻的相邻的那个同学身高也比我高呢?那肯定也要合并进来。按照我们上面的操作,我相邻的同学的相邻的同学应该被囊括进了我相邻的那个同学,也就是说我在把我相邻的那个同学囊括进来的时候,还要把我相邻的相邻的那个同学囊括进来,那什么算法可以干这个事呢?对了:并查集。
因此考虑使用并查集,因为我囊括的同学都是比我身高高的同学,所以我们应该按照身高从高到低的顺序依次用并查集。
然后就是写代码,代码自己看下面:
#include<bits/stdc++.h>
#define code using
#define by namespace
#define plh std
code by plh;
namespace fastio
{int read(){int z=0,f=1;char c=getchar();while(c<'0'||c>'9'){if(c=='-'){f=-1;}c=getchar();}while(c>='0'&&c<='9'){z=z*10+c-'0';c=getchar();}return z*f;}
}
using namespace fastio;
int n,siz[2000006],fa[2000006],id[2000006];
long long a[2000006];
bool vis[2000006];
unsigned long long ans,mn[2000006],mx[2000006];
bool cmp(int x,int y)
{return a[x]>a[y];
}
int find(int x)
{if(fa[x]==x){return x;}return fa[x]=find(fa[x]);
}
void merge(int x,int y)
{int fx=find(x),fy=find(y);if(fx!=fy){fa[fy]=fx;siz[fx]+=siz[fy];mn[fx]=min(mn[fx],mn[fy]);mx[fx]=max(mx[fx],mx[fy]);ans=max(ans,mx[fx]*mn[fx]*siz[fx]);}
}
signed main()
{
// freopen("sketch.in", "r", stdin);
// freopen("sketch.out", "w", stdout);n=read();
// vector<int>fa(n+1),id(n+1);
// vector<long long>a(n+1),siz(n+1);for(int i=1;i<=n;i++){a[i]=read();mn[i]=mx[i]=a[i];siz[i]=1;ans=max(ans,a[i]*a[i]*1ull);id[i]=fa[i]=i;}sort(id+1,id+n+1,cmp);for(int i=1;i<=n;i++){vis[id[i]]=1;if(vis[id[i]-1]){merge(id[i],id[i]-1);}if(vis[id[i]+1]){merge(id[i],id[i]+1);}}cout<<ans;return 0;
}
T2
题面:
一道很恶心的线段树题。
正解的大致意思就是用一颗线段树来维护已经固定了的点,然后看看每个区间的端点的奇偶性是否相同,如果相同,那就把中间的全填上相同奇偶性的值,如果不同, 那就必须多一个郁闷值了,因为不管你怎么填它都是一样的,所以这种区间就先跳过不填。
这里还要注意一下:根据贪心可以得到:当两端奇偶性相同时,应该从长度最短的区间开始填,直到填不了为止。
剩下的作者也不想写了,因为题目实在是太复杂,代码解释起来也很费力,如果可以,我以后单独开一篇文章讲一讲这题。
代码先放在这:
#include<bits/stdc++.h>
#define int long long
#define code using
#define by namespace
#define plh std
code by plh;
namespace fastio
{int read(){int z=0,f=1;char c=getchar();while(c<'0'||c>'9'){if(c=='-'){f=-1;}c=getchar();}while(c>='0'&&c<='9'){z=z*10+c-'0';c=getchar();}return z*f;}void write(int x,int k){if(x<0){putchar('-');x=-x;}if(x==0){if(!k){putchar('0');}return;}write(x/10,k+1);putchar(char(x%10+'0'));}
}
struct Tree{int sumlen[1000006],cntlen[1000006];int lowbit(int x){return x&-x;}void add(int x){for(int i=x;i<=1000000;i+=lowbit(i)){sumlen[i]+=x;cntlen[i]++;}}void del(int x){for(int i=x;i<=1000000;i+=lowbit(i)){sumlen[i]-=x;cntlen[i]--;}}pair<int,int> qry(int x){int ans=0,y=0;for(int i=20;i>=0;i--){if(y+(1<<i)<=1000000&&x>=sumlen[y+(1<<i)]){y+=(1<<i);x-=sumlen[y];ans+=cntlen[y];}}ans+=x/(y+1);x%=(y+1);return {ans,x};}
}tr[2];
using namespace fastio;
int n,m,q,cnt1,cnt2,a[1000006],t[2];
set<int>s;
void query()
{if(s.size()==2){if(t[0]>=n||t[1]>=n){write(0,0);return;}write(1,0);return;}int ans=cnt1*2+cnt2;int st[5],top=0;if(a[1]==0){auto it=++s.begin();int len=(*it)-1;tr[a[0]&1].del(len);st[++top]=(a[*it]&1);st[++top]=-len;ans--;} if(a[n]==0) {auto it=--s.end();it--;int len=n-(*it);tr[a[*it]&1].del(len);st[++top]=(a[*it]&1);st[++top]=len;ans--;}auto it0=tr[0].qry(t[0]),it1=tr[1].qry(t[1]);ans-=it0.first*2;ans-=it1.first*2;while(top){bool fl=false;if(st[top]<0){fl=true;st[top]=-st[top];}if(st[top-1]){if(it1.second>=st[top]){it1.second-=st[top];ans--;}}else{if(it0.second>=st[top]){it0.second-=st[top];ans--;}}if(!fl){tr[st[top-1]].add(st[top]);}else{tr[0].add(st[top]);}top-=2;}write(ans,0);
}
void push(auto x,auto y)
{if((*x)+1<(*y)){if((*x)==0||(*y)==n+1||(a[*x]&1)==(a[*y]&1)){tr[a[*x]&1].add((*y)-(*x)-1);cnt1++;} else{cnt2++;}}else{if((*x)==0||(*y)==n+1||(a[*x]&1)==(a[*y]&1)){return;}else{cnt2++;}}
}
void pop(auto x,auto y)
{if((*x)+1<(*y)){if((*x)==0||(*y)==n+1||(a[*x]&1)==(a[*y]&1)){tr[a[*x]&1].del((*y)-(*x)-1);cnt1--;} else{cnt2--;}}else{if((*x)==0||(*y)==n+1||(a[*x]&1)==(a[*y]&1)) {return;}else{cnt2--;}}
}
void delet(int x)
{auto it=s.lower_bound(x);auto itl=it,itr=it;itl--,itr++;pop(itl,it),pop(it,itr);a[x]=0;push(itl,itr);s.erase(it);
}
void adde(int x,int y)
{auto itr=s.lower_bound(x);auto itl=itr;itl--;pop(itl,itr);s.insert(x);a[x]=y;auto it=s.lower_bound(x);itl=it,itr=it;itl--,itr++;push(itl,it);push(it,itr);
}
signed main()
{cin>>n>>m>>q;s.insert(0),s.insert(n+1);for(int i=1;i<=n;i++){t[read()&1]++;}for(int i=1,x,y;i<=m;i++){x=read(),y=read();a[x]=y;s.insert(x);t[y&1]--;}for(auto i=++s.begin(),j=s.begin();i!=s.end();j=i,i++){push(j,i);}while(q--){int op=read();if(op==1){int x=read();t[a[x]&1]++;delet(x);}else{int x=read(),y=read();adde(x,y);t[a[x]&1]--;}query();putchar('\n');}return 0;
}
T3
题面:
一道水到不能再水的题……(但我还是挂了……)
很简单:枚举一个点 iii 作为起点,然后按照 ddd 的距离跳,看看有多少个 111 在上面,那么其他的 111 就是要删的,然后暴力枚举一下得出答案即可。
时间复杂度:O(d×nd)=O(n)O(d\times\cfrac{n}{d})=O(n)O(d×dn)=O(n)。
考场上写 freopen
不小心写错了,然后就 CE 了……(你问我为什么会 CE,我只能说我没编译……)
T4
题面:
题目很短,但很恶心,这里不多讲(因为这一道题用了七种算法,你让我怎么讲……),以后有时间单独开一篇文章讲一讲。
代码放在这展览一下:
#include<bits/stdc++.h>
#define int long long
#define code using
#define by namespace
#define plh std
code by plh;
const int LIM=100;
const int B=100;
int n,m;
namespace Tree
{vector<pair<int,int>>e[600006];void build(int x,int y,int z){e[x].push_back({y,z});e[y].push_back({x,z});}int st[600006][26];//st表 int lg[600006];//log2函数 int siz[300006];//子树大小 int tdfn[300006];//欧拉序 int num=0,dfn[300006];//欧拉序+1 int depth[300006];//深度 int dis[300006];//到根节点的距离void dfs(int x,int fx){dfn[x]=++num;depth[x]=depth[fx]+1;st[num][0]=x;siz[x]=1;tdfn[x]=++tdfn[0];for(auto i:e[x]){if(i.first!=fx){dis[i.first]=dis[x]+i.second;dfs(i.first,x);st[++num][0]=x;siz[x]+=siz[i.first];}}}void st_(){for(int i=2;i<=(n<<1);i++){lg[i]=lg[i>>1]+1;}dfs(1,0);for(int i=1;i<=20;i++){for(int j=1;j+(1<<i)-1<=(n<<1);j++){if(depth[st[j][i-1]]<depth[st[j+(1<<i-1)][i-1]]){st[j][i]=st[j][i-1];}else{st[j][i]=st[j+(1<<i-1)][i-1];}}}}int lca(int x,int y){x=dfn[x],y=dfn[y];if(x>y){swap(x,y);}int k=lg[y-x+1];return depth[st[x][k]]<depth[st[y-(1<<k)+1][k]]?st[x][k]:st[y-(1<<k)+1][k];}int dist(int x,int y){return dis[x]+dis[y]-dis[lca(x,y)]*2;}int isfa(int x,int y){return tdfn[x]<=tdfn[y]&&tdfn[x]+siz[x]-1>=tdfn[y];}
}
using Tree::dfn;
using Tree::lca;
using Tree::dist;
using Tree::dis;
using Tree::isfa;
namespace VirtualTree
{int fa[300006];//父亲节点int son[300006];//链最长的儿子节点 int len[300006];//底下的最长链 int w[300006];//每个点到父亲节点的边权vector<pair<int,int>>e[300006];//虚树 void dfs(int x,int fx){fa[x]=fx;son[x]=0;for(auto i:e[x]){if(i.first==fx){continue;}w[i.first]=i.second;dfs(i.first,x);if(len[i.first]+i.second>=len[son[x]]+w[son[x]]){son[x]=i.first;}}len[x]=(son[x]?w[son[x]]+len[son[x]]:0);} vector<pair<int,int>> build(vector<int>&v){sort(v.begin(),v.end(),[&](int a,int b){return dfn[a]<dfn[b];});vector<int>vv(v.size()+v.size()-1);int cnt=0;for(int i=0;i<v.size()-1;i++){vv[cnt++]=lca(v[i],v[i+1]);}for(auto i:v){vv[cnt++]=i;}sort(vv.begin(),vv.end());vv.erase(unique(vv.begin(),vv.end()),vv.end());sort(vv.begin(),vv.end(),[&](int a,int b){return dfn[a]<dfn[b];});for(auto i:vv){e[i].clear();}static int top=1,stk[300006];stk[top]=vv[0];int rt=vv[0];for(int i=1;i<vv.size();i++){int x=vv[i];while(top>1&&!isfa(stk[top],x)){e[stk[top]].push_back({stk[top-1],dis[stk[top]]-dis[stk[top-1]]});e[stk[top-1]].push_back({stk[top],dis[stk[top]]-dis[stk[top-1]]});top--;}stk[++top]=x;}while(top>1){e[stk[top]].push_back({stk[top-1],dis[stk[top]]-dis[stk[top-1]]});e[stk[top-1]].push_back({stk[top],dis[stk[top]]-dis[stk[top-1]]});top--;}//下面是贪心static int dis[300006];static bool vis[300006];for(auto i:vv){vis[i]=0;}queue<int>q;q.push(rt);dis[rt]=0;vis[rt]=1;while(!q.empty()){int x=q.front();q.pop();for(auto i:e[x]){int y=i.first;int z=i.second;if(vis[y]){continue;}vis[y]=1;dis[y]=dis[x]+z;q.push(y);}}rt=0;int mx=-10;for(auto i:v){if(dis[i]>=mx){mx=dis[i];rt=i;}}dfs(rt,0);w[rt]=0;sort(vv.begin(),vv.end(),[&](int x,int y)->bool{if(x==rt){return 1;}return len[x]+w[x]>len[y]+w[y];});vector<pair<int,int>>ans;vector<int>tmp;ans.push_back({rt,0ll});for(auto i:vv){vis[i]=0;}int s=0;for(auto i:vv){if(ans.size()>=LIM){break;}if(vis[i]){tmp.push_back(i);continue;}int u=i;s+=len[i]+w[i];vis[i]=1;while(son[u]){u=son[u];vis[u]=1;}ans.push_back({u,s});}cnt=0;while(ans.size()<v.size()&&ans.size()<LIM){ans.push_back({tmp[cnt++],s});}return ans;}
}
vector<pair<int,int>> merge(vector<pair<int,int>>&x,vector<pair<int,int>>&y)
{vector<int>a(x.size()+y.size());for(int i=0;i<x.size();i++){a[i]=x[i].first;}for(int i=0;i<y.size();i++){a[i+x.size()]=y[i].first;}return VirtualTree::build(a);
}
vector<pair<int,int>>st[3006][26];
int lg[300006],a[300006];
int zb(int x)
{return (x-1)/B+1;
}
int query(int l,int r,int k)
{int ll=zb(l),rr=zb(r);static int stk[300006];int top=0;if(ll==rr){for(int i=l;i<=r;i++){stk[++top]=a[i];}}else{for(int i=l;i<=ll*B;i++){stk[++top]=a[i];}for(int i=(rr-1)*B+1;i<=r;i++){stk[++top]=a[i];}l=ll+1,r=rr-1;if(l<=r){int k=lg[r-l+1];vector<pair<int,int>>lv=st[l][k];vector<pair<int,int>>rv=st[r-(1<<k)+1][k];for(auto i:lv){stk[++top]=i.first;}for(auto i:rv){stk[++top]=i.first;}}}vector<int>v(stk+1,stk+1+top);vector<pair<int,int>>ans=VirtualTree::build(v);return ans[k-1].second;
}
void decode(int &l,int &r,int &k,int lstans,int testop)
{lstans%=19260817;if(testop){l^=lstans;l=(l%n+n)%n+1;r^=lstans;r=(r%n+n)%n+1;if(l>r){swap(l, r);}k^=lstans; k=(k%min(r-l+1,100ll))+1;}
}
int op;
signed main()
{int c;cin>>c;cin>>op>>n;for(int i=1,x,y,z;i<n;i++){cin>>x>>y>>z;Tree::build(x,y,z);}Tree::st_();for(int i=1;i<=n;i++){cin>>a[i];}for(int i=2;i<=n;i++){lg[i]=lg[i>>1]+1;}int m=zb(n);for(int i=1;i<=n;i+=B){int l=i,r=min(i+B-1,n);vector<int>vec(a+l,a+r+1);st[zb(i)][0]=VirtualTree::build(vec); }for(int i=1;i<=20;i++){for(int j=1;j+(1<<i)<=m;j++){st[j][i]=merge(st[j][i-1],st[j+(1<<i-1)][i-1]);}}int q;cin>>q;int lstans=0;while(q--){int l,r,k;cin>>l>>r>>k;decode(l,r,k,lstans,op);lstans=query(l,r,k);cout<<lstans<<endl;}return 0;
}
其他的话
你可能觉得这篇文章好像就这样被我水过去了(实际上确实如此),但我是真的不想再提及这件事,因此每道题写的比较简短,我也没想过这篇文章的质量分能有多高。(而且,水的题不也很水吗?难的题不也很难吗?)
还有就是上面我的所有承诺必定会实现,请各位读者帮忙监督我(不然我怕我会忘)。