第十九届浙大城市学院程序设计竞赛
第十九届浙大城市学院程序设计竞赛
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;
}