第十九屆浙大城市學院程式設計競賽
阿新 • • 發佈:2022-04-12
第十九屆浙大城市學院程式設計競賽
E Disjoint Path On Tree
題意:求樹上不相交簡單路徑對的數量
做法:其實很容易想到,我們需要求f1[x]表示經過x點的子樹內的簡單路徑數,f2[x]表示子樹x的內的簡單路徑數量,但是統計數量的時候賽時我想的複雜了一些,因為還需要換根求一下各種值,但其實我們發現m個點構成的子樹內簡單路徑數量就是其中數對的數量m*(m+1)/2,因為任意兩點都存在簡單路徑,那麼計算方法就簡單了,就等於\(f1[x]*m(m+1)/2\),m=n-f3[x],f3[x]表示x子樹大小,但是我們考慮這樣計算,其實每個子樹的各個兒子之間的數量都被重複計算了,我們需要剪掉這部分,也就是剪掉f2[v]的,v是x的子節點
點選檢視程式碼
#include <bits/stdc++.h> using namespace std; const int maxn=2e5+10; typedef long long ll; vector<int>G[maxn]; ll f1[maxn],f2[maxn],f3[maxn],dp[maxn]; const ll mod=1e9+7; ll ans=0; int n; void init(int n){ for (int i=1;i<=n;i++) G[i].clear(),dp[i]=f1[i]=f2[i]=f3[i]=0; } ll quickpow(ll a,ll b){ ll p=1; for (;b;b>>=1){ if(b&1ll) p=p*a%mod; a=a*a%mod; } return p%mod; } void dfs(int x,int f){ f3[x]=f1[x]=1; for (int i=0;i<G[x].size();i++){ int v=G[x][i]; if(v==f) continue; dfs(v,x); dp[x]=(dp[x]-f2[x]*f2[v])%mod; f1[x]=(f1[x]+f3[x]*f3[v]%mod)%mod; f3[x]=(f3[x]+f3[v])%mod; f2[x]=(f2[x]+f2[v])%mod; } f2[x]=(f2[x]+f1[x])%mod; ll p=(n-f3[x]+mod)%mod; ll m=p*(p+1ll)%mod*quickpow(2,mod-2)%mod; dp[x]=(dp[x]+f1[x]*m)%mod; ans=((ans+dp[x])%mod+mod)%mod; } int main(){ #ifdef lmj_debug freopen("1.in","r",stdin); #endif int T; cin>>T; while(T--){ ans=0; scanf("%d",&n); if(n==1 || n==2) { if(n==1) printf("0\n"); else { int x,y; cin>>x>>y; printf("1\n"); } continue; } init(n); for (int i=1;i<n;i++){ int u,v; scanf("%d%d",&u,&v); G[u].push_back(v); G[v].push_back(u); } dfs(1,1); //for (int i=1;i<=n;i++) printf("%lld ",f2[i]); printf("%lld\n",ans); } return 0; }
H Distance
題意:給定多個線段,要求一箇中間點,使得到各個線段的距離都最短
做法:其實們就是選一箇中點即可,因為中點的性質就是到一條線段上其它點的和最小,所以我們只需要對頂堆維護中點值即可,現在我們來考慮怎麼計算距離之和,1.對於一個線段,如果其值位於現在中點的兩邊,那麼答案貢獻不增加。2.對於一條線段右端點r<中點,那麼中點的值會改變,但是我們思考中點移動會改變當前的嗎?在中點移動後並未使左右線段數量改變的前提下其實答案不會變,所以我們只需要考慮新加進來的點產生的貢獻,貢獻即為中點值top-r。3.對於l大於中點也是類似的加入貢獻即可
點選檢視程式碼
#include <bits/stdc++.h> using namespace std; typedef long long ll; priority_queue<int,vector<int>,less<int> >Q1; priority_queue<int,vector<int>,greater<int> >Q2; int main(){ #ifdef lmj_debug freopen("1.in","r",stdin); #endif int n; ll ans=0; cin>>n; Q1.push(-1e9-10); Q2.push(1e9+10); for (int i=1;i<=n;i++){ int l,r; scanf("%d%d",&l,&r); if(r<Q1.top()){ ans+=(Q1.top()-r); Q2.push(Q1.top()); Q1.pop(); Q1.push(l); Q1.push(r); }else if(l>Q2.top()){ ans+=(l-Q2.top()); Q1.push(Q2.top()); Q2.pop(); Q2.push(l); Q2.push(r); }else Q1.push(l),Q2.push(r); printf("%lld\n",ans); } return 0; }
J Substring Inversion (Easy Version)
做法:其實比較簡單就是直接暴力處理出所有字串然後排序,\(n^3logn\),考慮到a<c所以我們做匹配的時候直接\(n^3\)做,具體見程式碼
點選檢視程式碼
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
int sum[550];
vector< pair<string,int> >a;
int main(){
#ifdef lmj_debug
freopen("1.in","r",stdin);
#endif
int T;
cin>>T;
while(T--){
a.clear();
int n;
string s;
cin>>n>>s;
for (int i=0;i<n;i++){
sum[i]=0;
string ch="";
for (int j=i;j<n;j++){
ch+=s[j];
a.push_back(make_pair(ch,i));
}
}
long long ans=0;
sort(a.begin(),a.end());
for (int i=0;i<a.size();i++){
int r=a[i].second;
for (int j=r+1;j<n;j++){
ans+=sum[j];
}
sum[r]++;
}
printf("%lld\n",ans%mod);
}
return 0;
}
字尾陣列做法:
F Sum of Numerators
做法:其實很簡單,但是當時還是想了一小會兒,我們直接通過2^x冪去篩就行了,然後x要小於k,因為x-1篩出來的必定含有x的,所以還要將x篩出來的方案減一下就ok了
點選檢視程式碼
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll phi[40];
int main(){
#ifdef lmj_debug
freopen("1.in","r",stdin);
#endif
for (int i=0;i<=32;i++) phi[i]=(1ll<<i);
int T;
cin>>T;
while(T--){
ll n,k;
scanf("%lld%lld",&n,&k);
ll ans=0;
ll p=n/2ll;
if(n&1ll) p++;
ans+=((p)+(p*(p-1)));
if(k>32) k=32;
ll last=0;
for (int i=k;i>=1;i--){
ll x=n/phi[i];
ll o=(x+(x*(x-1)/2));
ans+=o;
ans-=last;
last=o*2ll;
}
if(k==0) {
p=n/2ll;
ans+=((2ll*p)+(p*(p-1)));
}
printf("%lld\n",ans);
}
return 0;
}