【NOI2019十二省联合省选】部分题简要题解

时间:2021-08-01 05:11:50

Day 1 T1 异或粽子

题意:给出一个长为n的序列,选择K个不完全重合的区间使得每个区间的异或值的总和最大。

题解:先做一个前缀异或和,对于每一个右端点我们记录三元组(l,r,x)表示在左端点在\([l,r]\)内,最大异或值为x,塞进堆里。每次取出堆顶,并将该三元组对应的区间分裂成两个,重新扔回堆里。计算区间最大异或值利用可持久化字典树。

时间复杂度\(O(n\log n+K\log Maxvalue)\)

#include <bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fod(i,a,b) for(int i=a;i>=b;--i)
#define N 500005
#define M 17000005
#define L 33
#define LL long long
using namespace std;
LL a[N],cf[L],ans,d[N];
int t[M][2],rt[N],mw,mx[M];
int n,cnt,n1; void ins(int k,int x,int w)
{
fod(i,mw,0)
{
int p=((a[w]&cf[i])>0);
t[k][p^1]=t[x][p^1];
mx[k]=w;
k=t[k][p]=++n1,x=t[x][p];
}
mx[k]=w;
}
void read(LL &x)
{
x=0;char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9') x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
}
struct node
{
int x,l,r,w;
LL v;
friend bool operator <(node x,node y)
{
return x.v<y.v;
}
};
priority_queue<node> h; int get(int l,int r,LL v)
{
int k=rt[r];
fod(i,mw,0)
{
int p=(v&cf[i])>0;
k=(t[k][1-p]&&mx[t[k][1-p]]>=l)?t[k][1-p]:t[k][p];
}
return mx[k];
}
int main()
{
freopen("xor.in","r",stdin);
freopen("xor.out","w",stdout);
cin>>n>>cnt;
cf[0]=1;
fo(i,1,32) cf[i]=cf[i-1]*(LL)2;
rt[0]=++n1;
mw=0;
fo(i,1,n)
{
read(a[i]);
a[i]^=a[i-1];
fod(j,31,0) if(a[i]&cf[j]) {mw=max(mw,j);break;}
} ins(1,0,0);
fo(i,1,n)
{
rt[i]=++n1;
ins(rt[i],rt[i-1],i); int w=get(0,i-1,a[i]);
h.push((node){i,0,i-1,w,a[i]^a[w]});
} fo(i,1,cnt)
{
node p=h.top();h.pop();
ans+=p.v;
int x=p.x;
if(p.l<p.w)
{
int w=get(p.l,p.w-1,a[x]);
h.push((node){x,p.l,p.w-1,w,a[x]^a[w]});
}
if(p.r>p.w)
{
int w=get(p.w+1,p.r,a[x]);
h.push((node){x,p.w+1,p.r,w,a[x]^a[w]});
}
}
printf("%lld\n",ans);
}

Day 1 T2 字符串问题

题意:给出一个字符串S,将S中的一些子串定为A串,一些定为B串,给出了m组某个A串支配某个B串的关系,要求选出一个最长的字符串T,T为A串拼接而成,且任意相邻的两个A串(ai,ai+1)中,存在一个B串,它是ai+1的前缀,且被ai支配。求最长的长度,需要判是否无限长。

题解:将倒着做一遍,将后缀自动机的fail树弄出来(后缀树),然后我们将包含A,B串的节点设为关键点,将A,B串在这个节点上对应的长度分裂成一个新节点,同一个节点分裂出来的点从长度小到大连边,显然这个新构出来的树点数还是是\(O(n)\)的,将支配关系也看做边连起来,最后新建一个节点,每个A串向这个点连边表示结束。跑拓扑序DP即可,如果出环就是无限长。

分裂节点、找节点的时候利用map和倍增来做,总的时间复杂度\(O(n\log n)\)

