冲刺国赛8.10

T1

先整个 \(dp\)

\(g_i\) 表示长度为 \(i\) 的方案数

那么转移可以每次枚举一个下降的段加入

\(g_n=\sum\limits_{i=1}^k\binom{k}{i}g_{n-i}f_i\)

其中 \(f_i\) 表示加入一条长度为 \(i\) 的下降的段的容斥系数

\(f_i=-\sum\limits_{j=i-m}^{i-1}f_j\)

发现当 \(i \equiv 0 \text{ mod }m+1\) 时,\(f_i=-1\) ,当 \(i \equiv 1 \text{ mod }m+1\) 时,\(f_i=1\) ,其他情况都为 \(0\)

然后就可以用线性递推做了

Code
#include<bits/stdc++.h>
#define int long long
#define mod 998244353
#define inf 0x3f3f3f3f3f3f3f3f
#define meow(args...) fprintf(stderr,args)
using namespace std;
inline int read(){
	int 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^48);ch=getchar();}
	return x*f;
}
int n,m,k,iv;
int p[200010],q[200010],f[200010];
int fac[200010],inv[200010];
int r[200010],g[2][200010],len,L;
inline void md(int &x){x=(x>=mod)?x-mod:x;}
inline int C(int n,int m){return fac[n]*inv[m]%mod*inv[n-m]%mod;}
inline int qpow(int x,int k){
	int res=1,base=x;
	while(k){if(k&1) res=res*base%mod;base=base*base%mod;k>>=1;}
	return res;
}
inline void ntt(int a[],int tx){
	for(int i=0;i<len;i++) if(i<r[i]) swap(a[i],a[r[i]]);
	for(int d=1,t=len>>1;d<len;d<<=1,t>>=1) for(int i=0;i<len;i+=d<<1) for(int j=0;j<d;j++){
		int tmp=g[tx][t*j]*a[i+j+d]%mod;
		md(a[i+j+d]=a[i+j]-tmp+mod);
		md(a[i+j]=a[i+j]+tmp);
	}
}
signed main(){
#ifdef LOCAL
	freopen("in","r",stdin);
	freopen("out","w",stdout);
#endif
	freopen("gradethree.in","r",stdin);
	freopen("gradethree.out","w",stdout);
	for(int i=fac[0]=inv[0]=1;i<=50000;i++) fac[i]=fac[i-1]*i%mod,inv[i]=inv[i-1]*qpow(i,mod-2)%mod;
	n=read(),m=read()+1,k=read();n=n+k-1;
	for(int i=0;i<=k;i++) if(i%m==0) q[i]=mod-C(k,i);else if(i%m==1) q[i]=C(k,i);
	
	for(len=1,L=0;len<=k+k;len<<=1,L++);
	for(int i=0;i<len;i++) r[i]=(r[i>>1]>>1)|((i&1)<<(L-1));iv=qpow(len,mod-2);
	g[0][0]=1;g[0][1]=qpow(3,(mod-1)/len);for(int i=2;i<len;i++) g[0][i]=g[0][i-1]*g[0][1]%mod;
	g[1][0]=1,g[1][1]=qpow(g[0][1],mod-2);for(int i=2;i<len;i++) g[1][i]=g[1][i-1]*g[1][1]%mod;
	
	p[k-1]=1;
	
	for(;n;n>>=1){
		for(int i=0;i<=k;i++) f[i]=(i&1)?(mod-q[i]):q[i];
		memset(f+k+1,0,sizeof(int)*(len-k-1));
		ntt(f,0),ntt(p,0),ntt(q,0);
		for(int i=0;i<len;i++) p[i]=p[i]*f[i]%mod,q[i]=q[i]*f[i]%mod;
		ntt(p,1),ntt(q,1);
		for(int i=0;i<=k;i++) q[i]=q[i<<1];
		for(int i=0;i<=k;i++) p[i]=p[i<<1|(n&1)];
		memset(p+k+1,0,sizeof(int)*(len-k-1));
		memset(q+k+1,0,sizeof(int)*(len-k-1));
	}
	
	printf("%lld\n",mod-p[0]*qpow(q[0],mod-2)%mod);
	return 0;
}

T2

按颜色种类根号分治,大于根号的暴力单独做

