5.31 NOI 模拟

\(T1\ Beauty\)



\(T2\ Jump\)

考场上一开始想的是树套树,然后我看到了\(128MB,\)

于是乎附上\(56pts\ MLE\)代码在空间\(512MB\)可以获得\(84pts,\)时限调到\(8s,\)可以\(AC\)

#define Eternal_Battle ZXK
#include<bits/stdc++.h>
#define MAXN 200005
using namespace std;
struct City
{
	int x,y;
}cy[MAXN];
struct Link
{
    int val,l,r,u,d;
};
vector<Link>rd[MAXN];
int dis[MAXN],ID1,ID2,rt,n,m,h,w;
bool vis[MAXN];
struct Tr1
{
	int l,r,ls,rs;
	int rt;
}tr1[MAXN<<3];
struct node
{
	int l,r,ls,rs;
	vector<int>Get;
}tr2[MAXN<<3];
priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > >q;
void Query2(int now,int L,int R,int l,int r,int st,int val)
{
	 if(!now) return ;
	 if(L>=l&&R<=r)
	 {
	 	for(int i=0;i<tr2[now].Get.size();i++)
	 	{
	 	    int y=tr2[now].Get[i];
			if(dis[y]>dis[st]+val) 
			{
			   dis[y]=dis[st]+val;
			   q.push(make_pair(dis[y],y));
			}	
		}
		return ;
	 }
	 int mid=(L+R)>>1;
	 if(l<=mid) Query2(tr2[now].ls,L,mid,l,r,st,val);
	 if(r>mid)  Query2(tr2[now].rs,mid+1,R,l,r,st,val);
}
void Query1(int now,int L,int R,int l,int r,int u,int d,int st,int val)
{
	 if(!now) return ;
//	 cout<<"Ins1: "<<st<<" "<<L<<" "<<R<<"\n";
	 if(L>=l&&R<=r)
	 {
	 	Query2(tr1[now].rt,1,w,u,d,st,val);
	 	return ;
	 }
	 int mid=(L+R)>>1;
	 if(l<=mid) Query1(tr1[now].ls,L,mid,l,r,u,d,st,val);
	 if(r>mid)  Query1(tr1[now].rs,mid+1,R,l,r,u,d,st,val);
}
void dij()
{
	memset(dis,0x3f,sizeof(dis));
	q.push(make_pair(dis[1],1));
	dis[1]=0;
	while(q.size())
	{
		int now=q.top().second;
		q.pop();
		if(vis[now]) continue;
		vis[now]=true;
		for(int i=0;i<rd[now].size();i++)
		{
			int val=rd[now][i].val;
			int l=rd[now][i].l;
			int r=rd[now][i].r;
			int u=rd[now][i].u;
			int d=rd[now][i].d;
			Query1(rt,1,h,l,r,u,d,now,val);
			continue;
		    for(int j=1;j<=n;j++)
		    {
		    	if(cy[j].x>=l&&cy[j].x<=r&&cy[j].y>=u&&cy[j].y<=d) 
		    	{
		    		if(dis[j]>dis[now]+val)
		    		{
		    		    dis[j]=dis[now]+val;
		    		    q.push(make_pair(dis[j],j));
						continue;
					}
				}
			}
		}
	}
}
void Insert2(int &now,int l,int r,int y,int id)
{
	 if(l>y||r<y) return ;
	 if(!now) now=++ID2;
//     cout<<"Ins2: "<<now<<" "<<l<<" "<<r<<" "<<y<<" "<<id<<"\n";
	 tr2[now].Get.push_back(id);
	 if(l==r) return ;
	 int mid=(l+r)>>1;
	 Insert2(tr2[now].ls,l,mid,y,id);
	 Insert2(tr2[now].rs,mid+1,r,y,id);
}
void Insert1(int &now,int l,int r,int x,int y,int id)
{
	if(l>x||r<x) return ;
	if(!now) now=++ID1;
//	cout<<"Ins1: "<<l<<" "<<r<<" "<<x<<"\n"; 
	Insert2(tr1[now].rt,1,w,y,id);
	if(l==r) return ;
    int mid=(l+r)>>1;
    Insert1(tr1[now].ls,l,mid,x,y,id);
    Insert1(tr1[now].rs,mid+1,r,x,y,id);
}
int main()
{
	freopen("jump.in","r",stdin);
	freopen("jump.out","w",stdout);
	scanf("%d%d%d%d",&n,&m,&h,&w);
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d",&cy[i].x,&cy[i].y);
		Insert1(rt,1,h,cy[i].x,cy[i].y,i);
	} 
    for(int i=1,l,r,u,d,cs,poz;i<=m;i++)
    {
    	scanf("%d%d%d%d%d%d",&poz,&cs,&l,&r,&u,&d);
    	rd[poz].push_back((Link){cs,l,r,u,d});
	}
	dij();
	for(int i=2;i<=n;i++)
	{
		printf("%d\n",dis[i]);
	}
}