#include <bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fod(i,a,b) for(int i=a;i>=b;--i)
#define N 900005
#define M 2000005
#define LL long long
using namespace std;
int t[N][26],n1,mx[N],ft[N][20],n,m,ma,mb,fs[N],nt[M],dt[M],pr[M],m1,mq,pt[2][N],len[N],d[N],rd[N],n2;
LL f[N],ans;
char st[N];
struct node
{
int x,y,p,w;
friend bool operator <(node x,node y)
{
return (x.x<y.x)||(x.x==y.x&&x.y<y.y);
}
}ask[N];
map<int,int> h[N];
typedef map<int,int>::iterator IT;
void link(int x,int y,int z)
{
nt[++m1]=fs[x];
dt[fs[x]=m1]=y;
rd[y]++;
pr[m1]=z;
}
void make()
{
int ls=1;
n1=1;
fo(j,0,25) t[1][j]=0;
fod(i,n,1)
{
int c=st[i]-'a',p=ls;
mx[ls=++n1]=n-i+1;
fo(j,0,25) t[n1][j]=0;
while(p&&!t[p][c]) t[p][c]=ls,p=ft[p][0];
if(p)
{
int q=t[p][c];
if(mx[q]==mx[p]+1) ft[ls][0]=q;
else
{
ft[++n1][0]=ft[q][0];
mx[n1]=mx[p]+1;
ft[q][0]=ft[ls][0]=n1;
fo(j,0,25) t[n1][j]=t[q][j];
while(p&&t[p][c]==q) t[p][c]=n1,p=ft[p][0];
}
}
else ft[ls][0]=1;
}
}
void jump(int &x,int e)
{
for(int j=19;mx[ft[x][0]]>=e;)
{
while(j&&mx[ft[x][j]]<e) j--;
x=ft[x][j];
}
}
void read(int &x)
{
x=0;char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9') x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
}
void bfs()
{
d[0]=0;
fo(i,1,n2) if(!rd[i]) d[++d[0]]=i;
int l=0;
while(l<d[0])
{
int k=d[++l];
ans=max(ans,f[k]);
for(int i=fs[k];i;i=nt[i])
{
int p=dt[i];
f[p]=max(f[p],f[k]+pr[i]);
rd[p]--;
if(rd[p]==0) d[++d[0]]=p;
}
}
}
int main()
{
freopen("string.in","r",stdin);
freopen("string.out","w",stdout);
int t1;
cin>>t1;
while(t1--)
{
fo(i,1,n1)
{
mx[i]=0,h[i].clear();
ft[i][0]=0;
}
fo(i,1,n2) fs[i]=rd[i]=f[i]=0;
m1=0;
n1=n2=0;
mq=0;
scanf("\n%s",st+1);
n=strlen(st+1);
make(); fo(i,1,n1) h[i][mx[i]]=i;
read(ma);
fo(i,1,ma)
{
int l,r;
read(l),read(r);
ask[++mq]=(node){l,r,0,i};
len[i]=r-l+1;
}
read(mb);
fo(i,1,mb)
{
int l,r;
read(l),read(r);
ask[++mq]=(node){l,r,1,i};
} sort(ask+1,ask+mq+1);
fo(j,1,19)
fo(i,1,n1) ft[i][j]=ft[ft[i][j-1]][j-1];
int k=1,j=n,w=1;
n2=n1;
ask[mq+1].x=n+1;
fod(i,mq,1)
{
if(i==mq||ask[i].x!=ask[i+1].x)
{
fod(p,ask[i+1].x-1,ask[i].x) k=t[k][st[p]-'a'];
w=k;
}
jump(w,ask[i].y-ask[i].x+1);
if(!h[w][ask[i].y-ask[i].x+1]) h[w][ask[i].y-ask[i].x+1]=++n2;
pt[ask[i].p][ask[i].w]=h[w][ask[i].y-ask[i].x+1];
} fo(i,1,n1)
{
IT it=h[i].begin();
int ls=ft[i][0];
for(;it!=h[i].end();it++)
if(ls) link(ls,(*it).second,0),ls=(*it).second;
}
read(m);
fo(i,1,m)
{
int x,y;
read(x),read(y);
link(pt[0][x],pt[1][y],len[x]);
}
n2++;
ans=0;
fo(i,1,ma) link(pt[0][i],n2,len[i]); bfs();
if(d[0]!=n2) printf("-1\n");
else printf("%lld\n",ans);
} }