小于根号的一起做,暴力挪动

然后再用双指针找到匹配的区间

Code
#include<bits/stdc++.h>
// #define int long long
#define inf 0x3f3f3f3f3f3f3f3f
#define meow(args...) fprintf(stderr,args)
using namespace std;
inline int read(){
	int 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^48);ch=getchar();}
	return x*f;
}
bool mem1;
int n,m,q,B;
int a[200010],b[200010];
int sum[200010],ans[200010];
int bl[200010],br[200010];
struct Q{int l,r,t;}L[200010];
pair<int,int> vl[200010],vr[200010];
vector<int>vec[200010],v1[450],v2;
vector<pair<int,int>>s[450];
bool mem2;
signed main(){
#ifdef LOCAL
	freopen("in","r",stdin);
	freopen("out","w",stdout);
#endif
	freopen("escape.in","r",stdin);
	freopen("escape.out","w",stdout);
	// cerr<<1.0*(&mem1-&mem2)/(1024*1024)<<endl;exit(0);
	n=read(),m=read(),q=read();B=sqrt(n);
	for(int i=1;i<=n;i++) a[i]=read();
	for(int i=1;i<=n;i++) b[i]=read();
	for(int i=1;i<=n;i++) vec[b[i]].emplace_back(i);
	for(int i=1;i<=m;i++) if(vec[i].size()) if(vec[i].size()<=B) v1[vec[i].size()].emplace_back(i);else v2.emplace_back(i);
	
	for(int i=1;i<=q;i++) L[i].t=read(),L[i].l=read(),L[i].r=read();
	for(int i=1;i<=q;i++) vl[i].first=L[i].l,vl[i].second=i;sort(vl+1,vl+1+q);
	for(int i=1;i<=q;i++) vr[i].first=L[i].r,vr[i].second=i;sort(vr+1,vr+1+q);
	
	for(int c=1;c<=B;c++) if(v1[c].size()){
		for(int i=0;i<c;i++) s[i].clear();
		for(int i=0;i<c;i++) for(auto v:v1[c]) for(int j=0;j<c;j++) s[i].emplace_back(vec[v][j],a[vec[v][(j-i+c)%c]]);
		for(int i=0;i<c;i++) sort(s[i].begin(),s[i].end());
		for(int i=0;i<c;i++) for(int j=1,edj=s[i].size();j<edj;j++) s[i][j].second+=s[i][j-1].second;
		
		for(int i=1,p=0;i<=q;i++){while(p<s[0].size()&&s[0][p].first<vl[i].first) p++;bl[vl[i].second]=p;}
		for(int i=1,p=0;i<=q;i++){while(p<s[0].size()&&s[0][p].first<=vr[i].first) p++;br[vr[i].second]=p-1;}
		
		for(int i=1,l,r,v;i<=q;i++){
			v=L[i].t%c;
			l=bl[i],r=br[i];
			ans[i]+=s[v][r].second-(l?s[v][l-1].second:0);
		}
	}
	
	for(auto c:v2){
		for(int i=0;i<vec[c].size();i++) sum[i]=a[vec[c][i]];
		for(int i=1;i<vec[c].size();i++) sum[i]+=sum[i-1];
		
		for(int i=1,p=0;i<=q;i++){while(p<vec[c].size()&&vec[c][p]<vl[i].first) p++;bl[vl[i].second]=p;}
		for(int i=1,p=0;i<=q;i++){while(p<vec[c].size()&&vec[c][p]<=vr[i].first) p++;br[vr[i].second]=p-1;}
		
		for(int i=1,l,r,v;i<=q;i++){
			l=bl[i],r=br[i];
			if(l>r) continue;
			v=L[i].t%vec[c].size();
			l=(l-v+vec[c].size())%vec[c].size();
			r=(r-v+vec[c].size())%vec[c].size();
			if(l<=r) ans[i]+=sum[r]-(l?sum[l-1]:0);else ans[i]+=sum[vec[c].size()-1]-sum[l-1]+sum[r];
		}
	}

	for(int i=1;i<=q;i++) printf("%d\n",ans[i]);
	return 0;
}

T3

题面写得太离谱,啥也没看懂,写个样例解释会死?

如果能看见线段的两个端点就能看见整个线段