我把内层线段树用\(set\)代替,好,只有\(60pts\)是怎么会是呢?

附上\(60pts\ TLE\)代码,常数真的很大

#define Eternal_Battle ZXK
#include<bits/stdc++.h>
#define MAXN 260005
using namespace std;
template<class T>
T Read()
{
	T x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9')
	{
		if(ch=='-')
		f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^'0');
		ch=getchar();
	}
	return x*f;
}
int (*read)()=Read<int>;
#define read Read<int>
struct Link
{
    int val,l,r,u,d;
};
vector<Link>rd[MAXN];
int dis[MAXN],ID1,ID2,rt,n,m,h,w;
bool vis[MAXN];
struct Tr1
{
	int ls,rs;
	set<pair<int,int> >Get;
}tr1[MAXN<<2];
priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > >q;
void Query1(int now,int L,int R,int l,int r,int u,int d,int st,int val)
{
	 if(!now) return ;
//	 cout<<"Ins1: "<<st<<" "<<L<<" "<<R<<"\n";
	 if(L>=l&&R<=r)
	 {
	 	pair<int,int>Now;
	 	Now=make_pair(u,0);
	 	set<pair<int,int> >::iterator it=tr1[now].Get.lower_bound(Now);
	 	for(;it->first<=d&&it!=tr1[now].Get.end();it++)
	 	{
	 		int y=it->second;
	 		if(dis[y]>dis[st]+val)
	 		{
	 		   dis[y]=dis[st]+val;
			   q.push(make_pair(dis[y],y));	
			}
		}
	 	return ;
	 }
	 int mid=(L+R)>>1;
	 if(l<=mid) Query1(tr1[now].ls,L,mid,l,r,u,d,st,val);
	 if(r>mid)  Query1(tr1[now].rs,mid+1,R,l,r,u,d,st,val);
}
void dij()
{
	memset(dis,0x3f,sizeof(dis));
	q.push(make_pair(dis[1],1));
	dis[1]=0;
	while(q.size())
	{
		int now=q.top().second;
		q.pop();
		if(vis[now]) continue;
		vis[now]=true;
		for(int i=0;i<rd[now].size();i++)
		{
			int val=rd[now][i].val;
			int l=rd[now][i].l;
			int r=rd[now][i].r;
			int u=rd[now][i].u;
			int d=rd[now][i].d;
			Query1(rt,1,h,l,r,u,d,now,val);
		}
	}
}
void Insert1(int &now,int l,int r,int x,int y,int id)
{
	if(l>x||r<x) return ;
	if(!now) now=++ID1;
	tr1[now].Get.insert(make_pair(y,id));
	if(l==r) return ;
    int mid=(l+r)>>1;
    Insert1(tr1[now].ls,l,mid,x,y,id);
    Insert1(tr1[now].rs,mid+1,r,x,y,id);
}
int main()
{
	freopen("jump.in","r",stdin);
	freopen("jump.out","w",stdout);
	scanf("%d%d%d%d",&n,&m,&h,&w);
	for(int i=1,x,y;i<=n;i++)
	{
		x=read(); y=read();
		Insert1(rt,1,h,x,y,i);
	} 
    for(int i=1,l,r,u,d,cs,poz;i<=m;i++)
    {
    	poz=read(); cs=read(); l=read(); r=read(); u=read(); d=read();
    	rd[poz].push_back((Link){cs,l,r,u,d});
	}
	dij();
	for(int i=2;i<=n;i++)
	{
		printf("%d\n",dis[i]);
	}
}