Day 2 T1 皮配

题意:有n所学校,c座城市,每个学校都属于某一个城市。现在有4位导师,分成两大阵营(1,2),(3,4)和两大派系(1,3),(2,4)。每个阵营和每个派系都有一个人数上限,每个学校有一个选手人数。现在问有多少种分配方案,满足同一个城市的学校选择同一个阵营,同一个学校的所有选手选择同一个导师。此外,有k所学校的选手一定不会选择某一位导师。

题解:若k=0,我们容易发现阵营限制和派系限制是独立的,由于一个城市必须选择相同阵营,且对于学校来说所属城市选哪个阵营没有关系。我们只需要将城市对于阵营人数做背包,学校对于派系做背包,最后答案乘在一起即可。

若k!=0,我们将有限制的k个学校取出,剩余没有限制的学校和城市跟之前一样DP。剩下k所学校暴力做同时两维限制的背包即可,最后再将三个东西乘在一起。

直接做容易卡常数,我们发现每所学校的人数不超过10,那么DP时和的上界对总人数取个min,单组数据复杂度就优化到是\(O((n+c)M+kM*10k)\)

相当丑...

#include <bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fod(i,a,b) for(int i=a;i>=b;--i)
#define N 2505
#define LL long long
#define mo 998244353
#define L 12
using namespace std; LL f[N][N],g[N][N],cf[L+1],l2[L+1],h[2][N][N][2];
int a[N],c[N],s[N],t,n,m,lim[4],mx,bp[N],fr[N],n1,m1,d[N],d2[N],bq[2][N][N][2];
bool bz[N]; bool cmp(int x,int y)
{
return (bp[x]<bp[y]);
}
bool cmp2(int x,int y)
{
return (bz[x]<bz[y]);
}
void inc(LL &x,LL v)
{
x=(x+v)%mo;
}
bool cmp3(int x,int y)
{
return fr[x]<fr[y];
}
LL gf(int l,int r)
{
if(r>lim[2]) r=lim[2];
if(r<0) return 0;
LL s=f[n1][r];
if(l>0) s=(s-f[n1][l-1]+mo)%mo;
return s;
}
LL gg(int l,int r)
{
if(r>lim[0]) r=lim[0];
if(r<0||l>r) return 0;
LL s=g[m1][r];
if(l>0) s=(s-g[m1][l-1]+mo)%mo;
return s;
}
int main()
{
freopen("mentor.in","r",stdin);
freopen("mentor.out","w",stdout);
cin>>t;
cf[0]=1;
fo(i,1,L) l2[cf[i]=cf[i-1]<<1]=i;
while(t--)
{
mx=0;
memset(bz,0,sizeof(bz));
memset(s,0,sizeof(s));
memset(f,0,sizeof(f));
memset(h,0,sizeof(h));
memset(g,0,sizeof(g));
memset(bq,0,sizeof(bq));
memset(bp,255,sizeof(bp));
scanf("%d%d",&n,&m);
int sum=0;
fo(j,0,3) scanf("%d",&lim[j]),mx=max(mx,lim[j]); fo(i,1,n)
{
int x,y;
scanf("%d%d",&x,&y);
fr[i]=x;
sum+=y;
s[x]+=y,c[i]=y;
d[i]=i;
}
int lc;
scanf("%d",&lc);
fo(i,1,lc)
{
int x,y;
scanf("%d%d",&x,&y);
bp[x]=y;
bz[fr[x]]=1;
}
sort(d+1,d+n+1,cmp);
n1=0;
while(n1<n&&bp[d[n1+1]]<0) n1++;
f[0][0]=1;
fo(i,1,n1)
{
fo(j,0,lim[2])
{
f[i][j]=f[i-1][j];
if(j>=c[d[i]]) inc(f[i][j],f[i-1][j-c[d[i]]]);
}
} fo(i,1,m) d2[i]=i;
sort(d2+1,d2+m+1,cmp2);
m1=0;
while(m1<m&&!bz[d2[m1+1]]) m1++;
g[0][0]=1;
fo(i,1,m1)
{
fo(j,0,lim[0])
{
g[i][j]=g[i-1][j];
if(s[d2[i]]>0&&j>=s[d2[i]]) inc(g[i][j],g[i-1][j-s[d2[i]]]);
}
} h[0][0][0][0]=1,bq[0][0][0][0]=n1;
sort(d+n1+1,d+n+1,cmp3);
int vr=0;
fo(i,n1+1,n)
{
int i1=(i-n1)&1;
if(i==n1+1||fr[d[i]]!=fr[d[i-1]]) vr+=s[fr[d[i]]];
fo(p,0,1)
{
int r1=min(lim[0],vr);
fo(j,0,r1)
{
int uj=(p==0&&(i==n1+1||fr[d[i]]!=fr[d[i-1]]))?j-s[fr[d[i]]]:j;
if(uj>=0)
{
int r2=min(10*(i-n1),lim[2]);
fo(k,0,r2)
{
h[i1][j][k][p]=0;
bq[i1][j][k][p]=i;
if(!(i==n1+1||fr[d[i]]!=fr[d[i-1]]))
{
if(bp[d[i]]!=p*2&&k>=c[d[i]])
{
if(bq[1^i1][uj][k-c[d[i]]][p]==i-1) inc(h[i1][j][k][p],h[1^i1][uj][k-c[d[i]]][p]);
}
if(bp[d[i]]!=p*2+1)
{
if(bq[1^i1][uj][k][p]==i-1) inc(h[i1][j][k][p],h[1^i1][uj][k][p]);
}
}
else
{
if(bp[d[i]]!=p*2&&k>=c[d[i]])
{
if(bq[1^i1][uj][k-c[d[i]]][0]==i-1) inc(h[i1][j][k][p],h[1^i1][uj][k-c[d[i]]][0]);
if(bq[1^i1][uj][k-c[d[i]]][1]==i-1) inc(h[i1][j][k][p],h[1^i1][uj][k-c[d[i]]][1]);
}
if(bp[d[i]]!=p*2+1)
{
if(bq[1^i1][uj][k][0]==i-1) inc(h[i1][j][k][p],h[1^i1][uj][k][0]);
if(bq[1^i1][uj][k][1]==i-1) inc(h[i1][j][k][p],h[1^i1][uj][k][1]);
}
}
}
}
}
}
} fo(i,1,lim[2]) inc(f[n1][i],f[n1][i-1]);
fo(i,1,lim[0]) inc(g[m1][i],g[m1][i-1]);
int i1=(n-n1)&1;
LL ans=0;
fo(j,0,lim[0]) fo(k,0,lim[2])
{
if(bq[i1][j][k][0]==n) inc(ans,h[i1][j][k][0]*gg(sum-j-lim[1],lim[0]-j)%mo*gf(sum-k-lim[3],lim[2]-k)%mo);
if(bq[i1][j][k][1]==n) inc(ans,h[i1][j][k][1]*gg(sum-j-lim[1],lim[0]-j)%mo*gf(sum-k-lim[3],lim[2]-k)%mo);
}
printf("%lld\n",ans);
}
}

