Comet OJ - Contest #15 题解
\(A\)
咕咕
const int N=1005;
int a[N],n,T;
int main(){
for(scanf("%d",&T);T;--T){
scanf("%d",&n);
fp(i,1,10)a[i]=n%10,n/=10;
R int fl=1;
fp(i,1,9)if(a[i]<a[i+1]){fl=0;break;}
if(!fl)puts("Impossible");
else printf("%d\n",a[1]);
}
return 0;
}
\(B\)
枚举内心,那么三个点合法当且仅当到这个内心距离相同,组合数算一下就行了
typedef long long ll;
const int N=2005;
int x[N],y[N],n;ll res,dis[N];
inline ll d(R int i,R int j){return 1ll*(x[i]-x[j])*(x[i]-x[j])+1ll*(y[i]-y[j])*(y[i]-y[j]);}
inline ll calc(R int x){return 1ll*x*(x-1)*(x-2)/6;}
int main(){
scanf("%d",&n);
fp(i,1,n)scanf("%d%d",&x[i],&y[i]);
fp(i,1,n){
R int tot=0;
fp(j,1,n)if(i!=j)dis[++tot]=d(i,j);
sort(dis+1,dis+1+tot);
for(R int l=1,r=1;l<=tot;l=r){
while(r<=tot&&dis[r]==dis[l])++r;
res+=calc(r-l);
}
}
printf("%lld\n",res);
return 0;
}
\(C\)
首先肯定存在一个分界点,满足前面是\(A\)掉的,后面是没交过或者fst的,记最后一个\(A\)掉的人为\(i\),那么\(i\)以及之前要满足\(a_i\)递增,\(i\)之后的要能分成两个集合,一个是fst的,一个是没交的,两个都要满足\(a_i\)递增,且fst的所有人的编号都小于没交的人的编号,同时任意一个\(a_j<a_i\)的\(j\)都必须是fst的,直接暴力\(check\)就行了
//quming
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
const int N=505;
int a[N],stx[N],sty[N],id[N],fl[N],fr[N],tx,ty,n,T,l,r;
bool ck(int l,int r,int tt){
if(l>=r)return 1;
int top=0;
fp(i,l,r)id[++top]=i;
sort(id+1,id+1+top,[](const int &x,const int &y){return a[x]<a[y];});
fl[0]=fr[top+1]=1,id[0]=l-1,id[top+1]=r+1;
fp(i,1,top)fl[i]=fl[i-1]&(id[i]>id[i-1]);
fd(i,top,1)fr[i]=fr[i+1]&(id[i]<id[i+1]);
fp(i,tt,top)if(fl[i]&&fr[i+1])return true;
return false;
}
int main(){
// freopen("testdata.in","r",stdin);
for(scanf("%d",&T);T;--T){
scanf("%d",&n),l=n+1,r=0;
fp(i,1,n)scanf("%d",&a[i]);
a[n+1]=n+1,a[0]=0;
fp(i,0,n){
if(i&&a[i]<a[i-1])break;
if(ck(i+1,n,a[i]-i))cmin(l,i),cmax(r,i);
}
if(l>r)l=r=-1;
printf("%d %d\n",l,r);
}
return 0;
}
\(D\)
这个数字\(s\)要能被表示成若干个\({10^k-1\over 9}\)之和,那么就是\(9s\)要能表示成若干个\(10^k-1\)之和,我们枚举数字个数\(p\),那么就是\(9s+p\)能被表示成\(10^k\)之和,条件就是\(p\)大于等于所有数位之和,且由于一次进位是让某一位-10,另一位+1,所以还要满足\(p\)和所有数位之和在模\(9\)意义下相等。显然\(p\)是\(O(|s|)\)级别的,根据\(01\)计算器的原理,复杂度\(O(|s|)\)
//quming
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
const int N=2e6+5;
char s[N];int a[N],q[N],h,t,n,res,T,sum;
inline int min(R int x,R int y){return x<y?x:y;}
int calc(){
fp(k,1,23333333){
R int i=1;
while(a[i]==9)a[i++]=0,sum-=9;
++a[i],++sum;
if(k>=sum&&sum%9==k%9)return k;
}
}
int main(){
// freopen("testdata.in","r",stdin);
for(scanf("%d",&T);T;--T){
scanf("%s",s+1),n=strlen(s+1);
fp(i,1,n)a[i]=s[n-i+1]-'0',a[i]=a[i]*9;
fp(i,1,n-1)a[i+1]+=a[i]/10,a[i]%=10;
for(;a[n]>9;++n)a[n+1]=a[n]/10,a[n]%=10;
fp(i,n+1,n+233333)a[i]=0;
sum=0;
fp(i,1,n)sum+=a[i];
printf("%d\n",calc());
}
return 0;
}
\(E\)
对于某一个元素,我们把对他进行的所有操作放在时间轴上,一个push记为+1,一个pop记为-1,那么一次时间t时的query,就是找到一个最大的i,满足[i,t]之和大于等于pos,那么这个i处肯定是一个push,且这个push的元素即为答案了
那么对于区间push和区间pop直接离线,然后就能维护每个元素的所有操作的时间轴了
代码里实现的时候略微有点不同,因为查询[i,t]太难写了,所以我代码里是把时间轴反过来,然后查询一个最小的满足条件的前缀和的位置的,且push记为-1,pop记为+1,所以看代码的时候注意一下
//quming
#include<bits/stdc++.h>
#define R register
#define pb push_back
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
const int N=2e5+5;
inline int min(R int x,R int y){return x<y?x:y;}
struct node;typedef node* ptr;
struct node{
ptr lc,rc;int t,mn;
inline void ppd(R int x){t+=x,mn+=x;}
inline void pd(){if(t)lc->ppd(t),rc->ppd(t),t=0;}
inline void upd(){mn=min(lc->mn,rc->mn);}
}e[N<<2],*rt,*pp=e;
int a[N],n,q;
char s[15];vector<int>ad[N],ed[N],qr[N];
void build(ptr &p,int l,int r){
p=++pp;if(l==r)return;
int mid=(l+r)>>1;
build(p->lc,l,mid),build(p->rc,mid+1,r);
p->upd();
}
int K,ans,ANS[N];
void query(ptr p,int l,int r,int x){
if(l==r)return ans=r,void();
int mid=(l+r)>>1;p->pd();
if(!ans&&x<=mid&&p->lc->mn<=K)query(p->lc,l,mid,x);
if(!ans&&x<=r&&p->rc->mn<=K)query(p->rc,mid+1,r,x);
}
int get(ptr p,int l,int r,int x){
if(l==r)return p->mn;
int mid=(l+r)>>1;p->pd();
return x<=mid?get(p->lc,l,mid,x):get(p->rc,mid+1,r,x);
}
void update(ptr p,int l,int r,int ql,int qr,int v){
if(ql<=l&&qr>=r)return p->ppd(v),void();
int mid=(l+r)>>1;p->pd();
if(ql<=mid)update(p->lc,l,mid,ql,qr,v);
if(qr>mid)update(p->rc,mid+1,r,ql,qr,v);
p->upd();
}
int main(){
// freopen("testdata.in","r",stdin);
scanf("%d%d",&n,&q);
build(rt,1,q);
for(R int i=1,l,r,v,id;i<=q;++i){
scanf("%s",s+1);
switch(s[2]){
case 'u':{
scanf("%d%d%d",&l,&r,&v);
a[q-i+1]=v,ad[l].pb(q-i+1),ed[r+1].pb(q-i+1);
break;
}
case 'o':{
scanf("%d%d",&l,&r);
ed[l].pb(q-i+1),ad[r+1].pb(q-i+1);
break;
}
case 'i':{
scanf("%d%d",&id,&v);
a[q-i+1]=v,qr[id].pb(q-i+1);
break;
}
}
}
fp(i,1,n){
for(auto v:ad[i])update(rt,1,q,v,q,-1);
for(auto v:ed[i])update(rt,1,q,v,q,1);
for(auto v:qr[i]){
K=get(rt,1,q,v)-a[v],ans=0;
query(rt,1,q,v);
// printf("%d %d\n",v,ans);
ANS[v]=a[ans];
}
}
fd(i,q,1)if(ANS[i])printf("%d\n",ANS[i]);
return 0;
}
\(F\)
对于\(C\),我们从后往前来枚举\(i\)并判断\([i+1,n]\)是否可行,由于是从后往前,那么相当于每次往集合里加入一个数,我们记\(ql\)和\(qr\)表示sort之后,开头最长值域和下标都递增的长度以及结尾最长值域和下标都递增的长度,每次用新插入的这个数更新ql和qr就行了,用平衡树维护一下即可,代码写的FHQtreap
//quming
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
unsigned int aaa=19260817;
inline unsigned int rd(){aaa^=aaa>>15,aaa+=aaa<<12,aaa^=aaa>>3;return aaa;}
const int N=2e5+5;
struct node;typedef node* ptr;
struct node{
ptr lc,rc;int v,sz;unsigned int pr;
inline void init(R int val){v=val,pr=rd(),sz=1;}
inline ptr upd(){return sz=lc->sz+rc->sz+1,this;}
}e[N],*rt=e;int tot;
inline ptr newnode(R int v){return e[++tot].init(v),(e+tot)->lc=(e+tot)->rc=e,e+tot;}
void split(ptr p,int k,ptr &s,ptr &t){
if(p==e)return s=t=e,void();
if(p->v<=k)s=p,split(p->rc,k,p->rc,t);
else t=p,split(p->lc,k,s,p->lc);
p->upd();
}
ptr merge(ptr s,ptr t){
if(s==e)return t;if(t==e)return s;
if(s->pr<t->pr)return s->rc=merge(s->rc,t),s->upd();
return t->lc=merge(s,t->lc),t->upd();
}
void insert(int k){
ptr s,t;
split(rt,k,s,t);
rt=merge(merge(s,newnode(k)),t);
}
void erase(int k){
ptr s,t,p;
split(rt,k,s,t),split(s,k-1,s,p),p=merge(p->lc,p->rc);
rt=merge(merge(s,p),t);
}
int rk(int k){
ptr s,t;int now;
split(rt,k-1,s,t);now=s->sz+1;
return rt=merge(s,t),now;
}
int Kth(ptr p,int k){
if(p->lc->sz==k-1)return p->v;
if(p->lc->sz>=k)return Kth(p->lc,k);
return Kth(p->rc,k-p->lc->sz-1);
}
int Pre(int k){
ptr s,t;int now;
split(rt,k-1,s,t),now=Kth(s,s->sz);
return rt=merge(s,t),now;
}
int nxt(int k){
ptr s,t;int now;
split(rt,k,s,t),now=Kth(t,1);
return rt=merge(s,t),now;
}
int ok[N],a[N],kr[N],n,ql,qr,T,l,r;
int main(){
// freopen("testdata.in","r",stdin);
for(scanf("%d",&T);T;--T){
rt=e,tot=0;
scanf("%d",&n),l=n+1,r=0;
fp(i,1,n)scanf("%d",&a[i]),kr[a[i]]=i,ok[i]=0;
a[0]=0,a[n+1]=n+1,kr[0]=0,kr[n+1]=n+1,ok[0]=ok[n+1]=0;
ok[n+1]=1;
insert(0),insert(n+1),ql=qr=2;
R int cnt=2;
fd(i,n,1){
R int pl=Pre(a[i]),pr=nxt(a[i]);
R int sz=rk(pl);
if(ql>=sz){
if(i>kr[pl]&&i<kr[pr])++ql;
else if(i<kr[pl])ql=sz;
else ql=sz+1;
}
sz=cnt-(rk(pr)-1);
if(qr>=sz){
if(i>kr[pl]&&i<kr[pr])++qr;
else if(i>kr[pr])qr=sz;
else qr=sz+1;
}
++cnt;
insert(a[i]);
ok[i]=(ql+qr>=cnt&&ql>=a[i-1]-(i-1)+1);
}
fp(i,0,n){
if(i&&a[i]<a[i-1])break;
if(ok[i+1])cmin(l,i),cmax(r,i);
}
if(l>r)l=r=-1;
printf("%d %d\n",l,r);
}
return 0;
}
\(G\)
首先类似于CF840B,如果我们已经确定了每个点的度数的奇偶,那么一定可以构造出一组合法方案
由于这个构造只需要一棵生成树就可以完成,那么我们把标号大边的尽量留在生成树上,这样标号小的边就可以全部选了
先来考虑某一棵生成树,把可以选的全部选完之后,就是树上某些点为奇某些为偶,我们可以只考虑偶数点,对于任意两个点,我们可以对他们路径上的所有边取反,从而只改变这两个点的状态而不改变路径上其它点的状态,所以可知最终的偶数点只有\(0/1\)两种情况
那么再来考虑边,我们建一个并查集重构树,保证序号小的边深度浅,那么从上到下一次考虑每条边,选了一条边之后就相当于对于它所连接的两个连通块中各改变一个点的状态,我们发现如果两个连通块都是偶数点为\(0\)的情况,这条边选了之后最终答案一定变劣,这种情况下不选,其他情况下都可以选,用树状数组维护一下即可
//quming
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
const int N=2e6+5;
struct eg{int v,nx;}e[N<<1];int head[N],tot;
inline void add(R int u,R int v){e[++tot]={v,head[u]},head[u]=tot;}
int dfn[N],d[N],low[N],c[N],fa[N],fr[N],to[N],vis[N],bl[N],ls[N],rs[N];
int n,m,nd,cnt;
inline void chg(R int x){for(;x<=n;x+=x&-x)c[x]^=1;}
inline int qwq(R int x){R int res=0;for(;x;x-=x&-x)res^=c[x];return res;}
inline int query(R int l,R int r){return qwq(r)^qwq(l-1);}
inline int find(R int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
void dfs(int u){
if(u<=n)return dfn[u]=low[u]=++cnt,void();
dfn[u]=nd+1,low[u]=0;
go(u)dfs(v),cmin(dfn[u],dfn[v]),cmax(low[u],low[v]);
}
int main(){
freopen("testdata.in","r",stdin);
scanf("%d%d",&n,&m),nd=n;
fp(i,1,m)scanf("%d%d",&fr[i],&to[i]),++fr[i],++to[i];
fp(i,1,n)fa[i]=i;
for(R int i=m,u,v;i;--i){
u=find(fr[i]),v=find(to[i]);
if(u==v)vis[i]=1,d[fr[i]]^=1,d[to[i]]^=1;
else{
++nd,fa[u]=fa[v]=fa[nd]=nd,ls[nd]=u,rs[nd]=v,bl[i]=nd;
add(nd,u),add(nd,v);
}
}
dfs(nd);
fp(i,1,n)if(!d[i])chg(dfn[i]);
fp(i,1,m)if(bl[i]){
R int l=query(dfn[ls[bl[i]]],low[ls[bl[i]]]);
R int r=query(dfn[rs[bl[i]]],low[rs[bl[i]]]);
if(l||r)vis[i]=1,chg(dfn[fr[i]]),chg(dfn[to[i]]);
}
fp(i,1,m)putchar(vis[i]+'0');
putchar('\n');
return 0;
}