加上一些优化,得到外层线段树套\(set\)代码,怎么这个技巧这么\(Nb\)呢?

附上\(100pts\)代码

#define Eternal_Battle ZXK
#include<bits/stdc++.h>
#define MAXN 150005
using namespace std;
int rd()
{
    int f=1,ans=0;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();}
    return f*ans;
}
priority_queue<pair<int,int> > que;
set<pair<int,int> >s[MAXN<<2];
int dis[MAXN<<2],vis[MAXN<<2],head[MAXN],cnt;
struct spe
{
    int x,y;
}g[MAXN];
struct Segment
{
	#define ls (k<<1)
	#define rs ((k<<1)|1)
    void change(int k,int l,int r,int x,int y,int id)
	{
        s[k].insert(make_pair(g[id].y,id));
        if(l==r) return;
        int mid=(l+r)>>1;
        if(x<=mid) change(ls,l,mid,x,y,id);
        if(mid<y) change(rs,mid+1,r,x,y,id);
        return;
    }
    void Query(int k,int l,int r,int x,int y,int L,int R,int id)
	{
        if(x<=l&&r<=y)
		{
            set<pair<int,int> >:: iterator it;
            while(!s[k].empty())
			{
                it=s[k].lower_bound(make_pair(L,-1));
                if(it==s[k].end()||(*it).first>R) break;
                int v=(*it).second;
                if(dis[v]>dis[id])
				{
                    dis[v]=dis[id];
                    que.push(make_pair(-dis[v],v));
                }
				s[k].erase(*it);
            }
            return;
        }
        int mid=(l+r)>>1;
        if(x<=mid) Query(ls,l,mid,x,y,L,R,id);
        if(mid<y) Query(rs,mid+1,r,x,y,L,R,id);
        return;
    }
}tr;
int n,m,w,h;
struct node
{
    int u,v,nex;
}x[MAXN<<1];
struct Spe
{
    int be,w,l,r,d,u;
}f[MAXN];
void add(int u,int v)
{
    x[cnt].u=u,x[cnt].v=v,x[cnt].nex=head[u],head[u]=cnt++;
}
int main()
{
	freopen("jump.in","r",stdin);
	freopen("jump.out","w",stdout);
    memset(head,-1,sizeof(head));
    n=rd(),m=rd(),w=rd(),h=rd();
    for(int i=1;i<=n;i++) 
	{
		g[i].x=rd(),g[i].y=rd();
	    tr.change(1,1,w,g[i].x,g[i].x,i);
	}
    for(int i=1;i<=m;i++) 
	{
    	f[i].be=rd(),f[i].w=rd();
		f[i].l=rd(),f[i].r=rd();
		f[i].d=rd(),f[i].u=rd();
		add(f[i].be,i);	
	}
    memset(dis,0x3f,sizeof(dis));
    dis[1]=0;que.push(make_pair(0,1));
    while(!que.empty())
	{
        int xx=que.top().second;que.pop();
        if(vis[xx]) continue;
        vis[xx]=1;
        if(xx>n)
		{
            tr.Query(1,1,w,f[xx-n].l,f[xx-n].r,f[xx-n].d,f[xx-n].u,xx);
            continue;
        }
        for(int i=head[xx];i!=-1;i=x[i].nex)
		{
            int v=x[i].v+n;
            if(dis[v]>dis[xx]+f[x[i].v].w)
			{
                dis[v]=dis[xx]+f[x[i].v].w;
                que.push(make_pair(-dis[v],v));
            }
        }
    }
	for(int i=2;i<=n;i++) printf("%d\n",dis[i]);
    return 0;
}

\(T3\ Combination\)