Day 2 T2 春节十二响

题意:给出一棵有根树,每个节点有点权,要将所有节点放入若干个集合中,每个集合的代价是集合中点权最大值。要求树上祖先和后代不能放入同一个集合。求最小总代价。

题解:先考虑一条链的情况(根不一定为链端),那一定是长一点的链上每个节点给一个集合,短的链的点塞进这些集合,根节点自成一个集合。深入思考可以发现一定是大的和大的点放在一起,小的和小的放在一起更优。同时这启发我们集合个数与深度有关。

扩展到树上,我们采用长链剖分,对于每条长链维护一个堆记录集合的权值,合并子树的时候就暴力取出长链的前若干大合并,再暴力塞回去。

由长链剖分的时间复杂度分析,这样做的时间复杂度是\(O(n\log n)\)的。

#include <bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fod(i,a,b) for(int i=a;i>=b;--i)
#define N 200005
#define LL long long
using namespace std;
int fs[N],nt[2*N],dt[2*N],ft[N],n,m,pr[N],dep[N],son[N],m1;
LL ans;
priority_queue<int> h[N];
int rt[N],n1,u1[N]; void link(int x,int y)
{
nt[++m1]=fs[x];
dt[fs[x]=m1]=y;
}
void make(int k,int fa)
{
for(int i=fs[k];i;i=nt[i])
{
int p=dt[i];
if(p!=fa) make(p,k),son[k]=(dep[p]>dep[son[k]])?p:son[k];
}
dep[k]=dep[son[k]]+1;
}
void dfs(int k,int fa)
{
if(son[k]) dfs(son[k],k),rt[k]=rt[son[k]];
for(int i=fs[k];i;i=nt[i])
{
int p=dt[i];
if(p!=fa&&p!=son[k]) dfs(p,k);
} for(int i=fs[k];i;i=nt[i])
{
int p=dt[i];
if(p!=fa&&p!=son[k])
{
u1[0]=0;
while(!h[rt[p]].empty())
{
int x=h[rt[k]].top(),y=h[rt[p]].top();
h[rt[k]].pop(),h[rt[p]].pop();
u1[++u1[0]]=max(x,y);
}
fo(j,1,u1[0]) h[rt[k]].push(u1[j]);
}
} if(!rt[k]) rt[k]=++n1;
h[rt[k]].push(pr[k]);
}
int main()
{
freopen("spring.in","r",stdin);
freopen("spring.out","w",stdout);
cin>>n;
fo(i,1,n) scanf("%d",&pr[i]);
fo(i,2,n) scanf("%d",&ft[i]),link(ft[i],i);
make(1,0);
dfs(1,0);
ans=0;
while(!h[rt[1]].empty())
{
ans+=h[rt[1]].top();
h[rt[1]].pop();
}
printf("%lld\n",ans);
}

