前言:
GJ Round 為學校內部模擬賽
記 7.13 為 Round 1,在此之前的模擬賽較為混亂以後再說(可能記為 Round 0 或者負數)
目前正在倒序更新
https://www.luogu.com.cn/article/a30ffmdb
Round 20 (9.18)
\(\mathcal A\)
簡單數學題
考慮將每一個 \(a_i\) 分別拆開計算貢獻計算對應的 \(a_i=i\) 時所產生的貢獻即可
答案即為 \(ans=\sum_{i=1}^{n} ({i-1 \choose a_i-1} \times 2^{n-i}) \pmod {10^9+7}\)
#include<bits/stdc++.h>
#define int long long
#define lowbit(x) (x&-x)
using namespace std;
const int N=2e5+5,mod=1e9+7;
int n,a[N],fac[N],inv[N],pw2[N];
int fpm(int a,int b)
{
int res=1;
while(b)
{
if(b&1) res=res*a%mod;
a=a*a%mod;
b>>=1;
}
return res;
}
int C(int n,int k)
{
return (k>n||n<0||k<0)?0:fac[n]*inv[n-k]%mod*inv[k]%mod;
}
void init()
{
fac[0]=pw2[0]=1;
for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i%mod,pw2[i]=pw2[i-1]*2%mod;
inv[n]=fpm(fac[n],mod-2);
for(int i=n;i;i--) inv[i-1]=inv[i]*i%mod;
}
signed main()
{
// freopen("ex_seq.in","r",stdin);
// freopen("ex_seq.out","w",stdout);
scanf("%lld",&n);
init();
for(int i=1;i<=n;i++) scanf("%lld",a+i);
sort(a+1,a+1+n);
int ans=0;
for(int i=1;i<=n;i++) ans=(ans+C(i-1,a[i]-1)*pw2[n-i]%mod)%mod;
printf("%lld",ans);
return 0;
}
\(\mathcal B\)
按順序從 \(1\) 到 \(n\) 拿盤子,對於每個盤子,你可以選擇不要,也可以拿走,但拿走盤子需要滿足下面三個條件至少一個:
之前沒有拿過盤子;
這個盤子的尺寸比之前拿走的盤子的都大,這樣你就可以把之前買的盤子疊在它的上面;
這個盤子的尺寸比之前拿走的盤子的都小,這樣你就可以把它疊在之前買的盤子的上面。
最後,你想知道,你最多能拿走多少個盤子?
從結尾開始做最長上升和最長下降子序列,二分或線段樹最佳化 dp 即可
#include<bits/stdc++.h>
#define ls u<<1
#define rs u<<1|1
using namespace std;
const int N=1e5+5;
int n,f[N],g[N],a[N],b[N];
struct SGT
{
int t[N<<2];
void push_up(int u)
{
t[u]=max(t[ls],t[rs]);
}
void update(int u,int l,int r,int x,int k)
{
if(x<l||r<x) return;
if(x==l&&r==x)
{
t[u]=k;
return;
}
int mid=(l+r)>>1;
if(x<=mid) update(ls,l,mid,x,k);
else update(rs,mid+1,r,x,k);
push_up(u);
}
int query(int u,int l,int r,int x,int y)
{
if(y<l||r<x) return 0;
if(x<=l&&r<=y) return t[u];
int mid=(l+r)>>1,res=0;
if(x<=mid) res=max(res,query(ls,l,mid,x,y));
if(y>mid) res=max(res,query(rs,mid+1,r,x,y));
return res;
}
}T1,T2;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",a+i),b[i]=a[i];
sort(b+1,b+1+n);
int len=unique(b+1,b+1+n)-b-1;
for(int i=n;i;i--)
{
a[i]=lower_bound(b+1,b+1+len,a[i])-b;
int x=(1<=a[i]-1?T1.query(1,1,n,1,a[i]-1):0);
int y=(a[i]+1<=n?T2.query(1,1,n,a[i]+1,n):0);
f[i]=x+1,g[i]=y+1;
T1.update(1,1,n,a[i],f[i]);
T2.update(1,1,n,a[i],g[i]);
}
int ans=0;
for(int i=1;i<=n;i++) ans=max(ans,f[i]+g[i]-1);
printf("%d",ans);
return 0;
}
時間複雜度 \(\mathcal O(n \log n)\)
\(\mathcal C\)
CF875F Royal Questions
考慮將平面內的每一行每一列連邊
求最大權值基環森林
跟最大生成樹差不多,用 Kruskal 跑再稍微改一下就差不多了
時間複雜度 \(O(nm (\log n+\log m))\)
\(\mathcal D\)
天道酬勤,你已經精通了 OI。
你認為 OI 的學習可以分為 \(n\) 個階段,在經歷第 \(i\) 個階段時,如果自身能力值大於 \(a_i\),那麼就可以得到 \(b_i\) 的進步,也就是能力值累加上 \(b_i\)。
並不是每個 Oler 都會完整的走完 \(n\) 個階段,你觀察了 \(q\) 個 Oler,第 \(i\) 個 Oler 會帶著 \(x_i\) 的初始能力值依次經歷第 \(l_i,l_{i+1},\dots,r_i\) 個階段。
他們都還在路上,而你想知道他們最終會變得多強,也就是經歷完這些階段後的能力值。
由於某些原因,有時候你急切的想知道答案。
資料結構(DS)題
咕咕咕
Round 21 (9.19)
\(\mathcal A\)
SB 題,還什麼困難卷積
不同的 \(a_i,b_i\) 只有 \(\mathcal O(\sqrt{sum})\) 個,排序去重模擬即可
#pragma GCC optimize(3,"Ofast","inline")
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=3e6+5;
int n,a[N],b[N],f[N],g[N];
signed main()
{
scanf("%lld",&n);
for(int i=1;i<=n;i++) scanf("%lld",a+i),f[a[i]]++;
for(int i=1;i<=n;i++) scanf("%lld",b+i),g[b[i]]++;
sort(a+1,a+1+n),sort(b+1,b+1+n);
int A=unique(a+1,a+1+n)-a-1,B=unique(b+1,b+1+n)-b-1;
int ans=0;
for(int i=1;i<=A;i++)
for(int j=1;j<=B;j++)
ans+=(int)sqrt(abs(a[i]-b[j]))*f[a[i]]*g[b[j]];
printf("%lld",ans);
return 0;
}
/*
4
1 2 3 4
2 3 3 3
12
*/
\(\mathcal B\)
CF623C Electric Charges
二分答案,預處理前字尾最大最小值,分別對於 \(x\) 和 \(y\) 得到極長段來求答案
\(\mathcal C\)
數論題,不會做
\(a_i\) 是楊輝三角中間對稱軸的那一列
咕咕咕
\(\mathcal D\)
紅茶國有 \(m\) 個部落,為了爭奪 \(n\) 個有靈氣的礦洞裡的資源,部落之間經常發生衝突。礦洞被標號為 \(1\) 到 \(n\),每個礦洞初始都被至多一個部落所佔領。平時的礦洞裡沒有任何有價值的資源,珍貴之物只有在特定的時候才會出現,具體地,依次會有 \(q\) 次事件發生,每次事件形如:
1 l r x
:\(x\) 號部落發起戰爭,佔領了編號為 \(l\) 到 \(r\) 的礦洞,原先佔有這些礦洞的部落將會失去它們,轉而由 \(x\) 號部落來佔領;
2 l r x
:編號為 \(l\) 到 \(r\) 的礦洞靈氣爆發,都出現了價值為 \(x\) 的寶物。一旦一個部落佔領的礦洞裡有寶物,寶物會立即被全部取走。為了知道哪些部落能成為王,你需要求出 \(q\) 次事件發生之後,每個部落分別得到的寶物的價值總和。
資料結構(DS)題
賽時著真做結果因為複雜度寫假了T飛了
聽說正著寫能用 set
維護顏色段,但筆者不會
由於沒有強制線上,可以考慮時光倒流,這樣就不用考慮正著做部落具有佔領權這一問題,那就基本上是區間加、區間覆蓋、區間查詢的模板線段樹題
最後再把一開始部落佔有的礦洞再 query
\(m\) 次就好了
#pragma GCC optimize(3,"Ofast","inline")
#include<bits/stdc++.h>
#define int long long
#define ls u<<1
#define rs u<<1|1
using namespace std;
const int N=5e5+5;
int a[N],ans[N];
int n,m,Q;
int t[N<<2],tag[N<<2],cov[N<<2];
void push_up(int u)
{
t[u]=t[ls]+t[rs];
}
void build(int u,int l,int r)
{
cov[u]=-1;
if(l==r) return;
int mid=(l+r)>>1;
build(ls,l,mid);
build(rs,mid+1,r);
push_up(u);
}
void push_down(int u,int l,int r)
{
if(~cov[u])
{
cov[ls]=cov[rs]=cov[u];
tag[ls]=tag[rs]=0;
t[ls]=t[rs]=cov[u];
cov[u]=-1;
}
if(!tag[u]) return;
tag[ls]+=tag[u],tag[rs]+=tag[u];
int mid=(l+r)>>1;
t[ls]+=tag[u]*(mid-l+1);
t[rs]+=tag[u]*(r-mid);
tag[u]=0;
}
void update(int u,int l,int r,int x,int y,int k)
{
if(y<l||r<x) return;
if(x<=l&&r<=y)
{
t[u]+=(r-l+1)*k;
tag[u]+=k;
return;
}
push_down(u,l,r);
int mid=(l+r)>>1;
if(x<=mid) update(ls,l,mid,x,y,k);
if(y>mid) update(rs,mid+1,r,x,y,k);
push_up(u);
}
void modify(int u,int l,int r,int x,int y,int k)
{
if(y<l||r<x) return;
if(x<=l&&r<=y)
{
t[u]=cov[u]=k;
tag[u]=0;
return;
}
push_down(u,l,r);
int mid=(l+r)>>1;
if(x<=mid) modify(ls,l,mid,x,y,k);
if(y>mid) modify(rs,mid+1,r,x,y,k);
push_up(u);
}
int query(int u,int l,int r,int x,int y)
{
if(y<l||r<x) return 0;
if(x<=l&&r<=y) return t[u];
push_down(u,l,r);
int mid=(l+r)>>1,res=0;
if(x<=mid) res+=query(ls,l,mid,x,y);
if(y>mid) res+=query(rs,mid+1,r,x,y);
return res;
}
struct dat
{
int opt,x,y,k;
}p[N];
signed main()
{
// freopen("king.in","r",stdin);
// freopen("king.out","w",stdout);
scanf("%lld%lld%lld",&n,&m,&Q);
for(int i=1;i<=n;i++) scanf("%lld",a+i);
build(1,1,n);
for(int i=1;i<=Q;i++) scanf("%lld%lld%lld%lld",&p[i].opt,&p[i].x,&p[i].y,&p[i].k);
for(int i=Q;i;i--)
{
int opt=p[i].opt,x=p[i].x,y=p[i].y,k=p[i].k;
if(opt&1)
{
ans[k]+=query(1,1,n,x,y);
modify(1,1,n,x,y,0);
continue;
}
update(1,1,n,x,y,k);
}
for(int i=1;i<=n;i++) ans[a[i]]+=query(1,1,n,i,i);
for(int i=1;i<=m;i++) printf("%lld\n",ans[i]);
return 0;
}
Round 22 (9.24)
\(\mathcal A\)
小模擬麻將題
-
先考慮 \(n=14\)
若 \(k=2\) 只考慮順子和雀頭
若 \(k=3,4\) 列舉雀頭再檢驗剩下的牌是刻子還是順子
-
在考慮 \(n=13\)
顯然可以列舉最後一張牌,然後就用 \(n=14\) 時的方法做就好
\(\mathcal B\)
人類智慧題
發現 \(0 \leq y_i \leq 10\),可以先按 \(x_i\) 從小到大排序,然後發動人類智慧,每個點只連它前面的 \(20\) 個點,然後跑最小生成樹,做完了
時間複雜度 \(\mathcal O(n \log n)\)
#pragma GCC optimize(3,"Ofast","inline")
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+5,M=2e6+5;
int n,cnt,fa[N],siz[N];
struct dat
{
int x,y;
}p[N];
bool cmp(const dat &a,const dat &b)
{
return a.x^b.x?a.x<b.x:a.y<b.y;
}
int dis(int a,int b,int x,int y)
{
return (a-x)*(a-x)+(b-y)*(b-y);
}
struct Edge
{
int u,v,w;
}e[M];
bool cmpe(const Edge &a,const Edge &b)
{
return a.w<b.w;
}
int find(int x)
{
return fa[x]==x?x:fa[x]=find(fa[x]);
}
int Kruskal()
{
int tot=0,mst=0;
for(int i=1;i<=n;i++) fa[i]=i,siz[i]=1;
sort(e+1,e+1+cnt,cmpe);
for(int i=1;i<=cnt;i++)
{
int x=find(e[i].u),y=find(e[i].v);
if(x==y) continue;
if(siz[x]<siz[y]) swap(x,y);
fa[y]=x;
siz[x]+=siz[y];
mst+=e[i].w;
if(++tot==n-1) break;
}
return mst;
}
signed main()
{
// freopen("ant.in","r",stdin);
// freopen("ant.out","w",stdout);
scanf("%lld",&n);
for(int i=1;i<=n;i++) scanf("%lld%lld",&p[i].x,&p[i].y);
sort(p+1,p+1+n,cmp);
for(int i=1;i<=n;i++)
for(int j=i+1;j<=min(i+20,n);j++)
e[++cnt]=(Edge){i,j,dis(p[i].x,p[i].y,p[j].x,p[j].y)};
printf("%lld",Kruskal());
return 0;
}
\(\mathcal C\)
咕咕咕
\(\mathcal D\)
咕咕咕
Round 23 (9.27)
\(\mathcal A\)
求機率題,也就模數換成了 \(147744151\) (還好是質數)
\(\mathcal B\)
類似於是換根 dp
先跑兩次 dfs 求一下樹的直徑,先以 \(1\) 為根跑一次統計轉向邊的數量,再跑一次 dfs 換根就貢獻即可
時間複雜度 \(\mathcal O(n)\),不知道題解在幹什麼搞倍增帶一隻 \(\log\)
#pragma GCC optimize(3,"Ofast","inline")
#include<bits/stdc++.h>
#define eb emplace_back
using namespace std;
const int N=2e5+5,INF=0x3f3f3f3f;
int n,D,dis1[N],dis2[N],cst[N],ans,sum;
struct Edge
{
int v,w,c;
};
vector <Edge> g[N];
void dfs1(int u,int fa)
{
for(auto [v,w,c]:g[u])
{
if(v==fa) continue;
dis1[v]=dis1[u]+w;
dfs1(v,u);
}
}
void dfs2(int u,int fa)
{
for(auto [v,w,c]:g[u])
{
if(v==fa) continue;
sum+=c;
dfs2(v,u);
}
}
void dfs3(int u,int fa,int s)
{
if(dis1[u]<=D&&dis2[u]<=D) ans=min(ans,s);
for(auto [v,w,c]:g[u])
{
if(v==fa) continue;
dfs3(v,u,s+(c==1?-1:1));
}
}
int main()
{
// freopen("ex_b1.in","r",stdin);
// freopen("ex_b1.out","w",stdout);
scanf("%d%d",&n,&D);
for(int i=1;i<n;i++)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
g[u].eb((Edge){v,w,1});
g[v].eb((Edge){u,w,0});
}
int x=0;
dfs1(1,0);
for(int i=1;i<=n;i++)
if(dis1[i]>dis1[x]) x=i;
dis1[x]=0;
dfs1(x,0);
memcpy(dis2,dis1,sizeof(dis1));
for(int i=1;i<=n;i++)
if(dis1[i]>dis1[x]) x=i;
dis1[x]=0;
dfs1(x,0);
dfs2(1,0);
ans=INF;
dfs3(1,0,sum);
printf("%d",ans==INF?-1:ans);
return 0;
}
/*
6 8
2 1 1
2 3 3
2 4 6
1 5 5
6 1 3
*/
\(\mathcal C\)
神秘題,在序列 \(a\) 的區間 \([l,r]\) 中選 \(k\) 個數,最大化 \(\gcd(b_1,b_2,\dots,b_k)\)
令 \(k^\prime \gets (r-l+1)-k\),然後不會
\(\mathcal O(n \log^4 V)\) 真能過嗎
咕咕咕
\(\mathcal D\)
咕咕咕
Round 24 (9.29)
\(\mathcal A\)
結論題,不過一開始沒能立刻看出條件其實推下去就是對於 \(\forall i \in [1,n]\) 都滿足
就跟奇偶性有關
\(\mathcal B\)
有點意思,需要選出來的數中 \(\forall i,j \in [L,R],a_i \mid a_j \lor a_j \mid a_i\)
較為簡單的數論題,從最大值 \(maxa\) 到 \(1\) 一直列舉,然後進 dfs 剪枝求答案即可
\(\mathcal C\)
咕咕咕
\(\mathcal D\)
咕咕咕
\(\mathcal E\)
咕咕咕
Round 25 (10.5)
\(\mathcal A\)
先按 \(l_i\) 從小到大排序,再按 \(r_i\) 從小到大排序
考慮貪心,維護兩個小根堆,一個是已匹配的,一個是未匹配的
能匹配的就匹配(廢話),不能匹配的從已匹配的中找一個小一點 \(r_j\) 來匹配就好
#pragma GCC optimize(3,"Ofast","inline")
#include<bits/stdc++.h>
#define VI vector<int>::iterator
#define PII pair<int,int>
#define mp make_pair
using namespace std;
const int N=4e5+5,INF=0x3f3f3f3f;
int n,ans;
struct dat
{
int l,r;
}a[N];
bool cmp(dat a,dat b)
{
return a.l^b.l?a.l<b.l:a.r<b.r;
}
priority_queue<PII,vector<PII>,greater<PII>> X,Y;
int main()
{
// freopen("a.in","r",stdin);
// freopen("a.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d%d",&a[i].l,&a[i].r);
sort(a+1,a+1+n,cmp);
for(int i=1;i<=n;i++)
{
if(!Y.empty()&&Y.top().first<a[i].l)
{
Y.pop();
X.push(mp(a[i].r,a[i].l));
}
else if(!X.empty()&&X.top().first<a[i].r)
{
Y.push(X.top()),X.pop();
X.push(mp(a[i].r,a[i].l));
}
else Y.push(mp(a[i].r,a[i].l));
}
printf("%d",X.size());
return 0;
}
\(\mathcal B\)
咕咕咕
\(\mathcal C\)
咕咕咕
\(\mathcal D\)
咕咕咕
Round 26 (10.6)
\(\mathcal A\)
簡單數學題,求方程有整數解 \(x^2-2Bx+C=0,(B,C)\) 的對數
#pragma GCC optimize (3,"Ofast","inline")
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+5;
int T,a[N];
signed main()
{
// freopen("equation.in","r",stdin);
// freopen("equation.out","w",stdout);
for(int i=0;i<=1e6;i++)
for(int j=i;j>=0;j--)
{
if(i*i-j*j>1e6) break;
a[i*i-j*j]++;
}
for(int i=1;i<=1e6;i++) a[i]+=a[i-1];
scanf("%lld",&T);
while(T--)
{
int L,R;
scanf("%lld%lld",&L,&R);
printf("%lld\n",a[R]-a[L-1]);
}
return 0;
}
/*
sqrt(B^2-C) = Z
B^2-C=x^2
*/
\(\mathcal B\)
考慮歸納,因為 \(a_i=i\) 會變成不動點,所以在交換的過程中可能需要錯排,而錯排是有解的
\(\mathcal C\)
洛谷 P6864 [RC-03] 記憶
很好的一道資料結構(DS)題
這題最重要是考慮到如何拆分序列以便於統計與更新答案
發現答案會與某些東西在每個操作都存在一定關係,那麼可以試著上矩陣來維護,每次用線段樹單點修改、區間查詢即可
\(\mathcal D\)
真看不懂給的題解,咕咕咕
Round 27 (10.9)
\(\mathcal A\)
簡單數論分塊,過
\(\mathcal B\)
確實有點難推出結論
求 \(ax+by=c\) 非負整數對 \((x,y)\) 的個數,其中 \(a \in [0,N],y \in [0,M],x=Fib_{2k+1},y=_{2k+2},k \in \mathbb{N}\)
擴充套件歐幾里得演算法(exgcd)即可
用歸納法證明出 \(-Fib_iFib_{i-1}+Fib_{i+1}Fib_{i-2}=1\) 省去 exgcd
的 \(\log\),笑死,筆者覺得不如觀察輸出對應的 \(\sout{(x,y)}\) 得出結論簡單
#pragma GCC optimize(3,"Ofast","inline")
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=105;
int T,n,Fib[maxn],X,N,M;
int solve(int A,int B,__int128 x,__int128 y)
{
x*=X,y*=X;
x-=(-y+A-1)/A*B,y+=(-y+A-1)/A*A;
if(x<0||y<0) return 0;
if(x>N) y+=(x-N+B-1)/B*A,x-=(x-N+B-1)/B*B;
if(y>M) x+=(y-M+A-1)/A*B,y-=(y-M+A-1)/A*A;
if(x<0||y<0) return 0;
if(x>N||y>M) return 0;
return min(x/B,(M-y)/A)+min((N-x)/B,y/A)+1;
}
signed main()
{
int T;
scanf("%lld",&T);
Fib[1]=Fib[2]=1;
for(int i=3;i<=100;i++) Fib[i]=Fib[i-1]+Fib[i-2];
while(T--)
{
scanf("%lld%lld%lld",&X,&N,&M);
if(!X)
{
puts("1");
continue;
}
int ans=0;
for(int i=1;i<=43;i++) ans+=solve(Fib[i*2-1],Fib[i*2],Fib[i*2-1],-Fib[i*2-2]);
printf("%lld\n",ans);
}
return 0;
}
/*
6
10 6 9
11 9 2
17 7 5
183 54 20
1919 810 114514
1121135 421443 428543
*/
\(\mathcal C\)
AT_joisc2014_e 水筒
先跑一次 bfs 建圖,然後跑 Kruskal 最小生成樹得到重構樹,最後在樹上跳倍增 LCA 求答案即可
(思路與 洛谷 P1967 [NOIP2013 提高組] 貨車運輸 相似,就多了個 bfs 建圖個過程)
\(\mathcal D\)
洛谷 P7363 [eJOI2020 Day2] Dots and Boxes
咕咕咕
Round 28 (10.10)
譴責 GJ 今天一開始只放一道題,名字叫做 \(\sout 5\) 道題
\(\mathcal A\)
簡單結論題,\(a_1<a_n\) 就符合了,過
\(\mathcal B\)
普通樹論題,但是不知道這個結論:劃分成大小為 \(k\) 的連通塊,當且僅當樹上有 \(n/k\) 個點的 \(size\) 是 \(k\) 的倍數
\(\mathcal C\)
思路有點巧妙,暴力是 dp,發現每個物品的價值都很小,考慮大小約為 \(100 \times 100\) 矩陣快速冪加快遞推
\(\mathcal D\)
不會,咕咕咕
\(\mathcal E\)
數論題,更加不會,看到答案是對 \(2^{32}\) 取模就知道不簡單
掛個柿子以後再來補:
似乎要拆分貢獻?
咕咕咕
Round 29 (10.11)
\(\mathcal A\)
傻波一小明就會做虧本買賣是吧
題目在下面寫了個 \(u<v\),才發現是有向無向圖 \(DAG\),雖然不一定聯通
考慮拓撲排序 + dp,時間複雜度 \(\mathcal O(n+m)\)
\(\mathcal B\)
找規律題,與楊輝三角掛鉤
求的就是每一行前 \(a_i+1\) 個數的和,即第 \(a_i\) 列的值
答案為 $ \sum_{i=1}^{n+1} {i+a_i-1 \choose i} $
\(\mathcal C\)
AT_joisc2017_c 手持ち花火 (Sparklers)
所有人都向 \(k\) 號跑最優,考慮時光倒流,二分,check
裡貪心,後面不會,咕咕咕
\(\mathcal D\)
有一棵 \(n\) 個點的樹,帶有邊權。現有 \(m\) 個詢問如下:在樹上選取 \(k_i\) 條路徑(樹上任意兩點的連線通路視為一條路徑),其中至少要有一條路徑覆蓋到點 \(a_i\),問所有被路徑覆蓋的邊權的和最大是多少。注意重複覆蓋的邊只會計算一次。
樹上問題,不容易發現答案包含直徑某一端點,長鏈剖分,後面不會,咕咕咕
Round 30 (10.14)
\(\mathcal A\)
你作為一名尋寶者,來到了一個古老而神奇的城堡。城堡由多個房間組成,房間之間由牆壁隔開,從一個房間到另一個房間唯一的方法就是任意門傳送。
城堡的地圖可以由一個 \(n\) 行 \(m\) 列的網格圖表示,每個格子可能是空間區域(用 \(1\) 表示)或牆壁區域(用 \(0\) 表示)。任意兩個共享一邊的空間區域被認為屬於同一個房間。
你可以由一個空間區域 \((x_1,y_1)\) 前往另一個空間區域 \((x_2,y_2)\)。
操作 \(1\) - 步行:如果 \((x_1,y_1)\) 和 \((x_2,y_2)\) 屬於同一個房間,那麼你可以花費 \(0\) 體力直接從 \((x_1,y_1)\) 走到 \((x_2,y_2)\)
操作 \(2\) - 任意門傳送:如果 \((x_1,y_1)\) 和 \((x_2,y_2)\) 不屬於同一個房間,那麼你可以花費 \(|x_1-x_2|+|y_1-y_2|\) 的體力從 \((x_1,y_1)\) 傳送至 \((x_2,y_2)\)
注意,如果 \((x_1,y_1)\) 和 \((x_2,y_2)\) 屬於同一個房間,你只能選擇步行前往,不能透過傳送前往。
現在,你計劃從位置 \((x_s,y_s)\) 前往位置 \((x_t,y_t)\),你可以進行任意多次步行和任意門傳送。你可以重複經過同一個房間、也可以重複經過同一個空間區域。
為了更好的體驗任意門的奇妙感覺,你希望使用傳送的次數儘可能多。同時,你所消耗的體力值不能超過直接傳送體力值:定義直接傳送體力值為至多經過一次傳送到達 \((x_t,y_t)\) 的最小體力值消耗。
你想知道,從 \((x_s,y_s)\) 前往任意一個空間區域,在所消耗的體力值不超過直接傳送體力值的前提下,最多能夠使用多少次傳送?
難繃,上來就搞神秘 bfs 題,不會,咕咕咕
\(\mathcal B\)
CF1310E Strange Function
模擬賽上 CF *2900 dp 也是隻有 GJ 敢這麼幹的
(題外話:賽時 puts("1");
有 \(28\) pts 真不錯「伏筆」)
考慮分類討論
-
\(k=1\) 時,將 \(n\) 個元素劃分,上個揹包就好
-
\(k=2\) 時,對最終序列從大到小排序,差分再上揹包就好
-
\(k>2\) 時,因為答案已經不多了,所以直接搜尋剪枝就過了(這就是為啥能總司令了~)
\(\mathcal C\)
構造題
構造每一行形如 $\underline{ryxy} \dots \underline{ryxy} $、大小為 \(40 \times 40\) 的矩陣就好了再把最後一列也這樣搞,剛好是 \(2223\) 個,比法定最大 \(n\) 還多 \(1\) 個
然後就交給隨機化每次隨機更改一個位置求貢獻就好了
時間複雜度:\(\mathcal O(Tm^2)\),其中 \(T=rand(),m=40\),理論上 \(T ∝ \frac{1}{n}\)
#include<bits/stdc++.h>
#define R 1
#define Y 2
#define X 3
using namespace std;
const int N=45;
const int dx[8]={-1,1,0,0,-1,1,-1,1};
const int dy[8]={0,0,-1,1,-1,1,1,-1};
int n,a[N][N],b[N][N];
int query()
{
int res=0;
for(int i=1;i<=40;i++)
for(int j=1;j<=40;j++)
for(int k=0;k<=6;k+=2)
{
if(a[i][j]!=Y) break;
int A=a[i+dx[k]][j+dy[k]];
int B=a[i+dx[k+1]][j+dy[k+1]];
if(A==R&&B==X) res++;
if(A==X&&B==R) res++;
}
return res;
}
mt19937 rd(time(NULL));
int rnd(int l,int r)
{
return rd()%(r-l+1)+l;
}
void init()
{
for(int i=1;i<=40;i++)
for(int j=1;j<=37;j+=4)
a[i][j]=R,a[i][j+1]=Y,a[i][j+2]=X,a[i][j+3]=Y;
for(int i=1;i<=37;i+=4)
a[i][40]=R,a[i+1][40]=Y,a[i+2][40]=X,a[i+3][40]=Y;
}
int main()
{
scanf("%d",&n);
init();
memcpy(b,a,sizeof(a));
printf("40 40\n");
while(query()>n)
{
int x=rnd(1,40),y=rnd(1,40);
a[x][y]=X;
if(query()<n) a[x][y]=b[x][y];
}
for(int i=1;i<=40;i++)
{
for(int j=1;j<=40;j++)
printf("%c",a[i][j]==R?'r':a[i][j]==Y?'y':'x');
printf("\n");
}
return 0;
}
\(\mathcal D\)
對排列 \(\lbrace s_n \rbrace\),定義 \(g(k,i)=\min(s_i,s_{i+1}, \dots ,s_{i+k-1}),G(k)=\max_{i=1}^{n-k+1} g(k,i)\)
現給出 \(G(1),G(2), \dots ,G(n)\),求有多少個滿足要求的排列。
注:排列 \(\lbrace s_n \rbrace\) 指 \(1\) 到 \(n\) 的 \(n\) 個整數按照任意順序排成一列後得到的序列,\(s_i\) 表示排在第個位置的數字。例如當 \(n=3\) 時表示長度為 \(3\) 的排列,共有 \(6\) 種可能,分別是:
\(\lbrace1,2,3\rbrace,\lbrace1,3,2\rbrace,\lbrace2,1,3\rbrace,\lbrace2,3,1\rbrace,\lbrace3,1,2\rbrace,\lbrace3,2,1\rbrace\)
咕咕咕
Round 31 (10.17)
我生活在一個綁包的世界裡
譴責 GJ 不通知有模擬賽,\(-1.5h\) 打模擬賽時間
\(\mathcal A\)
入機題,模擬即可
一開始還被求最大操作次數給詐騙了
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int n;
char st[N];
int main()
{
scanf("%s",st+1),n=strlen(st+1);
int ans=0;
for(int i=2;i<=n;i++)
{
int x=st[i-1]-'0'+st[i]-'0';
if(x<=9) st[i]='0'+x,ans++;
else st[i-1]='0'+x/10,st[i]='0'+x%10,ans++,i--;
}
printf("%d",ans);
return 0;
}
\(\mathcal B\)
不僅 \(200\) 個點還綁包?
構造題,可參考 AT_abc358_f Easiest Maze,但不是完全一樣,還是有點差異的
上下界還是有一定難度想到,畢竟賽後才知道那個 \(2 \mid n, 2 \mid m\) 會使下界 \(+1\)
上界可以考慮直接螺轉,下界可以考慮弄一些豎線,然後橫著來一刀就差不多了
\(\mathcal C\)
表示式求值的方案數
甚至覺得比 [CSP-J 2022] 邏輯表示式 那題會簡單一點,根本不用考慮優先順序,全部運算似乎都會用一對 ()
包著
開兩個棧,一個記 \(0\) 和 \(1\) 的方案數,另一個記運算子,每遇到一個 )
就計算一次貢獻即可
做完了,時間複雜度 \(\mathcal O(n)\)
可以不用像題解那樣建表示式樹
#include<bits/stdc++.h>
#define int long long
#define mp make_pair
#define PII pair<int,int>
#define fi first
#define se second
using namespace std;
const int N=1e6+5,mod=998244353;
int n;
char st[N];
stack<PII> ans;
stack<char> opt;
int mul(int x,int y)
{
return x*y%mod;
}
int add(int x,int y)
{
return x+y>=mod?x+y-mod:x+y;
}
signed main()
{
scanf("%s",st+1),n=strlen(st+1);
for(int i=1;i<=n;i++)
{
if(st[i]=='0') ans.push(mp(1,0));
if(st[i]=='1') ans.push(mp(0,1));
if(st[i]=='?') ans.push(mp(1,1));
if(st[i]=='&'||st[i]=='|'||st[i]=='^'||st[i]=='#') opt.push(st[i]);
if(st[i]==')')
{
PII x=ans.top();ans.pop();
PII y=ans.top();ans.pop();
char op=opt.top();opt.pop();
PII res={0,0};
if(op=='&'||op=='#')
{
res.fi=add(add(res.fi,mul(x.fi,y.fi)),add(mul(x.fi,y.se),mul(x.se,y.fi)));
res.se=add(res.se,mul(x.se,y.se));
}
if(op=='|'||op=='#')
{
res.fi=add(res.fi,mul(x.fi,y.fi));
res.se=add(add(res.se,mul(x.fi,y.se)),add(mul(x.se,y.fi),mul(x.se,y.se)));
}
if(op=='^'||op=='#')
{
res.fi=add(res.fi,add(mul(x.fi,y.fi),mul(x.se,y.se)));
res.se=add(res.se,add(mul(x.fi,y.se),mul(x.se,y.fi)));
}
ans.push(res);
}
}
printf("%lld",ans.top().se);
return 0;
}
\(\mathcal D\)
在二維平面上有 \(n\) 個點,第 \(i\) 個點 \((x_i,y_i)\) 有權值 \(w_i\)。
可以進行若干次這樣的操作:選擇兩個點 \(u,v\),將 \(u\) 的權值一部分 \(\Delta w\) 加給 \(v\),但是要承受 \(d=\sqrt{(x_u-x_v)^2+(y_u-y_v)^2}\) 的損失,即兩點間的歐幾里得距離。也就是 \(w_u-= \Delta w,w_v+= \max(0,\Delta w-d)\)。
現在你希望所有點中最小的權值的最大,並求出該值。
兩點間每操作一次,顯然全域性點權總和會減少,即 \(\sum w \gets \sum w - \sqrt{(x_u-x_v)^2+(y_u-y_v)^2}\),那麼兩點間顯然只會操作最多一次
進一步地,操作可以形成一棵樹或是森林,且同一個連通塊 \(\lvert V \rvert\) 內的最大值為 \(\frac{\sum_{u \in V} w_u - mst}{\lvert V \rvert }\),其中 \(mst\) 為連通塊 \(\lvert V \rvert\) 的最小生成樹,上界易證
那麼可以先狀壓求出每個連通塊的點權,再 dp 即可
時間複雜度 \(\mathcal O(2^n (n^2+n \log n))\)
#pragma GCC optimize(3,"Ofast","inline")
#include<bits/stdc++.h>
#define double long double
using namespace std;
const int N=21,M=N*N;
int n,fa[N];
double dis[N][N],dp[(1<<16)+5];
struct dat
{
int x,y;
double w;
}a[N];
int cnt;
struct Edge
{
int u,v;
double w;
}e[M];
bool cmp(Edge a,Edge b)
{
return a.w<b.w;
}
int find(int x)
{
return fa[x]==x?x:fa[x]=find(fa[x]);
}
double distance(dat a,dat b)
{
return sqrt(1.0*(a.x-b.x)*(a.x-b.x)+1.0*(a.y-b.y)*(a.y-b.y));
}
double Kruskal()
{
int tot=0;double mst=0;
for(int i=1;i<=n;i++) fa[i]=i;
sort(e+1,e+1+cnt,cmp);
for(int i=1;i<=cnt;i++)
{
int x=find(e[i].u),y=find(e[i].v);
if(x==y) continue;
fa[y]=x,mst+=e[i].w;
if(++tot==n-1) break;
}
return mst;
}
int main()
{
// freopen("desert.in","r",stdin);
// freopen("desert.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d%d%Lf",&a[i].x,&a[i].y,&a[i].w);
for(int i=1;i<n;i++)
for(int j=i+1;j<=n;j++) dis[i][j]=distance(a[i],a[j]);
int S=(1<<n)-1;
for(int i=0;i<=S;i++)
{
double sum=0;int siz=0;cnt=0;
for(int j=1;j<=n;j++)
if(i&(1<<(j-1))) sum+=a[j].w,siz++;
for(int j=1;j<n;j++)
for(int k=j+1;k<=n;k++)
if(i&(1<<(j-1))&&i&(1<<(k-1)))
e[++cnt]=(Edge){j,k,dis[j][k]};
double mst=Kruskal();
dp[i]=(sum-mst)/siz;
}
for(int i=0;i<=S;i++)
for(int j=i;j;j=(j-1)&i)
dp[i]=max(dp[i],min(dp[j],dp[j^i]));
printf("%.10Lf",dp[S]);
return 0;
}
/*
3
0 0 10
2 0 5
0 5 8
*/
Round 32 (10.18)
又是綁包。。。
\(\mathcal A\)
詐騙題,經過 \(\mathcal O(\log k)\) 次操作之後每個數變為 \(2k\) 或 \(2k+1\)
暴力列舉前 \(50\) 次總和即可,\(m>50\) 的基本都可以看做 \(m=50\)
\(\mathcal B\)
詐騙題,並且成功在賽時把我騙了
因為題目說保證有解且唯一,所以除了 \(s_0\) 會出現奇數次,其他會出現偶數次
所以直接輸出出現奇數次的字元,開個桶計數即可
#include<bits/stdc++.h>
using namespace std;
const int N=3e5+5;
int n,len,cnt[31];
char st[N];
int main()
{
// freopen("history.in","r",stdin);
// freopen("history.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n<<1;i++)
{
scanf("%s",st+1),len=strlen(st+1);
for(int j=1;j<=len;j++) cnt[st[j]-'a'+1]++;
}
scanf("%s",st+1),len=strlen(st+1);
for(int i=1;i<=len;i++) cnt[st[i]-'a'+1]--;
for(int i=1;i<=26;i++)
if(cnt[i]&1) return putchar('a'+i-1),0;
}
/*
2
abcabc
a
abc
b
abcb
*/
\(\mathcal C\)
咕咕咕
\(\mathcal D\)
咕咕咕
Round ? (10.19)
GJ 設成了 IOI 賽制 😃
真的沒意思,沒到兩個小時就 AC 前三題了
前面 \(3\) 題比較簡單,甚至 \(T3\) 我都做過原了。。。
\(T4\) 沒看,puts("0");
總司令 \(15\) 分走了
\(100+100+100+15=315\),打得最爽也是最高分的一集
\(\mathcal A\)
入機數學題
顯然對於每一個 \(b_i\) 取模,必有一個數 \(x\) 使得 \(\forall i \in [1,n],x \bmod b_i=b_i-1\)
那麼答案就為 \(ans=\sum (b_i-1)\)
\(\mathcal B\)
洛谷 P3106 [USACO14OPEN] Dueling GPSs S
考慮反向建邊,從 \(n\) 開始跑三次 Dijkstra做完了
\(\mathcal C\)
CF920F SUM and REPLACE
線段樹板題
考慮先用線性篩 \(\mathcal O(N)\) 預處理 \(d(i)\),然後每次暴力修改 \(a_i \gets d(a_i)\)
線段樹再記個區間最大值 \(maxl\),顯然當 \(maxl \leq 2\) 時就不用再往下遞迴了
應該是要勢能分析的,但是筆者不會,考慮到一個數被修改很少次就會變成 \(1\) 或 \(2\),每個數最多會被修改 \(\mathcal O(\log n)\) 次
所以時間複雜度 \(O(n \log n)\)
同型別題目推薦:
- CF438D The Child and Sequence
- 洛谷 P4145 上帝造題的七分鐘 2 / 花神遊歷各國
\(\mathcal D\)
咕咕咕