\[\sum_{0\le i\le n,k|i}\binom{n}{i} \\ =\sum_{0\le i\le n}[k|i]\binom{n}{i} \]

单位根反演

\[[k|i]=\frac{1}{k}\sum_{j=0}^{k-1}w_k^{i\times j} \]

反代

\[\sum_{0\le i\le n}\frac{1}{k}\sum_{j=0}^{k-1}w_k^{i\times j}\binom{n}{i} \\ =\frac{1}{k}\sum_{j=0}^{k-1}\sum_{0\le i\le n}(w_k^j)^i\binom{n}{i} \]

较为显然的二项式形式

\[\frac{1}{k}\sum_{j=0}^{k-1}(w_k^j+1)^n \]

\(\mod=998244353\)​可以直接计算

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define maxn 100010
const int p=998244353;
int n,k;
int ksm(int a,int b)
{
	int ans=1;
	for(;b;b>>=1,a=a*a%p)
	if(b&1)ans=ans*a%p;
	return ans;
}
int w,wn=1;
int ans;
signed main()
{
	cin>>n>>k;
	w=ksm(114514,(p-1)/k);
	for(int i=0;i<k;i++,wn=wn*w%p)
	ans=(ans+ksm(1+wn,n%(p-1)))%p;
	ans=ans*ksm(k,p-2)%p;
	cout<<ans;
}

我发现我的\(NTT\)真的只是背过的,我连模数意义下的单位根都不熟练

\(w_k=3^{(mod -1)/k}\)

至于\(\mod=998244853\)

可以使用多项式快速幂计算,复杂度\(O(k\log(n)\log(k))\)

然后发现题干中有一句话\(k=2^p\)

\(const(F(x))=[x^0]F(x)\)

打表发现

\(const((1+w_k^i)^n)=const((1+w_k)^n)\)

证明略

然后下面的式子就很通俗易懂了

而且发现了一个很nb的操作,在mod 998244353时,原根可以写114514!!!

于是乎我们的\(NTT\)可以这么写

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int G=114514,mod=998244353;

原根性质,\(a^{\phi(m)}=1(\mod m)\)

\(k\)次单位根\(a^{(mod-1)/k}\)

然后代码

