21杭电多校第三场
A
离谱平衡树题 咕
B
好像更离谱的结论题
C
设\(ans_i\)表示以\(a\)串的\(i\)为起始匹配位置的失配数
则\(ans_i=\sum\limits_{j=0}^{m-1} [b_j\neq a_{i+j}]\),将\(b\)串翻转,即有\(ans_i=\sum\limits_{j=0}^{m-1} [b_{m-1-j}\neq a_{i+j}]\)成为一个卷积形式
可以枚举\(b_j\)的取值进行\(0/1\)赋值来卷积得到\(ans_i\)
最后将\(cnt[ans_i]++\),输出\(cnt\)的前缀和
#include<bits/stdc++.h>
#define inf 2139062143
#define ll long long
#define db double
#define ld long double
#define ull unsigned long long
#define MAXN 200100
#define MOD 998244353
#define Fill(a,x) memset(a,x,sizeof(a))
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;++i)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;--i)
#define ren for(int i=fst[x];i;i=nxt[i])
#define pls(a,b) (a+b)%MOD
#define mns(a,b) (a-b+MOD)%MOD
#define mul(a,b) (1LL*(a)*(b))%MOD
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
return x*f;
}
const db pi=acos(-1);
int n,m,rev[MAXN<<2],sum,ans[MAXN<<2],cnt[MAXN];
char s1[MAXN],s2[MAXN];
struct Cd{db x,y;Cd(db X=0,db Y=0){x=X,y=Y;};};
Cd operator + (Cd a,Cd b) {return (Cd){a.x+b.x,a.y+b.y};}
Cd operator - (Cd a,Cd b) {return (Cd){a.x-b.x,a.y-b.y};}
Cd operator * (Cd a,Cd b) {return (Cd){a.x*b.x-a.y*b.y,a.y*b.x+a.x*b.y};}
Cd A[MAXN<<2],B[MAXN<<2];
void fft(Cd *a,int n,int f)
{
rep(i,0,n-1) if(i<rev[i]) swap(a[i],a[rev[i]]);
for(int i=1;i<n;i<<=1)
{
Cd wn(cos(pi/i),f*sin(pi/i));
for(int j=0;j<n;j+=i<<1)
{
Cd w(1,0),x,y;
for(int k=0;k<i;k++,w=w*wn)
x=a[k+j],y=a[k+j+i]*w,a[j+k]=x+y,a[j+k+i]=x-y;
}
}
if(f==1) return ;rep(i,0,n-1) a[i].x/=(db)n;
}
int lg,lmt;
void solve(Cd *a,Cd *b)
{
fft(a,lmt,1);fft(b,lmt,1);rep(i,0,lmt-1) a[i]=a[i]*b[i];
fft(a,lmt,-1);
}
int main()
{
rep(T,1,read())
{
n=read(),m=read(),sum=n+m;
scanf("%s%s",s1,s2);rep(j,0,m/2-1) swap(s2[j],s2[m-1-j]);
lg=ceil(log2(sum)),lmt=1<<lg;
rep(i,0,lmt-1) rev[i]=(rev[i>>1]>>1)|((i&1)<<(lg-1)),ans[i]=0;
rep(j,0,9)
{
rep(i,0,lmt-1) A[i]={0,0},B[i]={0,0};
rep(i,0,n-1) if(s1[i]!='0'+j&&s1[i]!='*') A[i]={1,0};
rep(i,0,m-1) if(s2[i]=='0'+j) B[i]={1,0};
solve(A,B);
rep(i,0,n+m-1) ans[i]+=(int)(A[i].x+0.5);
}
rep(i,0,m) cnt[i]=0;
rep(i,m-1,n-1) cnt[ans[i]]++;
rep(i,1,m) cnt[i]+=cnt[i-1];
rep(i,0,m) printf("%d\n",cnt[i]);
}
}
(\(fft\)会比\(ntt\)快不少
D
显然所有斜率相同的直线可以归为一组
每次都应该与之前的尽可能不同
所以每一轮从所有组里选一个出来加入集合,这样不断模拟直到所有边都被选过
#include<bits/stdc++.h>
#define inf 2139062143
#define ll long long
#define db double
#define ld long double
#define ull unsigned long long
#define MAXN 100100
#define MOD 998244353
#define Fill(a,x) memset(a,x,sizeof(a))
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;++i)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;--i)
#define ren for(int i=fst[x];i;i=nxt[i])
#define pls(a,b) (a+b)%MOD
#define mns(a,b) (a-b+MOD)%MOD
#define mul(a,b) (1LL*(a)*(b))%MOD
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int gcd(int a,int b){return !b?a:gcd(b,a%b);}
int n,tot,cnt[MAXN],ans;
map<pii,int> m;
vector<int> v[2];
int main()
{
int a,b,c,d,g,now;rep(T,1,read())
{
scanf("%d",&n);m.clear();v[0].clear(),v[1].clear();
rep(i,1,n) cnt[i]=0;
rep(i,1,n)
{
scanf("%d%d%d%d",&a,&c,&b,&d);
a=b-a,b=d-c,c=abs(a),d=abs(b);
if(!a) a=0,b=1;else if(!b) a=1,b=0;
else
{
g=gcd(c,d);a/=g,b/=g;
if(a<0) a=-a,b=-b;
}
if(!m[{a,b}]) tot++,cnt[1]++,m[{a,b}]++;
else cnt[m[{a,b}]]--,cnt[++m[{a,b}]]++;
}
rep(i,1,n) rep(j,1,cnt[i]) v[1].pb(i);
ans=0;rep(i,1,n)
{
ans--;now=i&1;for(auto x:v[now])
{
ans++;if(x>1) v[now^1].pb(x-1);
printf("%d\n",ans);
}
v[now].clear();
}
}
}
E
好像还挺有意思的\(dp\) 咕
F
网络流的奇妙增广 咕
G
每次出现\(m_i=1\)都需要重新计算,预处理一下这种情况下的前缀和
查询的时候判断一下\(l,r\)之间是否有断点即可
#include<bits/stdc++.h>
#define inf 2139062143
#define ll long long
#define db double
#define ld long double
#define ull unsigned long long
#define MAXN 100100
#define MOD 998244353
#define Fill(a,x) memset(a,x,sizeof(a))
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;++i)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;--i)
#define ren for(int i=fst[x];i;i=nxt[i])
#define pls(a,b) (a+b)%MOD
#define mns(a,b) (a-b+MOD)%MOD
#define mul(a,b) (1LL*(a)*(b))%MOD
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int n,q,sum[MAXN][3],bl[MAXN],a[MAXN];char s[10];
int hsh(char c){return isdigit(c)?c-'0':10+c-'A';}
int main()
{
int a,b;rep(T,1,read())
{
n=read(),q=read();rep(i,1,n) bl[i]=0;
rep(i,1,n)
{
bl[i]=(2-read())?i:0;scanf("%s",s);
rep(j,0,2) sum[i][j]=hsh(s[j<<1])*16+hsh(s[j<<1|1]);
}
rep(i,1,n)
{
int j=i+1;while(!bl[j])
{
rep(k,0,2) sum[j][k]+=sum[j-1][k];
bl[j++]=i;
}
i=j-1;
}
while(q--)
{
a=read(),b=read();
if(bl[a-1]!=bl[b])
rep(i,0,2) printf("%02X",min(sum[b][i],255));
else
rep(i,0,2) printf("%02X",min(sum[b][i]-sum[a-1][i],255));
puts("");
}
}
}
H
分块 咕
I
暴力枚举走到每个点的\(\sum a_i\)对应的最大\(\sum b_i\)
每次转移的时候使用归并排序,并且用单调栈优化掉\(a<a',b<b‘\)的这些显然没有用的点
因为数据随机跑的还挺快的
#include<bits/stdc++.h>
#define inf 2139062143
#define ll long long
#define db double
#define ld long double
#define ull unsigned long long
#define MAXN 150
#define MOD 998244353
#define Fill(a,x) memset(a,x,sizeof(a))
#define rep(i,s,t) for(register int i=(s),i##end=(t);i<=i##end;++i)
#define dwn(i,s,t) for(register int i=(s),i##end=(t);i>=i##end;--i)
#define ren for(int i=fst[x];i;i=nxt[i])
#define pls(a,b) (a+b)%MOD
#define mns(a,b) (a-b+MOD)%MOD
#define mul(a,b) (1LL*(a)*(b))%MOD
#define pii pair<int,int>
#define pb push_back
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
return x*f;
}
vector<pii>f[MAXN][MAXN];
vector<pii>::iterator it1,it2;
pii st[500005],tmp;ll ans;
int n,A[MAXN][MAXN],B[MAXN][MAXN],sa[MAXN],sb[MAXN],tp;
int main()
{
rep(T,1,read())
{
scanf("%d",&n);ans=0;
rep(i,1,n) rep(j,1,n) scanf("%d",&A[i][j]);
rep(i,1,n) rep(j,1,n) scanf("%d",&B[i][j]);
sa[0]=0;rep(j,1,n) sa[j]=sa[j-1]+A[1][j];
sb[0]=0;rep(j,1,n) sb[j]=sb[j-1]+B[1][j];
rep(j,1,n) f[1][j].pb({sa[j],sb[j]});
rep(i,2,n)
{
#define a first
#define b second
it1=f[i-1][1].begin();tp=0;
while(it1!=f[i-1][1].end())
{
tmp={(it1->a)+A[i][1],(it1->b)+B[i][1]};
while(tp&&st[tp].a<=tmp.a&&st[tp].b<=tmp.b) tp--;
st[++tp]=tmp;
it1++;
}
rep(k,1,tp) f[i][1].pb(st[k]);
rep(j,2,n)
{
it1=f[i-1][j].begin(),it2=f[i][j-1].begin();tp=0;
while(it1!=f[i-1][j].end()&&it2!=f[i][j-1].end())
{
if((it1->a)==(it2->a))
tmp={(it1->a)+A[i][j],max(it1->b,it2->b)+B[i][j]},it1++,it2++;
else if((it1->a)<(it2->a))
tmp={(it1->a)+A[i][j],(it1->b)+B[i][j]},it1++;
else
tmp={(it2->a)+A[i][j],(it2->b)+B[i][j]},it2++;
while(tp&&st[tp].a<=tmp.a&&st[tp].b<=tmp.b) tp--;
st[++tp]=tmp;
}
while(it1!=f[i-1][j].end())
{
tmp={(it1->a)+A[i][j],(it1->b)+B[i][j]};
while(tp&&st[tp].a<=tmp.a&&st[tp].b<=tmp.b) tp--;
st[++tp]=tmp;
it1++;
}
while(it2!=f[i][j-1].end())
{
tmp={(it2->a)+A[i][j],(it2->b)+B[i][j]};
while(tp&&st[tp].a<=tmp.a&&st[tp].b<=tmp.b) tp--;
st[++tp]=tmp;
it2++;
}
rep(k,1,tp) f[i][j].pb(st[k]);
}
rep(j,1,n) f[i-1][j].clear();
}
for(it1=f[n][n].begin();it1!=f[n][n].end();it1++) ans=max(ans,1LL*(it1->a)*(it1->b));
rep(j,1,n) f[n][j].clear();
printf("%lld\n",ans);
}
}
J
若只询问一次是一个经典的问题
二分一个权值\(x\in[-w_m-1,w_m+1]\),对所有白边权\(+=x\),求一个\(MST\),判断树里白边数量是否\(\ge need\)条
最终得到一个白边边权加了\(x\),白边数量\(\ge need\)的最小生成树,答案即为\(MST\)边权和\(-need\cdot x\)
(当白边\(> need\)时,对有解的情况来说,一定有一些\(val_w+x==val_b\),\(MST\)边权和不变,可以直接作差得到答案
此题需要询问所有\(need\)
有一个结论:最终的\(MST\)上所有边一定为纯黑的\(MST\)或纯白的\(MST\)上的边
这样可以预处理出两个纯色\(MST\)上的边,将复杂度从\(O(m)\)变为\(O(n)\)
由于值域较小,可以枚举所有加权\(x\),每次做一次最小生成树,对这种情况下的白边边数更新答案
最后查询时对每个\(x\),由经典问题解法,根据最小\(\ge x\)且有答案记录的\(x\),输出答案
#include<bits/stdc++.h>
#define inf 2139062143
#define ll long long
#define db double
#define ld long double
#define ull unsigned long long
#define MAXN 100100
#define MOD 998244353
#define Fill(a,x) memset(a,x,sizeof(a))
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;++i)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;--i)
#define ren for(int i=fst[x];i;i=nxt[i])
#define pls(a,b) (a+b)%MOD
#define mns(a,b) (a-b+MOD)%MOD
#define mul(a,b) (1LL*(a)*(b))%MOD
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int n,m,k,fa[MAXN],tot,ans[MAXN],val[MAXN];
struct Edge{int u,v,w;}e1[MAXN<<1],e2[MAXN<<1];
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
bool operator < (Edge &x,Edge &y){return x.w<y.w;}
int merge(int u,int v)
{
int fu=find(u),fv=find(v);
if(fu!=fv) return fa[fu]=fv;
else return 0;
}
void work(Edge *e)
{
sort(e+1,e+m+1);tot=0;rep(i,1,n) fa[i]=i;
rep(i,1,m) if(merge(e[i].u,e[i].v)) e[++tot]=e[i];
}
void solve(int x)
{
rep(i,1,n) fa[i]=i;int i=1,j=1,sum=0,num=0;
while(i<n&&j<n)
{
if(e2[j].w+x<=e1[i].w)
{if(merge(e2[j].u,e2[j].v)) sum+=e2[j].w+x,num++;j++;}
else
{if(merge(e1[i].u,e1[i].v)) sum+=e1[i].w;i++;}
}
while(i<n)
{if(merge(e1[i].u,e1[i].v)) sum+=e1[i].w;i++;}
while(j<n)
{if(merge(e2[j].u,e2[j].v)) sum+=e2[j].w+x,num++;j++;}
ans[num]=sum,val[num]=x;
}
int main()
{
rep(T,1,read())
{
n=read(),m=read();
rep(i,1,m)
{
scanf("%d%d%d%d",&e1[i].u,&e1[i].v,&e1[i].w,&e2[i].w);
e2[i].u=e1[i].u,e2[i].v=e1[i].v;
}
work(e1);work(e2);
rep(i,0,n-1) ans[i]=val[i]=0;
rep(i,0,1000) solve(i);
dwn(i,n-2,0) if(!ans[i]) ans[i]=ans[i+1],val[i]=val[i+1];
rep(i,0,n-1) printf("%d\n",ans[i]-i*val[i]);
}
}
K
签到题,因为线段树每层节点的长度只可能是相邻的数,暴力模拟即可
#include<bits/stdc++.h>
#define inf 2139062143
#define ll long long
#define db double
#define ld long double
#define ull unsigned long long
#define MAXN 100100
#define MOD 998244353
#define Fill(a,x) memset(a,x,sizeof(a))
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;++i)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;--i)
#define ren for(int i=fst[x];i;i=nxt[i])
#define pls(a,b) (a+b)%MOD
#define mns(a,b) (a-(b)+MOD)%MOD
#define mul(a,b) (1LL*(a)*(b))%MOD
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
using namespace std;
inline ll read()
{
ll x=0,f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
return x*f;
}
ll ans,n,k,a[3],b[3],c[3],d[3];
int main()
{
rep(T,1,read())
{
n=read(),k=read();
a[1]=n,c[1]=1;a[2]=c[2]=0;ans=0;
while(a[1]||a[2])
{
ans+=c[1]+c[2];b[1]=0,b[2]=0,d[1]=d[2]=0;
if(a[1]>k)
{
if(a[1]&1) b[1]=a[1]/2,b[2]=a[1]/2+1,d[1]=c[1],d[2]=c[1];
else b[1]=a[1]/2,d[1]=c[1]<<1;
}
if(a[2]&&a[2]>k)
{
if(a[2]&1) b[1]=a[2]/2,b[2]=a[2]/2+1,d[1]+=c[2],d[2]+=c[2];
else b[2]=a[2]/2,d[2]+=c[2]<<1;
}
a[1]=b[1],a[2]=b[2],c[1]=d[1],c[2]=d[2];
}
printf("%lld\n",ans);
}
}
L
极其麻烦的状压/轮廓线 咕