能看见两个端点的条件是观察点在线段所处直线的上方

那么能看见一个区间的所有线段,就必须在所有直线的上方

于是对每个区间做一个下凸包,把询问离线下来按 \(x\) 排序,再维护一个指针来取答案

然后再从观察点分别向左右二分

Code
#include<bits/stdc++.h>
#define int long long
#define lson rt<<1
#define rson rt<<1|1
#define inf 0x3f3f3f3f3f3f3f3f
#define meow(args...) fprintf(stderr,args)
using namespace std;
inline int read(){
	int 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^48);ch=getchar();}
	return x*f;
}
int n,q,tot,x,y;
int l[400010],r[400010];
int ansl[400010],ansr[400010];
struct node{
	int k,b;
	inline int calc(int x){return k*x+b;}
	inline bool operator<(const node &v)const{return k==v.k?b<v.b:k<v.k;}
};
struct Q{
	int x,y,id;
	inline bool operator<(const Q &b)const{return x<b.x;}
}L[400010];
struct seg{int p;vector<node>v;}st[400010*4];
inline bool jud(node a,node b,node c){
	return (__int128)(c.b-a.b)*(a.k-b.k)<=(__int128)(b.b-a.b)*(a.k-c.k);
}
void build(vector<node>&a){
	sort(a.begin(),a.end());vector<node>tmp;
	for(int i=0;i<a.size();i++){
		if(i+1<a.size()&&a[i].k==a[i+1].k) continue;
		while(tmp.size()>1&&jud(tmp[tmp.size()-2],tmp[tmp.size()-1],a[i])) tmp.pop_back();
		tmp.emplace_back(a[i]);
	}
	a=tmp;
}
void upd(int rt,int l,int r,int pos,node k){
	st[rt].v.emplace_back(k);
	if(st[rt].v.size()==(r-l+1)) build(st[rt].v);
	if(l==r) return ;
	int mid=(l+r)>>1;
	if(pos<=mid) upd(lson,l,mid,pos,k);
	else upd(rson,mid+1,r,pos,k);
}
int query(int rt,int l,int r,int L,int R){
	if(L<=l&&r<=R){
		while(st[rt].p<st[rt].v.size()-1&&st[rt].v[st[rt].p+1].calc(x)>=st[rt].v[st[rt].p].calc(x)) st[rt].p++;
		return st[rt].v[st[rt].p].calc(x);
	}
	int mid=(l+r)>>1,res=-inf;
	if(L<=mid) res=max(res,query(lson,l,mid,L,R));
	if(R>mid) res=max(res,query(rson,mid+1,r,L,R));
	return res;
}
signed main(){
#ifdef LOCAL
	freopen("in","r",stdin);
	freopen("out","w",stdout);
#endif
	freopen("play.in","r",stdin);
	freopen("play.out","w",stdout);
	n=read(),q=read();
	for(int i=1,L,k,b,x=0,y=0;i<=n;i++){
		l[i]=x,r[i]=x+(L=read());k=read();b=y-k*x;
		upd(1,1,n,i,(node){k,b});
		x=r[i],y=k*x+b;
	}
	for(int i=1;i<=q;i++) L[i].x=read(),L[i].y=read(),L[i].id=i;sort(L+1,L+1+q);
	
	for(int i=1,l,r,pos,res;i<=q;i++){
		x=L[i].x,y=L[i].y;pos=upper_bound(::l+1,::l+1+n,L[i].x)-::l-1;if(pos>n) pos=n;
		l=1,r=pos,res=pos;
		while(l<=r){
			int mid=(l+r)>>1;
			if(query(1,1,n,mid,pos)<=y) res=mid,r=mid-1;else l=mid+1;
		}
		ansl[L[i].id]=::l[res];
		l=pos,r=n,res=pos;
		while(l<=r){
			int mid=(l+r)>>1;
			if(query(1,1,n,pos,mid)<=y) res=mid,l=mid+1;else r=mid-1;
		}
		ansr[L[i].id]=::r[res];
	}
	
	for(int i=1;i<=q;i++) printf("%lld %lld\n",ansl[i],ansr[i]);
	return 0;
}
posted @ 2022-08-10 19:11  Max_QAQ  阅读(83)  评论(0编辑  收藏  举报