#include<bits/stdc++.h>
#define mo 998244853
#define base 35000
using namespace std;
const double Pie=acos(-1);
inline long long readl()
{
    long long r(0);char in=getchar();while(in<'0'||in>'9')in=getchar();
    while(in>='0'&&in<='9')r=(r<<1)+(r<<3)+(in^48),in=getchar();return r;
}
inline int read()
{
    int r(0);char in=getchar();while(in<'0'||in>'9')in=getchar();
    while(in>='0'&&in<='9')r=(r<<1)+(r<<3)+(in^48),in=getchar();return r;
}
inline int MO(int x){return x<mo?x:x-mo;}
inline int poww(int x,int y)
{
    int r(1);
    while(y)
    {
        if(y&1)r=1ll*r*x%mo;
        x=1ll*x*x%mo;
        y>>=1;
    }
    return r;
}
struct num:public pair<double,double>
{
    num(int x):pair<double,double>(x/base,x%base){}
    num(double x,double y):pair<double,double>(x,y){}
    int tnt(){return (int)round(first*base+second);}
    friend num operator ~(const num &x){return num(x.first,-x.second);}
    friend num operator /(const num &x,const double &y){return num(x.first/y,x.second/y);}
    friend num operator +(const num &x,const num &y){return num(x.first+y.first,x.second+y.second);}
    friend num operator -(const num &x,const num &y){return num(x.first-y.first,x.second-y.second);}
    friend num operator *(const num &x,const num &y){return num(x.first*y.first-x.second*y.second,x.first*y.second+x.second*y.first);}
    friend pair<num,num> gt(const num &a0,const num &a1,const num &b0,const num &b1)
    {
        num x0=(a0+a1)/2,x1=(a1-a0)/2*num(0,1),y0=(b0+b1)/2,y1=(b1-b0)/2*num(0,1);
        return pair<num,num>(x0*y0+x1*y1*num(0,1),x0*y1+x1*y0*num(0,1));
    }
    friend num gt(const num &x,const num &y)
    {
        int nw=MO(1ll*MO((1ll*MO((long long)round(x.first)%mo+mo)*base%mo)+MO((long long)round(y.first+y.second)%mo+mo))*base%mo+MO((long long)round(x.second)%mo+mo));return num(nw/base,nw%base);
    }
};
struct Poly:public vector<num>
{
    int len;static vector<int>pos;
    Poly(){}
    Poly(const vector<num>&x){assign(x.begin(),x.end());len=x.size()-1;}
    void dft(int len)
    {
        resize(len,num(0,0));for(int i=0;i<len;i++)if(pos[i]<i)std::swap(at(pos[i]),at(i));
        for(int i=2;i<=len;i<<=1)
        {
            int leng=i>>1;
            for(int j=0;j<len;j+=i)for(int k=0;k<leng;k++)
            {
                num nw=num(cos(Pie*k/leng),sin(Pie*k/leng))*at(j+k+leng);
                at(j+k+leng)=at(j+k)-nw;at(j+k)=at(j+k)+nw;
            }
        }
    }
    void idft(int len)
    {
        for(int i=0;i<len;i++)if(pos[i]<i)std::swap(at(pos[i]),at(i));
        for(int i=2;i<=len;i<<=1)
        {
            int leng=i>>1;
            for(int j=0;j<len;j+=i)for(int k=0;k<leng;k++)
            {
                num nw=num(cos(Pie*k/leng),-sin(Pie*k/leng))*at(j+k+leng);
                at(j+k+leng)=at(j+k)-nw;at(j+k)=at(j+k)+nw;
            }
        }
        for(int i=0;i<len;i++)at(i)=at(i)/len;
    }
    friend Poly operator *(Poly x,Poly y)
    {
        int leng=x.len,len=leng+1;pos.assign(len,0);
        for(int i=0;i<len;i++)pos[i]=(pos[i>>1]>>1)|(i&1?len>>1:0);x.dft(len),y.dft(len);
        Poly p,q;p.assign(len,num(0,0));q.assign(len,num(0,0));
        tie(p.front(),q.front())=gt(x.front(),~x.front(),y.front(),~y.front());
        for(int i=1;i<len;i++)tie(p[i],q[i])=gt(x[i],~x[len-i],y[i],~y[len-i]);
        x.assign(leng+1,num(0,0));y.clear();x.len=leng;p.idft(len);q.idft(len);
        for(int i=0;i<=leng;i++)x[i]=gt(p[i],q[i]);return x;
    }
	friend Poly operator %(Poly x,int y)
	{
		if(y<x.len)x.len=y,x.resize(y+1,num(0,0));return x;
	}
	friend Poly gtnv(Poly z)
	{
		vector<int>seq;int le=z.len+1;while(le>1)seq.push_back(le-1),le=(le+1)>>1;reverse(seq.begin(),seq.end());Poly y(vector<num>(1,num(poww(z[0].tnt(),mo-2))));
		for(vector<int>::iterator ite=seq.begin();ite!=seq.end();ite++)
		{
			Poly x=z%*ite;x=x*y%*ite;x.front()=num(MO(mo+2-x.front().tnt()));for(iterator ite=x.begin()+1;ite!=x.end();ite++)*ite=num(MO(mo-ite->tnt()));y=y*x%*ite;
		}
		return y;
	}
};
vector<int>Poly::pos;
long long n;
int m;
int main()
{
	n=readl();
	m=read();
	Poly f,g;
	f.assign(m,num(0));
	g.assign(m,num(0));
	f.len=g.len=m-1;
    f[0]=g[0]=num(1);
    f[1%m]=f[1%m]+1;
    while(n)
    {
    	if(n&1)g=g*f;
    	f=f*f;
    	n>>=1;
    }
    printf("%d",g[0].tnt());
    return 0;
}
posted @ 2022-05-31 16:57  Authentic_k  阅读(22)  评论(0编辑  收藏  举报