2024.11.10 2024广西省赛
Solved:10/13
Upsolved:11/13
Penalty:920
Rank:1/169(N+1)
Dirt:53%
Problem | A | B | C | D | E | F | G | H | I | J | K | L | M | 题数 | 罚时 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Time | 123 | 3 | 47 | 11 | 18 | 105 | 26 | 155 | 5 | 197 | 10 | 920 | |||
dirt | 9 | 1 | 1 |
B,L,E,F,J:签到题
D
一看题就有种卡精度的直觉。直接写分数类,一发就过了。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
//typedef pair<ll,ll> pll;
const int N=1e5+5;
int n,x,l,r,v;
struct frac{
ll a,b;
frac(ll a,ll b):a(a),b(b){ll d=__gcd(a,b);a/=d,b/=d;}
bool operator<(const frac& p)const{
return a*p.b<b*p.a;
}
};
map<frac,vector<pii>> mp;
multiset<int> s;
int main(){
ios::sync_with_stdio(0);cin.tie(0);
cin>>n>>x;
for(int i=1;i<=n;++i){
cin>>l>>r>>v;
if(l<=x)mp[frac(x-l,v)].push_back(pii(r-l,-1));
if(r<x)mp[frac(x-r,v)].push_back(pii(r-l,1));
else if(l<=x)mp[frac(0,1)].push_back(pii(r-l,1));
}
int ans=0;
for(auto [t,e]:mp){
for(auto [x,y]:e)if(y==1)s.insert(x);
if(!s.empty())ans=max(ans,*s.begin());
for(auto [x,y]:e)if(y==-1)s.erase(s.find(x));
if(!s.empty())ans=max(ans,*s.begin());
}
cout<<ans<<'\n';
}
H
开始写了 umap 套 map,然后 T 了。后来卡了很多发(包括但不限于:双哈希用一个ll存、改手写哈希、map改vector)还是 T。
最后改成 pair sort 过了。
百万级的 umap 和手写哈希都是非常慢的!!!理论O(1)的复杂度实际上根本跑不过 sort/lower_bound 的 log。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
mt19937 rnd(time(0));
const int N=3005,M=4550005,base=2e9+5,p1=2e9+11,p2=2.1e9+11;
int n,p,m,tot=0;
ll a[N];
pll h[M];
int main(){
ios::sync_with_stdio(0);cin.tie(0);
cin>>n>>p>>m;
for(int i=1;i<=n;++i)cin>>a[i];
for(int i=1;i<=n;++i){
ll hs=0,h1=0,h2=0;
for(int j=i;j;--j){
hs=(hs*p+a[j])%m;
h1=(h1*base+a[j]+1)%p1;
h2=(h2*base+a[j]+1)%p2;
h[++tot]=pll(hs,h1<<31|h2);
}
}
sort(h+1,h+tot+1);
ll ans=0;
int i=1;
while(i<=tot){
int j=i,k=i;
ll sum=0;
while(j<=tot&&h[j].first==h[i].first){
while(k<=tot&&h[k].second==h[j].second)++k;
sum+=1ll*(k-j)*(k-j);
j=k;
}
ans+=1ll*(j-i)*(j-i)-sum;
i=j;
}
cout<<ans<<'\n';
}
A
推式子题。朴素dp:
\[f(n,k) = \frac{n-k+1}n + \frac 1n\sum_{j=1}^{k-1}(f(n-i,k-i)+1), f(n,1)=1
\]
然后随便找规律或者归纳一下就能得到
\[f(n,k)=1+\sum_{i=n-k+2}^n\frac 1i
\]
最后答案是
\[\frac 1n\sum_{k=1}^nf(n,k)=2-\frac 1n\sum_{i=1}^n\frac 1i
\]
当然经验丰富的选手可能看到 input 3 output 11/6 就能猜到调和级数(问号)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e7+5,mod=998244353;
int n;
ll inv[N];
int main(){
cin>>n;
inv[1]=1;
for(int i=2;i<=n;++i)inv[i]=inv[mod%i]*(mod-mod/i)%mod;
ll ans=0;
for(int i=1;i<=n;++i)ans=(ans+inv[i])%mod;
ans=((2-ans*inv[n])%mod+mod)%mod;
cout<<ans<<'\n';
}
K
动态开点线段树+线段树上二分,写过几百遍的套路了。因为空间没开够爆了一发。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+5,V=N*120,M=1e9;
int n,m,l,r,w;
ll h;
int rt,lc[V],rc[V],cnt;
ll sum[V],tag[V];
void add(int x,int l,int r,int v){
sum[x]+=1ll*(r-l+1)*v;
tag[x]+=v;
}
void pd(int x,int l,int r){
if(!lc[x])lc[x]=++cnt;
if(!rc[x])rc[x]=++cnt;
int mid=(l+r)>>1;
add(lc[x],l,mid,tag[x]),add(rc[x],mid+1,r,tag[x]);
tag[x]=0;
}
void upd(int& x,int l,int r,int L,int R,int v){
if(!x)x=++cnt;
if(l==L&&r==R){
add(x,l,r,v);
return;
}
pd(x,l,r);
int mid=(l+r)>>1;
if(R<=mid)upd(lc[x],l,mid,L,R,v);
else if(L>mid)upd(rc[x],mid+1,r,L,R,v);
else upd(lc[x],l,mid,L,mid,v),upd(rc[x],mid+1,r,mid+1,R,v);
sum[x]=sum[lc[x]]+sum[rc[x]];
}
ll qsum(int x,int l,int r,int L,int R){
if(!x)return 0;
if(l==L&&r==R)return sum[x];
pd(x,l,r);
int mid=(l+r)>>1;
if(R<=mid)return qsum(lc[x],l,mid,L,R);
if(L>mid)return qsum(rc[x],mid+1,r,L,R);
return qsum(lc[x],l,mid,L,mid)+qsum(rc[x],mid+1,r,mid+1,R);
}
int qry(int x,int l,int r,int pos,ll v){
if(l==r)return l;
pd(x,l,r);
int mid=(l+r)>>1;
if(pos<=mid){
ll s=qsum(lc[x],l,mid,pos,mid);
if(v<=s)return qry(lc[x],l,mid,pos,v);
else return qry(rc[x],mid+1,r,mid+1,v-s);
}
return qry(rc[x],mid+1,r,pos,v);
}
int main(){
ios::sync_with_stdio(0);cin.tie(0);
cin>>n>>m;
for(int i=1;i<=n;++i){
cin>>l>>r>>w;
upd(rt,1,M,l,r,w);
}
for(int i=1;i<=m;++i){
cin>>l>>h;
if(qsum(rt,1,M,l+1,M)<h)cout<<"-1\n";
else cout<<qry(rt,1,M,l+1,h)-l<<'\n';
}
}
M
贪心,能在子树里匹配的都匹配完。每个子树维护一个set存没匹配的节点的深度。启发式合并。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+5;
int n,x,y;
vector<int> e[N];
void adde(int x,int y){
e[x].push_back(y);
}
set<int> s[N];
int f[N],sz[N];
int find(int x){return x==f[x]?x:f[x]=find(f[x]);}
ll ans=0;
void mrg(int x,int y,int dep){
x=find(x),y=find(y);
if(sz[x]<sz[y])swap(x,y);
f[y]=x,sz[x]+=sz[y];
for(int d:s[y]){
if(s[x].find(d)!=s[x].end())ans+=2*(d-dep),s[x].erase(d);
else s[x].insert(d);
}
}
void dfs(int u,int f,int dep){
sz[u]=1,s[u].insert(dep);
for(int v:e[u])if(v^f){
dfs(v,u,dep+1);
mrg(u,v,dep);
}
}
int main(){
ios::sync_with_stdio(0);cin.tie(0);
cin>>n;
for(int i=1;i<n;++i){
cin>>x>>y;
adde(x,y),adde(y,x);
}
for(int i=1;i<=n;++i)f[i]=i;
dfs(1,0,0);
cout<<ans<<'\n';
}
G
答案是
\[[y^k]\prod_{i=1}^n (1+x^{a_i}y)
\]
考虑对每一项进行 FWT,最终 FWT 数组的每一位都形如
\[[y^k](1+y)^{x_{1,i}}(1-y)^{x_{-1,i}}
\]
注意 \(x_{1,i}+x_{-1,i}=n\)。对确定的 \(k\) 和所有的 \(x_{-1,i}\),上面这个式子的第 \(k\) 项可以 NTT 计算。
最后再 IFWT 回去即可。
简单来说就是先 FWT 再 IFWT,但是 FWT 的式子形式特殊可以直接写出形式化表达。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1<<17,M=1<<16,mod=998244353,inv2=(mod+1)/2;
inline int inc(int x,int y){return x+y>=mod?x+y-mod:x+y;}
inline int dec(int x,int y){return x-y<0?x-y+mod:x-y;}
inline int mul(int x,int y){return (ll)x*y%mod;}
inline int qpow(int x,int y){
int res=1;
for(;y;y>>=1)res=y&1?mul(res,x):res,x=mul(x,x);
return res;
}
inline int inv(int x){return qpow(x,mod-2);}
int re[N],w[2][N];
inline int getre(int n){
int len=1,bit=0;
while(len<n)len<<=1,++bit;
for(int i=1;i<len;++i)re[i]=(re[i>>1]>>1)|((i&1)<<(bit-1));
w[0][0]=w[1][0]=1,w[0][1]=qpow(3,(mod-1)/len),w[1][1]=inv(w[0][1]);
for(int o=0;o<2;++o)for(int i=2;i<len;++i)
w[o][i]=mul(w[o][i-1],w[o][1]);
return len;
}
inline void NTT(int* a,int n,int o=0){
for(int i=1;i<n;++i)if(i<re[i])swap(a[i],a[re[i]]);
int R;
for(int k=1;k<n;k<<=1)
for(int i=0,t=k<<1,st=n/t;i<n;i+=t)
for(int j=0,nw=0;j<k;++j,nw+=st)
R=mul(a[i+j+k],w[o][nw]),a[i+j+k]=dec(a[i+j],R),a[i+j]=inc(a[i+j],R);
if(o){
R=inv(n);
for(int i=0;i<n;++i)a[i]=mul(a[i],R);
}
}
int t0[N],t1[N],t2[N];
inline void multi(const int* a,const int* b,int* c,int n,int m){
int len=getre(n+m+1);
memset(t0,0,sizeof(int)*len),memcpy(t0,a,sizeof(int)*(n+1));
memset(t1,0,sizeof(int)*len),memcpy(t1,b,sizeof(int)*(m+1));
NTT(t0,len),NTT(t1,len);
for(int i=0;i<len;++i)t0[i]=mul(t0[i],t1[i]);
NTT(t0,len,1);
memcpy(c,t0,sizeof(int)*(n+m+1));
}
int n,k,m,len,a[N];
void FWT_xor(int* a,int m){
for(int k=1;k<len;k<<=1){
for(int i=0;i<len;i+=k<<1){
for(int j=0;j<k;++j){
int t=a[i+j+k];
a[i+j+k]=dec(a[i+j],t);
a[i+j]=inc(t,a[i+j]);
}
}
if(!~m)
for(int i=0;i<len;++i)
a[i]=mul(a[i],inv2);
}
}
ll fac[N],inf[N];
int f[N],g[N];
void init(int n,int k){
fac[0]=1;
for(int i=1;i<=n;++i)fac[i]=fac[i-1]*i%mod;
inf[1]=1;
for(int i=2;i<=n;++i)inf[i]=inf[mod%i]*(mod-mod/i)%mod;
inf[0]=1;
for(int i=1;i<=n;++i)inf[i]=inf[i-1]*inf[i]%mod;
for(int i=0,_=1;i<=n;++i){
if(i<=k)f[i]=inf[i]*inf[k-i]%mod*_%mod;
else f[i]=0;
if(i<=n-k)g[i]=inf[i]*inf[n-k-i]%mod;
else g[i]=0;
_=mod-_;
}
multi(f,g,f,k,n-k);
}
ll C(int n,int m){
if(m<0||n<m)return 0;
return fac[n]*inf[m]%mod*inf[n-m]%mod;
}
int c[M];
int main(){
ios::sync_with_stdio(0);cin.tie(0);
cin>>n>>k>>m;
init(n,k);
for(int i=1;i<=n;++i)cin>>a[i],++c[a[i]];
len=1;
while(len<m)len<<=1;
FWT_xor(c,1);
for(int i=0;i<len;++i){
int t=1ll*(n-c[i]+mod)*inv2%mod;
c[i]=f[t];
}
FWT_xor(c,-1);
for(int i=0;i<m;++i)cout<<c[i]<<' ';
}
我愿意追随你的轨迹,不远万里,不问归期