冲刺国赛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;
}