Codeforces Gym 103428
是CCPC2021威海站的题
只有E H J M
其实是模拟赛
T1(J)
签到,手摸发现一定是每条边长度相等。
然后你考虑到角度大小相等。
乱求一下 \(\gcd\) 即可。
查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
int t,a,b;
signed main(){
ios::sync_with_stdio(0);
cin>>t;
while(t--){
cin>>a>>b;
b*=180;
int tt=__gcd(a,b);
b/=tt;
cout<<b-1<<'\n';
}
return 0;
}
T2(M)
数学题
熔池容斥好题!
先抽象出一个问题:(注意下面的 \(n,m,k\) 不同于原题的 \(n,m,k\))
在 \(0\text{到}n\) 中选择 \(m\) 个数使得它们的和为 \(k\)。
假设没有 \(n\) 的限制,就是经典隔板法了。
\(C^{m-1}_{k+m-1}\)
然后你再把 \(n\) 的限制考虑进去。
那么 \(n\) 时的答案是:
\(f_i=C^{m-1}_{k+m-1-i\times n}\times C^i_m\)
这个东西因为可能会算重复,奇加偶减容斥即可
最后再容斥一遍,\(n\) 的答案减掉 \(n+1\) 的答案就可以了。
怎么把两个 \(n,m,k\) 对应呢,读者自证不难(doge
查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod=998244353;
int qpow(int a,int b){
int ans=1,base=a;
while(b){
if(b&1)ans=ans*base%mod;
base=base*base%mod;
b>>=1;
}
return ans;
}
int inv(int x){
return qpow(x,mod-2);
}
int n,m,k;
//真正的顺序为:k,n-m+1,m
int jc[200005];
int C(int x,int y){
if(x>y)return 0;
return jc[y]*inv(jc[y-x])%mod*inv(jc[x])%mod;
}
int calc(int x){
int ans=0;
for(int i=1;i*x<=k;++i){
if(i&1)ans=(ans+C(m-1,k+m-1-i*x)*C(i,m)%mod)%mod;
else ans=(ans-C(m-1,k+m-1-i*x)*C(i,m)%mod+mod)%mod;
}
// for(int i=0;i<=min(m,k/m);++i)dp[i]=;
return ans;
}
signed main(){
int a,b,c;
ios::sync_with_stdio(0);
cin>>a>>b>>c;
n=c,m=a-b+1,k=b;
if(n==0){
cout<<(k==0);
return 0;
}
jc[0]=1;
for(int i=1;i<=k+m;++i)jc[i]=jc[i-1]*i%mod;
cout<<((calc(n)-calc(n+1))%mod+mod)%mod;
return 0;
}
T3(E)
神仙期望
我们设 \(dp_i\) 表示你再选 \(i\) 次的期望得分(虽然实现的时候代码统一下标加了 \(1\) 的说)
先玩一下 \(dp_0\),自然就是所有情况除以方案数了!
\(dp_0=2\frac{\sum_{1\le i<j\le n}{}(a_i+a_j)}{n\times (n-1)}\)
查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const double eps=1e-6;
int n,k,Q,a[100005],b[100005];
long long sum[100005];
double dp[100005];
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n>>k>>Q;
for(int i=1;i<=n;++i)cin>>a[i],b[i]=a[i];
sort(a+1,a+n+1);
for(int i=1;i<=n;++i)sum[i]=sum[i-1]+a[i];
for(int i=1;i<=k+1;++i){
int lt=1,rt=n;
double summ=0;
while(lt<=n){
while(lt<rt&&a[lt]+a[rt]>=dp[i-1])rt--;
rt=max(lt,rt);
summ+=(rt-lt)*dp[i-1]+(n-rt)*a[lt]+sum[n]-sum[rt];
lt++;
}
// cout<<summ<<'\n';
dp[i]=summ/((n-1)*n/2);
}
cout<<fixed<<setprecision(10)<<dp[k+1]<<'\n';
while(Q--){
int x,y,c;
cin>>x>>y>>c;
double summ=b[x]+b[y];
// cout<<summ<<" "<<dp[c]<<endl;
if(c==0){
cout<<"accept\n";
continue;
}
if(abs(summ-dp[c])<eps){
cout<<"both\n";
}
else if(summ+eps>dp[c]){
cout<<"accept\n";
}
else{
cout<<"reselect\n";
}
}
return 0;
}
之后的每次就是把所有小于上一次的期望的 \(a_i+a_j\) 全部替换成上一次的期望即可。
T4(H)
网络流最小割问题,需要卡常避坑优化写法等一系列操作。
建模:\(v_{i,p}\) 表示把距离点 \(i\) 不超过 \(p\) 的点全部选掉。
\(u_i\) 表示每个点。
源点向每个 \(v_{i,p}\) 建边,为 \(v_p-v_{p-1}\),保证一级一级降。
\(v_{i,p}\) 往距离 \(i\) 为 \(p\) 的点建边。
每个\(v_{i,p}\) 向 \(v_{i,p-1}\)连边 inf,表示必须先割 \(v_{i,p}\) 才能割 \(v_{i,p-1}\)
然后 \(u_i\) 往汇点连 \(w_i\) 即可。
跑最小割即为答案。
查看代码
// Codeforces Gym 103428 (CCPC Weihai Onsite) M
#include<bits/stdc++.h>
//#define int long long
#define inf (int)1e9
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC target("avx")
#pragma GCC optimize("Ofast")
#pragma GCC optimize("inline")
#pragma GCC optimize("-fgcse")
#pragma GCC optimize("-fgcse-lm")
#pragma GCC optimize("-fipa-sra")
#pragma GCC optimize("-ftree-pre")
#pragma GCC optimize("-ftree-vrp")
#pragma GCC optimize("-fpeephole2")
#pragma GCC optimize("-ffast-math")
#pragma GCC optimize("-fsched-spec")
#pragma GCC optimize("unroll-loops")
#pragma GCC optimize("-falign-jumps")
#pragma GCC optimize("-falign-loops")
#pragma GCC optimize("-falign-labels")
#pragma GCC optimize("-fdevirtualize")
#pragma GCC optimize("-fcaller-saves")
#pragma GCC optimize("-fcrossjumping")
#pragma GCC optimize("-fthread-jumps")
#pragma GCC optimize("-funroll-loops")
#pragma GCC optimize("-fwhole-program")
#pragma GCC optimize("-freorder-blocks")
#pragma GCC optimize("-fschedule-insns")
#pragma GCC optimize("inline-functions")
#pragma GCC optimize("-ftree-tail-merge")
#pragma GCC optimize("-fschedule-insns2")
#pragma GCC optimize("-fstrict-aliasing")
#pragma GCC optimize("-fstrict-overflow")
#pragma GCC optimize("-falign-functions")
#pragma GCC optimize("-fcse-skip-blocks")
#pragma GCC optimize("-fcse-follow-jumps")
#pragma GCC optimize("-fsched-interblock")
#pragma GCC optimize("-fpartial-inlining")
#pragma GCC optimize("no-stack-protector")
#pragma GCC optimize("-freorder-functions")
#pragma GCC optimize("-findirect-inlining")
#pragma GCC optimize("-fhoist-adjacent-loads")
using namespace std;
int n,m,s,t,head[10000005],nxt[10000005],edge[10000005],to[10000005],pre[10000005],level[10000005];
int tot;
void add(int u,int v,int w){
to[++tot]=v;
edge[tot]=w;
nxt[tot]=head[u];
head[u]=tot;
to[++tot]=u;
edge[tot]=0;
nxt[tot]=head[v];
head[v]=tot;
}
bool bfs(){
memset(level,0,sizeof(level));
queue<int>q;
level[s]=1;
q.push(s);
while(!q.empty()){
int cur=q.front();
q.pop();
for(int i=head[cur];i;i=nxt[i]){
if(edge[i]&&!level[to[i]]){
q.push(to[i]);
level[to[i]]=level[cur]+1;
if(to[i]==t)return 1;
}
}
}
return 0;
}
int Dinic(int x,int flow){
if(x==t)return flow;
int rest=flow,increase;
for(int i=head[x];i&&rest;i=nxt[i]){
int y=to[i];
if(edge[i]&&level[y]==level[x]+1){
increase=Dinic(y,min(rest,edge[i]));
if(!increase)level[y]=0;
edge[i]-=increase;
edge[i^1]+=increase;
rest-=increase;
}
}
return flow-rest;
}
vector<int>nbr[205];
int d[205],dp[205][12];
void _add(int u,int v){
nbr[u].push_back(v);
return;
}
void before(int cur,int fa){
d[cur]=d[fa]+1;
dp[cur][0]=fa;
for(int i=1;i<=10;++i)dp[cur][i]=dp[dp[cur][i-1]][i-1];
for(auto to:nbr[cur]){
if(to==fa)continue;
before(to,cur);
}
return;
}
int LCA(int u,int v){
if(d[u]<d[v])swap(u,v);
for(int i=10;i>=0;i--){
if(d[u]-d[v]>=(1<<i))u=dp[u][i];
}
if(u==v)return u;
for(int i=10;i>=0;i--){
if(dp[u][i]!=dp[v][i]){
u=dp[u][i];
v=dp[v][i];
}
}
return dp[u][0];
}
int calc(int u,int v){
int lca=LCA(u,v);
return d[u]+d[v]-d[lca]*2;
}
//V_{i,p}->(i-1)*n+p+1; u_i->(n-1)*n+i
int U(int i){
return n*n+i;
}
int V(int i,int p){
return (i-1)*n+p;
}
int w[205],v[205],dis[205][205];
vector<int>_[205][205];
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n;
tot=1;
int res=0;
for(int i=1;i<=n;++i)cin>>w[i];
for(int i=1;i<=n;++i)cin>>v[i];
for(int i=1;i<n;++i){
int u,v;
cin>>u>>v;
_add(u,v);
_add(v,u);
}
before(1,0);
s=0,t=n*(n+1)+1;
// for(int i=1;i<=n;++i){
// cout<<"Point "<<i<<": \n";
// cout<<d[i]<<'\n';
// for(int j=0;j<=10;++j)cout<<dp[i][j]<<' ';
// cout<<'\n';
// }
int flow=0,maxflow=0,sum=n*v[n];
for(int i=1;i<=n;++i)add(U(i),t,w[i]);
for(int i=1;i<=n;++i)for(int j=1;j<=n;++j)dis[i][j]=calc(i,j),_[i][dis[i][j]].push_back(j);
for(int i=1;i<=n;++i){
for(int p=1;p<=n;++p){
add(s,V(i,p),v[p]-v[p-1]);
if(p>1)add(V(i,p),V(i,p-1),inf);
for(auto j:_[i][p-1]){
// if(dis[i][j]!=p)continue;
add(V(i,p),U(j),inf);
}
}
}
while(bfs())maxflow+=Dinic(s,inf);
cout<<sum-maxflow;
return 0;
}