10.5 考试 (感觉比较难)
T1
正解是 建26颗线段树,但是他们有人被卡常了...
还是分块大法好,跑的最快
直接记录下每一个块 26个字母出现次数,再打上升序还是降序的标记
毕竟考试调了两个小时呢23333
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <cmath>
#include <algorithm>
#define dd double
#define mem(a,b) memset(a,b,sizeof(a))
#define ll long long
using namespace std;
inline char readchar()
{
char q=getchar();
while(q<'a'||q>'z')q=getchar();
return q;
}
inline int read()
{
char q=getchar();int ans=0;
while(q<'0'||q>'9')q=getchar();
while(q>='0'&&q<='9'){ans=ans*10+q-'0';q=getchar();}
return ans;
}
const int N=100006;
int n,m,fen;
int s[N];
int dui[N],L[1006],R[1006],len[1006];
int num[1006][36],flag[1006];
int t[36];
inline void pushdown(int x)
{
if(flag[x]==-1)
return ;
int now;
if(flag[x]==1)
{
now=L[x];
for(int i=0;i<26;++i)
while(num[x][i])
s[now++]=i,--num[x][i];
}
else
{
now=R[x];
for(int i=0;i<26;++i)
while(num[x][i])
s[now--]=i,--num[x][i];
}
flag[x]=-1;
}
inline void pushup(int x)
{
mem(num[x],0);
for(int i=L[x];i<=R[x];++i)
++num[x][s[i]];
}
inline void get_sum(int l,int r)
{
int q1=min(r,R[dui[l]]);
pushdown(dui[l]);
for(int i=l;i<=q1;++i)
++t[s[i]];
if(dui[l]!=dui[r])
{
pushdown(dui[r]);
for(int i=L[dui[r]];i<=r;++i)
++t[s[i]];
}
for(int i=dui[l]+1;i<dui[r];++i)
for(int j=0;j<26;++j)
t[j]+=num[i][j];
}
inline void changesh(int l,int r)
{
get_sum(l,r);
int q1=min(r,R[dui[l]]),now1,now2,temp;
now1=0;
now2=l;
while(now2<=q1)
{
while(!t[now1])
++now1;
s[now2]=now1;
++now2;
--t[now1];
}
pushup(dui[l]);
for(int i=dui[l]+1;i<dui[r];++i)
{
flag[i]=1;
mem(num[i],0);
now2=0;
while(now2<len[i])
{
while(!t[now1])
++now1;
temp=min(len[i]-now2,t[now1]);
num[i][now1]+=temp;
now2+=temp;
t[now1]-=temp;
}
}
if(dui[l]!=dui[r])
{
now2=L[dui[r]];
while(now2<=r)
{
while(!t[now1])
++now1;
s[now2]=now1;
++now2;
--t[now1];
}
pushup(dui[r]);
}
}
inline void changexi(int l,int r)
{
get_sum(l,r);
int q1=max(l,L[dui[r]]),now1,now2,temp;
now1=0;
now2=r;
while(now2>=q1)
{
while(!t[now1])
++now1;
s[now2]=now1;
--now2;
--t[now1];
}
pushup(dui[r]);
for(int i=dui[r]-1;i>dui[l];--i)
{
flag[i]=0;
for(int j=0;j<26;++j)num[i][j]=0;
now2=0;
while(now2<len[i])
{
while(!t[now1])
++now1;
temp=(len[i]-now2<t[now1]?len[i]-now2:t[now1]);
num[i][now1]+=temp;
now2+=temp;
t[now1]-=temp;
}
}
if(dui[l]!=dui[r])
{
now2=R[dui[l]];
while(now2>=l)
{
while(!t[now1])
++now1;
s[now2]=now1;
--now2;
--t[now1];
}
pushup(dui[l]);
}
}
inline void out11()
{
for(int i=1;i<=dui[n];++i)
pushdown(i);
for(int i=1;i<=n;++i)
printf("%c",s[i]+'a');
}
int main(){
//freopen("T1.in","r",stdin);
//freopen("T1.out","w",stdout);
mem(flag,-1);
n=read();m=read();
//fen=(int)ceil(sqrt((dd)n*13.5+0.5));
fen=(int)ceil(sqrt(n+0.5));
for(int i=1;i<=n;++i)
{
s[i]=readchar()-'a';
dui[i]=(i-1)/fen+1;
++num[dui[i]][s[i]];
}
for(int i=1;i<=dui[n];++i)
{
L[i]=(i-1)*fen+1;
R[i]=min(n,i*fen);
len[i]=R[i]-L[i]+1;
}
int op,l0,r0,now;
for(int i=1;i<=m;++i)
{
l0=read();r0=read();op=read();
if(l0>r0)
swap(l0,r0);
if(op==1)
changesh(l0,r0);
else
changexi(l0,r0);
}
out11();
}]
T2
考试执着的认为是 容斥,也想过dp,但是觉得就是...(容斥个??)
f[i][j] 前i列中选了j列右区间的方案数
考虑 第i列选右区间还是不选 再考虑左区间选的方案数,用排列计算
#include <cstdio>
#include <cstring>
#include <iostream>
#include <cstdlib>
#include <algorithm>
#define ll long long
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
const int N=3006;
const int mod=998244353;
ll jie[N],jieni[N],ni[N];
void chu()
{
ni[1]=1;
for(int i=2;i<N;++i)
ni[i]=(ll)(mod-mod/i)*ni[mod%i]%mod;
jie[0]=jieni[0]=1;
for(int i=1;i<N;++i)
{
jie[i]=jie[i-1]*i%mod;
jieni[i]=jieni[i-1]*ni[i]%mod;
}
}
inline ll A(int n,int m)
{
if(n<m)
return 0;
return jie[n]*jieni[n-m]%mod;
}
int n,m;
int l[N],r[N];
ll f[N][N];
int jil[N],prel[N],jir[N],prer[N];
void dp()
{
f[0][0]=1;
for(int i=1;i<=m;++i)
for(int j=0;j<=n;++j)
{
f[i][j]=(f[i][j]+f[i-1][j])%mod;
if(j>0&&prer[i]-(j-1)>=0)
//if(j>0)
{
//printf("i=%d j=%d\n",i,j);
f[i][j]=(f[i][j]+f[i-1][j-1]*(prer[i]-(j-1))%mod )%mod;
}
if(jil[i])
{
if(j+prel[i]>i)
f[i][j]=0;
else
f[i][j]=f[i][j]*A(i-j-prel[i-1],jil[i])%mod;
}
}
}
int main(){
//freopen("in.in","r",stdin);
chu();
scanf("%d%d",&n,&m);
//printf("n=%d\n",n);
for(int i=1;i<=n;++i)
{
scanf("%d%d",&l[i],&r[i]);
++jil[l[i]];
++jir[r[i]];
}
for(int j=1;j<=m;++j)
{
prel[j]=prel[j-1]+jil[j];
prer[j]=prer[j-1]+jir[j];
}
dp();
/*printf("\n");
for(int i=1;i<=m;++i)
{
for(int j=1;j<=n;++j)
printf("%lld ",f[i][j]);
printf("\n");
}*/
cout<<f[m][n];
}
T3
对手的变换其实是把 x循环左移一位...
而x异或一部分数后再左移,相当于x先左移,前i个数异或和再左移
之后就是:
给m+1个数,选一个x,使得x与m+1个数异或和最小值最大
这个可以把m+1个数建一颗0/1trie
然后在树上走
如果这个节点0/1儿子都有,你肯定得选1/0是最后这一位异或是0 (你要保证最小值)
如果只有一个儿子,这一位你要选一个数 使其最后异或是1 (最大值)
之后在得到的m+1个数里 找到ans
#include <cstdio>
#include <cstring>
#include <iostream>
#include <cstdlib>
#include <algorithm>
#define ll long long
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
const int M=100006;
struct son
{
son* ch[2];
son()
{
ch[0]=ch[1]=NULL;
}
}*root;
int n,m,maxp;
int a[M],pre[M];
int con;
int b[M];
void add(int x)
{
int temp;
son *now=root;
for(int i=n-1;i>=0;--i)
{
temp=0;
if( (1<<i)&x )
temp=1;
if(now->ch[temp]==NULL)
now->ch[temp]=new son();
now=now->ch[temp];
}
}
inline int move(int x)
{
return ((1<<(n-1))&x)?(((x<<1)&maxp)|1):((x<<1)&maxp);
}
inline int move2(int x)
{
return (1&x)?((x>>1)|(1<<(n-1))):(x>>1);
}
void dfs(son *x,int h,int now)
{
//printf("h=%d now=%d\n",h,now);
if(h==-1)
{
b[++con]=now;
return ;
}
if(x->ch[0]!=NULL&&x->ch[1]!=NULL)
{
dfs(x->ch[0],h-1,now);
dfs(x->ch[1],h-1,now);
}
else
if(x->ch[0]!=NULL)
dfs(x->ch[0],h-1,now|(1<<h));
else
if(x->ch[1]!=NULL)
dfs(x->ch[1],h-1,now|(1<<h));
return ;
}
void out11()
{
printf("%d %d %d\n",move(1),move(2),move(3));
printf("\n");
for(int i=1;i<=m;++i)
printf("%d ",pre[i]);
printf("\n");
for(int i=1;i<=con;++i)
printf("%d ",b[i]);
printf("\n");
}
int main(){
//freopen("in.in","r",stdin);
//freopen("big2.in","r",stdin);
scanf("%d%d",&n,&m);
maxp=(1<<n)-1;
for(int i=1;i<=m;++i)
{
scanf("%d",&a[i]);
pre[i]=pre[i-1]^a[i];
}
b[++con]=pre[m];
for(int i=1;i<=m;++i)
b[++con]=move(pre[i])^(pre[m]^pre[i]);
root=new son();
for(int i=1;i<=con;++i)
add(b[i]);
con=0;
dfs(root,n-1,0);
int mx=-1,num=0;
for(int i=1;i<=con;++i)
{
if(mx<b[i])
{
mx=b[i];
num=1;
}
else
if(mx==b[i])
++num;
}
//out11();
printf("%d\n%d",mx,num);
}
这次考试个人觉得比较难
第一题调的时间太长,代码能力有待提升....