先更第二场。第一场题解看哭了,不太全。
题目链接
https://ac.nowcoder.com/acm/contest/5667#question
A.All with Pairs
题意
f ( s 1 , s 2 ) f(s1,s2) f(s1,s2)定义为一个最大的数i, s 1... i = t ∣ t ∣ − i + 1... ∣ t ∣ s_{1...i}=t_{|t|-i+1...|t|} s1...i=t∣t∣−i+1...∣t∣。就是s和t的最长公共前后缀长度。给n个字符串,完了求: ∑ i = 1 n ∑ j = 1 n f ( s i , s j ) 2 ( m o d 998244353 ) \sum_{i=1}^{n} \sum_{j=1}^{n} f\left(s_{i}, s_{j}\right)^{2}(\bmod 998244353) i=1∑nj=1∑nf(si,sj)2(mod998244353)
吐槽
这道题其实并不太难。但是场上过得人不多,完了我们没看,我觉得看了是能想出来的。
题解
答题思路是可以线性的枚举每个前缀,看看有多少个后缀和这个前缀相同(记作 c n t i cnt_i cnti),这个字符串哈希就能搞定。完了答案加上这个前缀长度的平方乘上后缀个数就完了。但是这样不一定保证这些配对中这个前缀长度最大。现在想知道这个前缀能匹配,且最长的时候有几个后缀。然后发现只要一个前缀能匹配上一个后缀,那么这个前缀的next也能匹配上后缀的后缀。所以next那个地方应该减掉这个前缀的匹配数(这些匹配数在next那里多算了,因为在那里next并不是最长的前后缀)。注意一个i值在 n e x t i next_i nexti那里减一次,因为现在这个 c n t i cnt_i cnti已经包含了之后的多算的了。 n e x t i next_i nexti和kmp里的略有不同。这里表示1~i的最长公共前后缀长度。
AC代码(next板子)
#include<cstdio>
#include<cstring>
#include<map>
using namespace std;
int nx[1001000];
const int modd=998244353;
typedef unsigned long long ull;
map<ull,int> ma;
void getnx(unsigned int s1[],int n)
{
nx[1]=0;
for(int i=2,j=1;i<=n+1;)//next[i] 表示最大的x,满足s1[1 : x - 1] 是s1[1 : i - 1] 的后缀。即i失配该把i变成啥
{
nx[i]=j;
while(j&&s1[j]!=s1[i])j=nx[j];
j++,i++;
}
}
char ss[1001000];
const int NN=1e5+10;
unsigned int* s[NN];
int lenth[NN];
int cnt[1001000];
const unsigned int jinzhi=31;
int main(){
int n;scanf("%d\n",&n);
for(int i=1;i<=n;i++){
scanf("%s",ss+1);
int len=strlen(ss+1);
lenth[i]=len;
s[i]=new unsigned int[len+2];
for(int j=1;j<=len;j++){
s[i][j]=ss[j]-'a'+1;
}
ull pow=1;
ull ha=0;
for(int j=len;j>=1;j--){
ha=pow*s[i][j]+ha;
pow*=jinzhi;
ma[ha]+=1;
}
}
long long ans=0;
for(int i=1;i<=n;i++){
int len=lenth[i];
getnx(s[i],len);
for(int j=1;j<=len;j++){//这里nxi变成1到i的最长公共前后缀长度。
nx[j]=nx[j+1]-1;
}
ull ha=0;
for(int j=1;j<=len;j++){
ha=ha*jinzhi+s[i][j];
cnt[j]=ma[ha];
}
for(int j=1;j<=len;j++){
cnt[nx[j]]-=cnt[j];
}
for(int j=1;j<=len;j++){
ans+=1ll*cnt[j]*j*j%modd;
ans=ans%modd;
}
}
printf("%lld\n",ans);
return 0;
}
B.Boundary
题意
有一堆点。找到压到点最多的一定经过原点圆压住了多少点。
题解
这个思路绕个弯。直接枚举两点。反正过原点,三点确定圆。完了开个map啥的记录一下圆,往圆上加点就完了。或者统计同一个圆出现了几次。一个圆出现的次数应该是其上的点的不同点对数,即 n u m = n ( n − 1 ) / 2 num=n(n-1)/2 num=n(n−1)/2统计出num选最大值枚举n就行。然而我代码老wa,也不知道咋肥四。。。有会的大佬帮忙康康。
没AC代码
#include<cstdio>
#include<cstring>
#include<set>
#include<map>
#include<cstring>
#include<algorithm>
#include<cstdlib>
using namespace std;
const int NN=1e4+10;
struct Circle
{
double x,y;
Circle(double a=0,double b=0):x(a),y(b){}
void print(){
printf("%lf %lf\n",x,y);
}
}circ[2000010];
const double eps=0.00000001;
bool cmp_xy(Circle const &a,Circle const &b){
if(a.x<b.x)return 1;
else if(a.x==b.x&&a.y<b.y)return 1;
return 0;
}
//map<Circle,set<int>,cmp_xy> ma;
double x[NN],y[NN];
int getres(long long kk){
int i;
for(i=2;1ll*i*(i-1)<kk;i++);
return i;
}
double abss(double x){
if(x<0)return -x;
else return x;
}
int main(){
int n;scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%lf%lf",x+i,y+i);
}
int ans=0;
int cnt=0;
for(int i=1;i<=n;i++){
for(int j=i+1;j<=n;j++){
if((x[i]==0&&x[j]==0)||y[i]/x[i]==y[j]/x[j]){
continue;
}
double k1,b1,k2,b2,xx,yy;
if(y[i]==0){
k2=-x[j]/y[j];
b2=y[j]/2+x[j]*x[j]/2/y[i];
xx=x[i]/2;
yy=k2*xx+b2;
}
else if(y[j]==0){
k1=-x[i]/y[i];
b1=y[i]/2+x[i]*x[i]/2/y[i];
xx=x[j]/2;
yy=k1*xx+b1;
}
else{
k1=-x[i]/y[i];
b1=y[i]/2+x[i]*x[i]/2/y[i];
k2=-x[j]/y[j];
b2=y[j]/2+x[j]*x[j]/2/y[i];
xx=(b2-b1)/(k1-k2);
yy=k1*xx+b1;
}
circ[++cnt]=Circle(xx,yy);
// neww.print();
// for(set<int>::iterator k=ma[neww].begin();k!=ma[neww].end();++k){
// printf("%d",*k);
// }printf("\n");
}
}
sort(circ+1,circ+cnt+1,cmp_xy);
int num;
for(int i=1;i<=cnt;i++){
if(i>1&&abss(circ[i].x-circ[i-1].x)<=eps&&abss(circ[i].y-circ[i-1].y)<=eps)num++;
else num=1;
ans=max(ans,num);
}
printf("%d\n",getres(1ll*ans*2));
return 0;
}
自闭了。
CD两题很水,不想写了
E.Exclusive OR
题意
给一个n长度序列,求出选出可重复的i个数的最大异或和。
吐槽
woc这题fwt数组要开long long。我没开wa到跳楼。比如所有2e5个数都是0,那异或卷积完就爆int了。要把每次卷积完的非零项统一成1,否则long long也爆。
题解
这个题关键是对秩的理解。数异或的秩和向量组的秩挺像的。构造线性基的方法其实本质是高斯消元。
注意秩的理解。每组数的异或秩的大小是这组数中线性无关数的最大个数。但是直接找出不好找,要用一直异或的办法找到线性基。这个题不需要构造线性基。秩是19,所以最多19个数一定能选出所有值。所以FWT跑19次。还有就是i次的答案一定不会小于i-2次的答案。所以大于19的次数就是离他最近的(最大的),和它差2的整数倍次数的答案。
AC代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int N;
const int NN=2e5+10;
const int MM=(1<<19)+10;
long long a[MM],b[MM];
void FWT_or(long long *a,int opt)
{
for(int i=1;i<N;i<<=1)
for(int p=i<<1,j=0;j<N;j+=p)
for(int k=0;k<i;++k)
if(opt==1)a[i+j+k]=(a[j+k]+a[i+j+k]);
else a[i+j+k]=(a[i+j+k]-a[j+k]);
}
void FWT_and(long long *a,int opt)
{
for(int i=1;i<N;i<<=1)
for(int p=i<<1,j=0;j<N;j+=p)
for(int k=0;k<i;++k)
if(opt==1)a[j+k]=(a[j+k]+a[i+j+k]);
else a[j+k]=(a[j+k]-a[i+j+k]);
}
void FWT_xor(long long *a,int opt)
{
for(int i=1;i<N;i<<=1)
for(int p=i<<1,j=0;j<N;j+=p)
for(int k=0;k<i;++k)
{
long long X=a[j+k],Y=a[i+j+k];
a[j+k]=(X+Y);a[i+j+k]=(X-Y);
if(opt==-1)a[j+k]=1ll*a[j+k]/2,a[i+j+k]=1ll*a[i+j+k]/2;
}
}
int ans[NN];
int main() {
int n;scanf("%d",&n);
int maxn=0;
for(int i=1;i<=n;i++){
int x;scanf("%d",&x);
maxn=max(maxn,x);
b[x]++;
}
for(N=1;N<maxn;N<<=1);N<<=1;
a[0]=1;
FWT_xor(b,1);
for(int i=1;i<=19;i++){
FWT_xor(a,1);
for(int j=0;j<N;j++)a[j]=a[j]*b[j];
FWT_xor(a,-1);
for(int j=0;j<N;j++){
if(a[j]>0){a[j]=1;ans[i]=j;}
}
}
for(int i=1;i<=n;i++){
if(i>19){
if((i-19)%2){
printf("%d ",ans[18]);
}
else{
printf("%d ",ans[19]);
}
}
else{
printf("%d ",ans[i]);
}
}
return 0;
}
/*
23
1 2 4 8 16 32 64 128 256 512 1024 2048 4096 8192 16384 32768 65536 131072 0 0 0 0 0
*/
G. Greater and Greater
吐槽
我发现多校给的时间空间限制真的玄学。这题开两个二维bitset会mle,开一个就不炸。也不知道为啥。开一个如果按照数据范围也是800m会爆炸的。服了。
AC代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<bitset>
#include<iostream>
using namespace std;
const int NN=150010;
const int MM=40001;
bitset<MM> s[MM];
int a[NN],b[MM],bb[MM];
int num1[NN];
bool cmp_b(int x,int y){
return b[x]<b[y];
}
int so[MM];
int main(){
int n,m;scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d",a+i);
}
for(int i=1;i<=m;i++){
scanf("%d",b+i);
bb[i]=b[i];
so[i]=i;
}
sort(so+1,so+m+1,cmp_b);
sort(bb+1,bb+m+1);
for(int i=1;i<=m;i++){
bitset <MM> I;
I[so[i]-1]=1;
s[i]=s[i-1]|I;
}
for(int i=1;i<=n;i++){
num1[i]=upper_bound(bb+1,bb+m+1,a[i])-bb-1;
}
bitset<MM> cur;
bitset<MM> lastcur;
bitset<MM> Im;Im[m-1]=1;
for(int i=0;i<m;i++)lastcur[i]=1;
int ans=0;
for(int i=n;i>=1;i--){
cur=((lastcur>>1)|Im)&s[num1[i]];
if(i<=n-m+1&&cur[0]==1){
ans++;
}
lastcur=cur;
}
printf("%d\n",ans);
return 0;
}
h.Happy Triangle
题解
这题标答是手动平衡树的。但是这个题离线,直接线段树维护区间最小差值就完了。然而我的代码只跑过了50%的数据就挖了。有大佬帮忙康康评论一下。。。我把每个可能是数都按顺序映射到线段树的基本数组中,删除也安排了唯一的点删除,感觉巨对。想破头也不知道咋就挖了。
没AC代码
#include<cstdio>
#include<cstring>
#include<set>
#include<cstring>
#include<algorithm>
#include<map>
using namespace std;
multiset<int> se;
const int NN=2e5+10;
int minn[NN<<2];
int left[NN<<2];
int right[NN<<2];
int op[NN],v[NN];
const int oo=1e9+10;
map <int,int>ma;
void up(int cur){
if(left[cur<<1]==-1)left[cur]=left[cur<<1|1];
else left[cur]=left[cur<<1];
if(right[cur<<1|1]==-1)right[cur]=right[cur<<1];
else right[cur]=right[cur<<1|1];
minn[cur]=min(minn[cur<<1],minn[cur<<1|1]);
if(left[cur<<1|1]!=-1&&right[cur<<1]!=-1)minn[cur]=min(minn[cur],left[cur<<1|1]-right[cur<<1]);
}
void inser(int cur,int l,int r,int p,int val){
if(l==r){
left[cur]=val;
right[cur]=val;
minn[cur]=oo;
return;
}
int mid=(l+r)>>1;
if(p<=mid)inser(cur<<1,l,mid,p,val);
else inser(cur<<1|1,mid+1,r,p,val);
up(cur);
}
void eras(int cur,int l,int r,int p){
if(l==r){
left[cur]=-1;
right[cur]=-1;
minn[cur]=oo;
return;
}
int mid=(l+r)>>1;
if(p<=mid)eras(cur<<1,l,mid,p);
else eras(cur<<1|1,mid+1,r,p);
up(cur);
}
int ans=oo,lastr=-1;
void get_min(int cur,int l,int r,int x,int y){
if(x<=l&&r<=y){
ans=min(ans,minn[cur]);
if(left[cur]!=-1&&lastr!=-1){
ans=min(ans,left[cur]-lastr);
}
lastr=right[r];
return;
}
int mid=(l+r)>>1;
if(x<=mid)get_min(cur<<1,l,mid,x,y);
if(y>mid) get_min(cur<<1|1,mid+1,r,x,y);
}
struct pp{
int v,id;
pp(int a=0,int b=0):v(a),id(b){};
} num[NN];
bool cmp_v(pp x,pp y){
if(x.v<y.v)return 1;
else if(x.v==y.v&&x.id<y.id)return 1;
else return 0;
}
int a[NN];
int main(){
int cnt=0;
memset(left,-1,sizeof(left));
memset(right,-1,sizeof(right));
int n;scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d%d",&op[i],&v[i]);
if(op[i]==1){num[++cnt].v=v[i];num[cnt].id=i;}
}
if(cnt<=1){
for(int i=1;i<=n;i++)printf("No\n");
return 0;
}
sort(num+1,num+cnt+1,cmp_v);
for(int i=1;i<=cnt;i++){
a[num[i].id]=i;
}
for(int i=1;i<=n;i++){
if(op[i]==2){
pp neww;neww.id=0;neww.v=v[i];
a[i]=lower_bound(num+1,num+cnt+1,neww,cmp_v)-num+ma[v[i]];
ma[v[i]]=ma[v[i]]+1;
}
else if(op[i]==3){
pp neww;neww.id=0;neww.v=v[i];
a[i]=lower_bound(num+1,num+cnt+1,neww,cmp_v)-num;
}
}
for(int i=1;i<=(cnt<<2);i++)minn[i]=oo;
for(int i=1;i<=n;i++){
if(op[i]==1){
inser(1,1,cnt,a[i],v[i]);
se.insert(v[i]);
}
else if(op[i]==2){
eras(1,1,cnt,a[i]);
se.erase(se.find(v[i]));//直接erase掉vi不对
}
else{
set<int>::iterator pos=se.upper_bound(v[i]);
int aa=-1,b=-1;
if(pos!=se.begin()){
--pos;
b=*pos;
if(pos!=se.begin()){
--pos;
aa=*pos;
}
}
if(aa!=-1&&b!=-1&&aa+b>v[i]){
printf("Yes\n");
continue;
}
ans=oo;lastr=-1;
get_min(1,1,cnt,max(1,a[i]-1),cnt);
if(ans<v[i]){
printf("Yes\n");
}
else{
printf("No\n");
}
}
}
return 0;
}
/*
13
3 1
3 2
3 3
1 4
1 4
1 5
2 4
2 4
3 2
3 1
1 4
3 2
3 1
*/
吐槽
卧槽上面那个代码手误了。线段树查询right[cur]写成right[r]了。就这居然能跑过50%的数据。服了。想了好几天,茶饭不思的。还以为有情况没考虑到。。。
AC代码
#include<cstdio>
#include<cstring>
#include<set>
#include<cstring>
#include<algorithm>
#include<map>
using namespace std;
multiset<long long> se;
const long long NN=400100;
long long minn[NN<<2];
long long left[NN<<2];
long long right[NN<<2];
long long op[NN],v[NN];
const long long oo=2e9+10;
map <long long,long long>ma;
void up(long long cur){
if(left[cur<<1]==-1)left[cur]=left[cur<<1|1];
else left[cur]=left[cur<<1];
if(right[cur<<1|1]==-1)right[cur]=right[cur<<1];
else right[cur]=right[cur<<1|1];
minn[cur]=min(minn[cur<<1],minn[cur<<1|1]);
if(left[cur<<1|1]!=-1&&right[cur<<1]!=-1)minn[cur]=min(minn[cur],left[cur<<1|1]-right[cur<<1]);
}
void inser(long long cur,long long l,long long r,long long p,long long val){
if(l==r){
left[cur]=val;
right[cur]=val;
minn[cur]=oo;
return;
}
long long mid=(l+r)>>1;
if(p<=mid)inser(cur<<1,l,mid,p,val);
else inser(cur<<1|1,mid+1,r,p,val);
up(cur);
}
void eras(long long cur,long long l,long long r,long long p){
if(l==r){
left[cur]=-1;
right[cur]=-1;
minn[cur]=oo;
return;
}
long long mid=(l+r)>>1;
if(p<=mid)eras(cur<<1,l,mid,p);
else eras(cur<<1|1,mid+1,r,p);
up(cur);
}
long long ans=oo,lastr=-1;
void get_min(long long cur,long long l,long long r,long long x,long long y){
if(x<=l&&r<=y){
ans=min(ans,minn[cur]);
if(left[cur]!=-1&&lastr!=-1){
ans=min(ans,left[cur]-lastr);
}
lastr=right[cur];
return;
}
long long mid=(l+r)>>1;
if(x<=mid)get_min(cur<<1,l,mid,x,y);
if(y>mid) get_min(cur<<1|1,mid+1,r,x,y);
}
struct pp{
long long v,id;
pp(long long a=0,long long b=0):v(a),id(b){};
} num[NN];
bool cmp_v(pp x,pp y){
if(x.v<y.v)return 1;
else if(x.v==y.v&&x.id<y.id)return 1;
else return 0;
}
long long a[NN];
int main(){
long long cnt=0;
memset(left,-1,sizeof(left));
memset(right,-1,sizeof(right));
long long n;scanf("%lld",&n);
for(long long i=1;i<=n;i++){
scanf("%lld%lld",&op[i],&v[i]);
if(op[i]==1){num[++cnt].v=v[i];num[cnt].id=i;}
}
if(cnt<1){
for(long long i=1;i<=n;i++)printf("No\n");
return 0;
}
sort(num+1,num+cnt+1,cmp_v);
for(long long i=1;i<=cnt;i++){
a[num[i].id]=i;
}
for(long long i=1;i<=n;i++){
if(op[i]==2){
pp neww;neww.id=0;neww.v=v[i];
a[i]=lower_bound(num+1,num+cnt+1,neww,cmp_v)-num+ma[v[i]];
ma[v[i]]=ma[v[i]]+1;
}
else if(op[i]==3){
pp neww;neww.id=0;neww.v=v[i];
a[i]=lower_bound(num+1,num+cnt+1,neww,cmp_v)-num;
}
}
for(long long i=1;i<=(cnt<<2);i++)minn[i]=oo;
for(long long i=1;i<=n;i++){
if(op[i]==1){
inser(1,1,cnt,a[i],v[i]);
se.insert(v[i]);
}
else if(op[i]==2){
eras(1,1,cnt,a[i]);
se.erase(se.lower_bound(v[i]));//直接erase掉vi不对
}
else{
set<long long>::iterator pos=se.upper_bound(v[i]);
long long aa=-1,b=-1;
if(pos!=se.begin()){
--pos;
b=*pos;
if(pos!=se.begin()){
--pos;
aa=*pos;
}
}
if(aa!=-1&&b!=-1&&aa+b>v[i]){
printf("Yes\n");
continue;
}
ans=oo;lastr=-1;
get_min(1,1,cnt,max(1ll,a[i]-1),cnt);
if(ans<v[i]){
printf("Yes\n");
}
else{
printf("No\n");
}
}
}
return 0;
}
/*
13
3 1
3 2
3 3
1 4
1 4
1 5
2 4
2 4
3 2
3 1
1 4
3 2
3 1
*/
I.Interval
这个题是个网格最小割。把区间弄成点,没有花钱限制的流量无穷,限制的流量是价格。完了源点是区间(1,n),汇点新建,所有大小为1的区间往汇点连正无穷的边。跑最小割。然而我的代码跑过了65%的数据就wa了。不知道为啥。。有大佬帮忙康康评论一下。。。。
没AC代码
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
using namespace std;
long long oo=2e9+10;
long long read(){
long long x = 0; long long zf = 1; char ch = ' ';
while (ch != '-' && (ch < '0' || ch > '9')) ch = getchar();
if (ch == '-') zf = -1, ch = getchar();
while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar(); return x * zf;
}
const int NN=510;
const int MM=NN*NN;
const int VV=NN*NN;
struct Edge{
int to;
long long dis;
} edges[MM<<2];
vector <int> con[VV];
int cur[VV];
int edge_num=-1;
int n, m, s, t;
void addEdge2(int from, int to, long long dis){
edges[++edge_num].to = to;
edges[edge_num].dis = dis;
con[from].push_back(edge_num);
}
void addEdge(int from, int to, long long dis){
addEdge2(from, to, dis), addEdge2(to, from, 0);
}
int d[VV];
long long DFS(int u, long long flow){
if (u == t) return flow;
long long _flow = 0, __flow;
int up=con[u].size();
for (int i = cur[u]; i<up ; i++){
int c_e=con[u][i];
int v = edges[c_e].to;
if (d[v] == d[u] + 1 && edges[c_e].dis > 0){
__flow = DFS(v, min(flow, edges[c_e].dis));
flow -= __flow;
edges[c_e].dis -= __flow;
_flow += __flow;
edges[c_e^1].dis += __flow;
if (!flow)
break;
}
}
if (!_flow) d[u] = -1;
return _flow;
}
bool BFS(){
memset(d, -1, sizeof(d));
queue<int> que; que.push(s);
d[s] = 0; int u, _new;
while (!que.empty()){
u = que.front(), que.pop();
int up=con[u].size();
for (int i = 0; i<up; i++){
int c_e=con[u][i];
_new = edges[c_e].to;
if (d[_new] == -1 && edges[c_e].dis > 0){
d[_new] = d[u] + 1;
que.push(_new);
}
}
}
return (d[t] != -1);
}
long long dinic(){
long long max_flow = 0;
while (BFS()){
for (int i = 1; i <= n; ++i) cur[i] = 0;
max_flow += DFS(s, oo);
if(max_flow>=oo)return max_flow;
}
return max_flow;
}
int c[NN][NN][2];
int id[NN][NN];
int main(){
int n,m;
scanf("%d%d",&n,&m);
memset(c,-1,sizeof(c));
long long maxn=0;
for(int i=1;i<=m;i++){
int x,y,co;
char op[1];
scanf("%d%d%s%d",&x,&y,op,&co);
maxn+=co;
if(op[0]=='L')c[x][y][0]=co;
else c[x][y][1]=co;
}
oo=maxn+1;
int v=0;
for(int i=1;i<=n;i++){
for(int j=i;j<=n;j++){
id[i][j]=++v;
}
}
for(int i=1;i<=n;i++){
for(int j=i+1;j<=n;j++){
if(c[i][j][0]!=-1){
addEdge(id[i][j],id[i+1][j],c[i][j][0]);
}
else addEdge(id[i][j],id[i+1][j],oo);
if(c[i][j][1]!=-1){
addEdge(id[i][j],id[i][j-1],c[i][j][1]);
}
else addEdge(id[i][j],id[i][j-1],oo);
}
}
s=id[1][n];t=v+1;
for(int i=1;i<=n;i++){addEdge(id[i][i],t,oo);}
long long ans=dinic();
if(ans>=oo){
printf("-1\n");
}
else printf("%lld\n",ans);
return 0;
}