【NOI2019十二省联合省选】部分题简要题解的更多相关文章

  1. 苹果新的编程语言 Swift 语言进阶(十二)--选项链

    选项链是使用选项来查询和调用其属性.方法或下标的一个过程,假设选项包括一个值,则属性.方法.下标的查询和调用成功,否则,调用返回nil. 选项链能用在不论什么类型的选项来检查对其一个属性.方法.下标的 ...

  2. NOI2019十二省联考旅游记

    真的是去旅游的啊,毕竟菜是原罪嘛 Day 0 去指定地点试机,果然,键盘还是一如既往的不好用,我也不知道为什么. 晚上,教练请吃自助餐,幸福的像个胖子 Day 1 早上坐车过去,在车上看了看原来写过的 ...

  3. 十二省NOI&OpenCurlyDoubleQuote;省选”联考模测(第二场)A抽卡大赛

    /* dp维护整体的概率, 每次相当于回退一格然后重新dp一格 */ #include<cstdio> #include<algorithm> #include<iost ...

  4. 洛谷P5283 &amp&semi; LOJ3048:&lbrack;十二省联考2019&rsqb;异或粽子——题解

    https://www.luogu.org/problemnew/show/P5283 https://loj.ac/problem/3048 小粽是一个喜欢吃粽子的好孩子.今天她在家里自己做起了粽子 ...

  5. 洛谷&lbrack;LnOI2019&rsqb;长脖子鹿省选模拟赛 简要题解

    传送门 听说比赛的时候T4T4T4标程锅了??? WTF换我时间我要写T3啊 于是在T4T4T4调半天无果的情况下260pts260pts260pts收场真的是tcltcltcl. T1 快速多项式变 ...

  6. Python开发【第二十二篇】:Web框架之Django【进阶】

    Python开发[第二十二篇]:Web框架之Django[进阶]   猛击这里:http://www.cnblogs.com/wupeiqi/articles/5246483.html 博客园 首页 ...

  7. CQOI2019&lpar;十二省联考&rpar;游记

    CQOI2019(十二省联考)游记 Day -? 自从联赛爆炸,\(THUWC\)爆炸,\(WC\)爆炸(就没有不爆炸的)之后我已经无所畏惧... 听说是考\(4.5 h\)吗? Day -1 \(Z ...

  8. 十二省联考 - JLOI2019 游记

    十二省联考 - JLOI 2019 游记 想了想,还是起一个副标题吧 一场失败的胜利 Day -inf 想了想,还是从头开始说吧. 其实考完NOIP之后,大概估算一下,吉林省队的数量还算是比较乐观的, ...

  9. 【腾讯Bugly干货分享】腾讯验证码的十二年

    本文来自于腾讯bugly开发者社区,未经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/581301b146dfb1456904df8d Dev Club 是一个交流移动 ...

随机推荐

  1. 基于 jQuery 实现的精致作品集图片导航效果

    今天,我们要用 jQuery 来创建一个作品集图像的导航模板.我们的想法是,以分组的方式显示一组作品集,并通过二维的方式(水平/垂直)来浏览.任一箭头或当前图像下方的小盒子可以作为导航使用. 在线演示 ...

  2. StringBuffer与StringBuilder有什么区别

    package String比较; /* * StringBuffer与StringBuilder有什么区别 * StringBuilder是JDK5增加的一个新类,功能几乎与StringBuffer ...

  3. OpenJudge计算概论-找最大数序列

    /*===================================== 找最大数序列 总时间限制: 1000ms 内存限制: 65536kB 描述 输入n行(n 不大于 30),每行不超过10 ...

  4. Thinkphp模板怎么使用自定义函数

    内置模板引擎支持对模板变量使用函数,并支持多个函数同时使用. 注意:自定义函数要放在项目应用目录/common/common.php中. 这里是关键. 模板变量的函数调用格式:{$varname|fu ...

  5. sysobjects&period;xtype介绍

    SQL Server数据库的一切信息都保存在它的系统表格里. 在大多数情况下,对你最有用的两个列是Sysobjects.name和Sysobjects.xtype.前面一个用来列出待考察对象的名字,而 ...

  6. HDU 4604 Deque 二分最长上升子序列

    题目大意就是给一个deque 然后有n个数,依次进行操作,每种操作,你可以把这个数放在deque首部,也可以放在尾部,也可以扔掉不管,但是要保证deque中的数是非递减的.最要求deque中最长能是多 ...

  7. inno setup 多语言安装

    之前的安装程序默认语言为英文,现在我们需要将它变成中文,由于InnoSetup安装包中默认没有带中文语言文件,我们需要下载一个先: 到http://www.400gb.com/u/758954/123 ...

  8. webpack的配置及使用

    webpack 安装 命令行输入 npm install webpack 配置文件 webpack.config.js moudule.exports = { //Import 入口文件 entry: ...

  9. 《think in python》学习-10

    think in python 10 列表 和字符串相似,列表是值得序列.在列表中,它可以是任何类型,列表中的值成为元素,有时也称为列表项 s = [10,20,30,40] print s #列表也 ...

  10. centos7 安装jdk 1&period;8

    1.下载jdk1.8  for linux的安装包 jdk-8u11-linux-x64.tar.gz,下载地址:http://download.oracle.com/otn-pub/java/jdk ...