21杭电多校第三场

A

离谱平衡树题 咕

B

好像更离谱的结论题

C

(ans_i)表示以(a)串的(i)为起始匹配位置的失配数

(ans_i=sumlimits_{j=0}^{m-1} [b_j eq a_{i+j}]),将(b)串翻转,即有(ans_i=sumlimits_{j=0}^{m-1} [b_{m-1-j} eq 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
",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
",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
",ans);
    }
}

J

若只询问一次是一个经典的问题

二分一个权值(xin[-w_m-1,w_m+1]),对所有白边权(+=x),求一个(MST),判断树里白边数量是否(ge need)

最终得到一个白边边权加了(x),白边数量(ge need)的最小生成树,答案即为(MST)边权和(-needcdot 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
",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
",ans);
	}
}

L

极其麻烦的状压/轮廓线 咕

原文地址:https://www.cnblogs.com/yyc-jack-0920/p/15081112.html