前言
- 比賽連結。
這次和高中一起打的,排名一次比一次低了,差點出前一半了……
主要是 T1 \(dijkstra\) 唐氏複雜度打假了,T2 掛分,T3 沒想出來壓位,T4 題都沒看。
T1 最短路
- 原題:luogu P2966 [USACO09DEC] Cow Toll Paths G。
本題考察對 \(Floyed\) 的理解,\(Floyed\) 陣列在沒有最佳化之前實際是有三維的,\(f_{k,i,j}\) 表示從 \(i\) 到 \(j\) 的路徑中只可能經過 \(1\sim k\) 作為中轉點時的最短路,也就是說不可能以 \(k+1\sim n\) 為中轉點,其中 \(k\) 這一維可以省略。
那麼將 \(k\) 這一維按照點權從小到大排序,於是有 \(a_k=\max\limits_{i=1}^k\{a_i\}\),所以對於 \(f_{k,i,j}\),其路徑最大點權為 \(\max(a_i,a_j,a_k)\)。
可以開兩個陣列 \(f,g\) 分別表示只計算邊權和包括點權的,於是有:
\(g_{s,t}\) 即為所求。
點選檢視程式碼
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
#define sort stable_sort
using namespace std;
const int N=310,M=1e4+10;
template<typename Tp> inline void read(Tp&x)
{
x=0;register bool z=true;
register char c=getchar();
for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0;
for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
x=(z?x:~x+1);
}
void wt(int x){if(x>9)wt(x/10);putchar((x%10)+'0');}
void write(int x){if(x<0)putchar('-'),x=~x+1;wt(x);}
int n,m,q,f[N][N],g[N][N],a[N];
struct aa {int a,id;}e[N];
bool cmp(aa a,aa b) {return a.a<b.a;}
signed main()
{
freopen("path.in","r",stdin);
freopen("path.out","w",stdout);
read(n),read(m),read(q);
for(int i=1;i<=n;i++)
read(e[i].a),e[i].id=i,a[i]=e[i].a;
memset(f,0x3f,sizeof(f));
memset(g,0x3f,sizeof(g));
for(int i=1;i<=n;i++) f[i][i]=0,g[i][i]=a[i];
for(int i=1,x,y,z;i<=m;i++)
read(x),read(y),read(z),
f[x][y]=f[y][x]=min(f[x][y],z),
g[x][y]=g[y][x]=f[x][y]+max(a[x],a[y]);
sort(e+1,e+1+n,cmp);
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
if(i!=e[k].id)
for(int j=1;j<=n;j++)
{
if(i==j||e[k].id==j) continue;
g[i][j]=min(g[i][j],f[i][e[k].id]+f[e[k].id][j]+max({e[k].a,a[i],a[j]}));
f[i][j]=min(f[i][j],f[i][e[k].id]+f[e[k].id][j]);
}
for(int i=1,s,t;i<=q;i++)
read(s),read(t),
write(g[s][t]>=0x7f7f7f7f7f?-1:g[s][t]),puts("");
}
T2
-
原題:luogu P3474 [POI2008] KUP-Plot purchase。
-
部分分 \(100pts\),唐氏做法:
這是一個捨棄部分正確性換取複雜度的做法,在隨機的大資料下幾乎不可能卡掉,小資料下極容易死,但是小資料可以暴力衝過去。
對於大資料,現在預處理時符合答案的直接結束,以增加部分正確性。
只考慮正方形的答案,之後二分邊長即可。
複雜度 \(O(n^2\log(n))\)。
考慮到學校 OJ 資料向來比較水且沒有捆綁,可以透過此題。
然後賽時加了點最佳化想增加點正確性,結果掛了 \(60pts\)。點選檢視程式碼
#include<bits/stdc++.h> #define int long long #define endl '\n' #define sort stable_sort using namespace std; const int N=2010; template<typename Tp> inline void read(Tp&x) { x=0;register bool z=true; register char c=getchar(); for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0; for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48); x=(z?x:~x+1); } void wt(int x){if(x>9)wt(x/10);putchar((x%10)+'0');} void write(int x){if(x<0)putchar('-'),x=~x+1;wt(x);} int n,k,a[N][N],sum[N][N]; bool check(int mid) { int x1=1,y1=1,x2=mid,y2=mid,minn=0x7f7f7f7f,nowx=0,nowy=0; while(x2<=n&&y2<=n) { int ans=sum[x2][y2]-sum[x2][y1-1]-sum[x1-1][y2]+sum[x1-1][y1-1]; if(ans>=k&&ans<=2*k) { cout<<x1<<' '<<y1<<' '<<x2<<' '<<y2<<endl; exit(0); } minn=min(minn,ans); if(y2==n) { x2++,y2=mid; x1=x2-mid+1,y1=y2-mid+1; nowy=0; } else { y2++; x1=x2-mid+1,y1=y2-mid+1; } } return minn<k; } signed main() { freopen("matrix.in","r",stdin); freopen("matrix.out","w",stdout); read(n),read(k); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) { read(a[i][j]); if(a[i][j]>=k&&a[i][j]<=2*k) { cout<<i<<' '<<j<<' '<<i<<' '<<j<<endl; return 0; } } for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) { sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+a[i][j]; if(sum[i][j]>=k&&sum[i][j]<=2*k) { cout<<1<<' '<<1<<' '<<i<<' '<<j<<endl; return 0; } } if(n<=200) { for(int x2=1;x2<=n;x2++) for(int y2=1;y2<=n;y2++) for(int x1=1;x1<=x2;x1++) for(int y1=1;y1<=y2;y1++) { int ans=sum[x2][y2]-sum[x2][y1-1]-sum[x1-1][y2]+sum[x1-1][y1-1]; if(ans>=k&&ans<=2*k) { cout<<x1<<' '<<y1<<' '<<x2<<' '<<y2<<endl; return 0; } } puts("-1"); return 0; } int l=1,r=n; while(l<=r) { int mid=(l+r)>>1; if(check(mid)) l=mid+1; else r=mid-1; } puts("-1"); }
-
正解:
暫無。
T3 陣列
-
原題:CF1114F Please, another Queries on Array?。
-
部分分 \(20pts\):暴力。
-
部分分 \(10pts\):對於沒有修改的操作,主席樹,詳見 BZOJ4026 dC Loves Number Theory。
-
正解:
和主席樹的思想一樣,根據公式 \(\varphi(n)=n\prod\limits_{p\in\mathbb{p},p|n}\frac{p-1}{p}\),只需要處理出區間 \([l,r]\) 的乘積和其所包含的質因數即可。
根據 \(a_i,x\le 300\),所以只可能有 \(62\) 個質因數,考慮壓成一個 \(01\) 串,類似狀壓處理。
讓後賽時雖然看到了 \(300\) 的資料範圍但是想他乘上好幾次數值就極大了,但是沒有想到他乘多少次質因數也是 \(\le 300\) 的,於是沒有想到狀壓,就很唐。
需要略微卡常,提前處理出每個 \(c_i=\dfrac{prime_i-1}{prime_i}\),不然在本就常數巨大的線段樹上再在每次輸出再加一個 \(\log(1e9)\) 簡直是雪上加霜。
複雜度理論上 \(O(n\log(n))\)。
點選檢視程式碼
#include<bits/stdc++.h> #define ll long long #define endl '\n' #define sort stable_sort #define f t[p] #define ls p<<1 #define rs p<<1|1 using namespace std; const int N=1e5+10,P=1e9+7; template<typename Tp> inline void read(Tp&x) { x=0;register bool z=true; register char c=getchar(); for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0; for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48); x=(z?x:~x+1); } void wt(int x){if(x>9)wt(x/10);putchar((x%10)+'0');} void write(int x){if(x<0)putchar('-'),x=~x+1;wt(x);} int n,m,a[N],tot,prime[N],c[N]; bool vis[N]; void pre() { vis[1]=1; for(int i=2;i<=300;i++) if(vis[i]==0) { prime[++tot]=i; for(int j=2;j<=300/i;j++) vis[i*j]=1; } } ll qpow(ll a,ll b) { ll ans=1; for(;b;b>>=1) { if(b&1) (ans*=a)%=P; (a*=a)%=P; } return ans; } struct aa { int l,r; ll val,add=1,pri,lazy; }t[N<<2]; void pushup(int p) { f.val=t[ls].val*t[rs].val%P; f.pri=t[ls].pri|t[rs].pri; } void build(int p,int l,int r) { f.l=l,f.r=r; if(l==r) { f.val=a[l]; for(int i=1;i<=tot&&prime[i]<=a[l];i++) if(a[l]%prime[i]==0) f.pri|=(1ll<<(i-1)); return ; } int mid=(l+r)>>1; build(ls,l,mid),build(rs,mid+1,r); pushup(p); } void spread(int p) { if(f.add==1&&f.lazy==0) return ; ll &x=f.add,&y=f.lazy; (t[ls].val*=qpow(x,t[ls].r-t[ls].l+1))%=P,(t[ls].add*=x)%=P; (t[rs].val*=qpow(x,t[rs].r-t[rs].l+1))%=P,(t[rs].add*=x)%=P; t[ls].pri|=y,t[ls].lazy|=y; t[rs].pri|=y,t[rs].lazy|=y; x=1,y=0; } void change(int p,int l,int r,ll d) { if(l<=f.l&&r>=f.r) { (f.val*=qpow(d,f.r-f.l+1))%=P; (f.add*=d)%=P; ll dis=0; for(int i=1;i<=tot&&prime[i]<=d;i++) if(d%prime[i]==0) dis|=(1ll<<(i-1)); f.pri|=dis; f.lazy|=dis; return ; } spread(p); int mid=(f.l+f.r)>>1; if(l<=mid) change(ls,l,r,d); if(r>mid) change(rs,l,r,d); pushup(p); } aa ask(int p,int l,int r) { if(l<=f.l&&r>=f.r) return f; spread(p); int mid=(f.l+f.r)>>1; aa ans; ans.val=1,ans.pri=0; if(l<=mid) { aa x=ask(ls,l,r); (ans.val*=x.val)%=P; ans.pri|=x.pri; } if(r>mid) { aa x=ask(rs,l,r); (ans.val*=x.val)%=P; ans.pri|=x.pri; } return ans; } signed main() { freopen("array.in","r",stdin); freopen("array.out","w",stdout); pre(); for(int i=1;i<=tot;i++) c[i]=(prime[i]-1)*(qpow(prime[i],P-2))%P; read(n),read(m); for(int i=1;i<=n;i++) read(a[i]); build(1,1,n); for(int i=1,op,l,r,d;i<=m;i++) { read(op),read(l),read(r); if(op==1) read(d),change(1,l,r,d); else { aa ans=ask(1,l,r); ll val=ans.val; ll sta=ans.pri; for(int j=1;j<=tot;j++) if((sta>>(j-1))&1) val=val*c[j]%P; write(val),puts(""); } } }
T4 樹
- 原題:luogu P3591 [POI2015] ODW。
考慮將 \(c_i\) 二進位制拆分向上跳,複雜度為 \(\sum\limits_{i=1}^{n-1}\dfrac{n}{c_i}\times \log(c_i)\),當 \(c_i\ge \sqrt n\) 時複雜度可以接受,所以考慮根號分治,對於 \(c_i<\sqrt n\) 的特殊處理。
對於 \(c_i<\sqrt n\) 的考慮一共只有 \(\sqrt n\) 種步長,類似於樹上字首和的方法,\(sum_{c,x}\) 表示從 \(x\) 向上跳到根節點的貢獻,\(sum_{c,y}\) 同理,考慮怎麼減去 \(lca\) 的貢獻。
從 \(x\) 跳到 \(lca\) 需要 \(\lceil\dfrac{dep_x-dep_{lca}}{c_i}\rceil\) 步,設 \(r_x=(dep_x-dep_{lca})\bmod c_i\),則第一個多餘的貢獻產生在 \(lca\) 的 \(c_i-r_x\) 級祖先處,\(r_y\) 同理;注意當 \(r_x=0\) 時特判,直接將去 \(2\times sum_{c,lca}\) 即可。
對於每個 \(c_i\) 跑一次 \(dfs\) 求字首和,利用二進位制拆分求出 \(x\) 的 \(k\) 級祖先。
總複雜度為 \(O(n\log(n)\sqrt n)\)。
點選檢視程式碼
#include<bits/stdc++.h>
// #define int long long
#define endl '\n'
#define sort stable_sort
using namespace std;
const int N=5e4+10,M=20;
template<typename Tp> inline void read(Tp&x)
{
x=0;register bool z=true;
register char c=getchar();
for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0;
for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
x=(z?x:~x+1);
}
void wt(int x){if(x>9)wt(x/10);putchar((x%10)+'0');}
void write(int x){if(x<0)putchar('-'),x=~x+1;wt(x);}
int n,a[N],b[N],c[N],dis[250][N];
vector<int>e[N];
int fa[N][M],son[N],sz[N],top[N],dep[N];
void dfs1(int x,int father)
{
fa[x][0]=father,dep[x]=dep[father]+1,sz[x]=1;
for(int y:e[x])
if(y!=father)
dfs1(y,x),
sz[x]+=sz[y],
son[x]=sz[y]>sz[son[x]]?y:son[x];
}
void dfs2(int x,int t)
{
top[x]=t;
if(!son[x]) return ;
dfs2(son[x],t);
for(int y:e[x])
if(y!=fa[x][0]&&y!=son[x]) dfs2(y,y);
}
void init()
{
for(int j=1;j<=log2(n);j++)
for(int i=1;i<=n;i++)
fa[i][j]=fa[fa[i][j-1]][j-1];
}
int Lca(int x,int y)
{
for(;top[x]!=top[y];x=fa[top[x]][0])
if(dep[top[x]]<dep[top[y]]) swap(x,y);
return dep[x]<dep[y]?x:y;
}
int Fa(int x,int k)
{
int ans=x;
for(int i=log2(k);i>=0;i--)
if(dep[x]-dep[fa[ans][i]]<=k) ans=fa[ans][i];
return ans;
}
void dfs(int x,int c)
{
if(dep[x]>c) dis[c][x]=dis[c][Fa(x,c)]+a[x];
else dis[c][x]=a[x];
for(int y:e[x])
if(y!=fa[x][0]) dfs(y,c);
}
signed main()
{
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
read(n);
for(int i=1;i<=n;i++) read(a[i]);
for(int i=1,x,y;i<=n-1;i++)
read(x),read(y),
e[x].push_back(y),
e[y].push_back(x);
dfs1(1,0),dfs2(1,1);
init();
for(int i=1;i<=n;i++) read(b[i]);
for(int i=1;i<=n-1;i++) read(c[i]);
for(int i=1;i<=n-1;i++)
if(c[i]<=sqrt(n))
if(!dis[c[i]][1]) dfs(1,c[i]);
for(int i=1;i<=n-1;i++)
{
int x=b[i],y=b[i+1],lca=Lca(b[i],b[i+1]),ans=0;
if(c[i]<=sqrt(n))
{
ans=dis[c[i]][x]+dis[c[i]][y];
int r1=(dep[x]-dep[lca])%c[i],
r2=(dep[y]-dep[lca])%c[i];
if(!r1) ans=ans-dis[c[i]][lca]*2+a[lca];
else ans=ans-dis[c[i]][Fa(lca,c[i]-r1)]-dis[c[i]][Fa(lca,c[i]-r2)];
}
else
{
for(;dep[x]>=dep[lca];x=Fa(x,c[i])) ans+=a[x];
for(;dep[y]>dep[lca];y=Fa(y,c[i])) ans+=a[y];
}
write(ans),puts("");
}
}