YL 模拟赛总结 2

Posted on 2024-03-03 18:30  _XOFqwq  阅读(4)  评论(0编辑  收藏  举报

Problem


T1

我们知道,某数 \(x \bmod 9\) 的值 \(= x\) 各个数位上的数之和 \(\bmod \ 9\) 的值。

于是遍历 \([a,b]\),求出区间中每个数各个数位上的数之和 \(sum\),输出 \(sum \bmod 9\) 即可。

T2

中规中矩的 \(dp\)

\(dp_{i,0}\) 表示第 \(i\) 个数填 \(0\) 时的方案数,\(dp_{i,1}\) 同理。

于是答案即为 \(dp_{i,0}+dp_{i,1}\)

\(O(n)\) 预处理,转移方程:

\[\begin{cases} dp_{i,0}=dp_{i-1,0}+dp_{i-1,1}\\ dp_{i,1}=dp_{i-1,0} \end{cases} \]

然后 \(k\) 次询问每次 \(O(1)\) 回答即可。

代码:

#include<bits/stdc++.h>
#define int long long
using namespace std;

const int MOD=12345;
int n,k;
int dp[100031][2];

signed main(){
    cin>>n;
    dp[1][0]=dp[1][1]=1;
    for(int i=2;i<=100000;i++)
        dp[i][0]=(dp[i-1][0]+dp[i-1][1])%MOD,
        dp[i][1]=dp[i-1][0]%MOD;
    while(n--)
        cin>>k,cout<<(!k?0:(dp[k][0]+dp[k][1])%MOD)<<'\n';
    return 0;
}

T3

尝试以每个点为根,计算其散播的最小时间。

具体地,我们先对于当前的点向外扩展,然后将扩展到的点的最小时间压入优先队列。

然后:

对于这张图,点 \(x\) 向外扩展到了 \(x1,x2,x3\) 这三个点,分别在 \(1,2,3\) 的单位时间。

那么,为使每个点的时间尽可能少,我们应当将当前时间最大的点放在 \(x1\),次大的放在 \(x2\),最小的放在 \(x3\),这样就满足了上述条件。

于是我们在递归后将优先队列中的点一一取出,按照上述法则更新每个点的时间即可。

代码:

#include<bits/stdc++.h>
using namespace std;

int n,tot,ans=1e9;
int out[1031],cost[1031];
vector<int> G[2031];
priority_queue<int> pq[1031];

void dfs(int x,int pre){
	cost[x]=0;
	for(auto i:G[x]){
		if(i==pre) continue;
		dfs(i,x);
		pq[x].push(cost[i]);
	}
	for(int i=1;!pq[x].empty();i++){
		int now=pq[x].top(); pq[x].pop();
		cost[x]=max(cost[x],now+i);
	}
}

int main(){
	cin>>n;
	for(int i=2,v;i<=n;i++)
		cin>>v,
		G[i].push_back(v),G[v].push_back(i);
	for(int i=1;i<=n;i++){
		dfs(i,i);
		int now_ans=-1e9;
		for(int j=1;j<=n;j++)
			now_ans=max(now_ans,cost[j]);
		if(now_ans+1<ans){
			ans=now_ans+1;
			tot=0;
			memset(out,0,sizeof(out));
			out[++tot]=i;
		}
		else if(now_ans+1==ans)
			out[++tot]=i;
		//cout<<now_ans<<'\n';
	}
	cout<<ans<<'\n';
	for(int i=1;i<=tot;i++) cout<<out[i]<<' ';
	return 0;
}

T4

显然速度是具有单调性的,因此考虑二分答案。

check 函数的设计:

对于我们二分的速度值 \(k\),我们令图中一条边 \(e=(i,j)\) 的边权为 \(p_{i,j}-k \times t_{i,j}\)(即实际距离减理想距离),然后再这张图上跑 SPFA 最短路。

求出 \(dis\) 数组(单源最短路)后,若 \(dis_n \ge 0\),则说明还有某些路没跑完,速度值太小,令 \(l=mid\),否则令 \(r=mid\)

若图存在负环,则速度变成无限大都跑不出去,于是一直令 \(l=mid\) 即可。

代码:

#include<bits/stdc++.h>
using namespace std;

int n;
int p[131][131],t[131][131];
double w[131][131],dis[131];
int cnt[131];
bool vis[131];

bool spfa(){
    queue<int> q;
    memset(dis,-0x3f3f3f3f,sizeof(dis));
    memset(cnt,0,sizeof(cnt));
    memset(vis,0,sizeof(vis));
    q.push(1);
    dis[1]=0,vis[1]=cnt[1]=1;
    while(!q.empty()){
        int u=q.front(); q.pop(),vis[u]=0;
        for(int v=1;v<=n;v++){
            if(p[u][v]&&dis[v]<dis[u]+w[u][v]){
                dis[v]=dis[u]+w[u][v];
                if(!vis[v]){
                    cnt[v]++;
                    if(cnt[v]>n) return 1;
                    vis[v]=1;
                    q.push(v);
                }
            }
        }
    }
    return dis[n]>=0;
}
bool check(double x){
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            w[i][j]=p[i][j]-t[i][j]*x;
    return spfa();
}

int main(){
    cin>>n;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            cin>>p[i][j];
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            cin>>t[i][j];
    double l=0.0,r=10000.0,eps=0.0001;
    while(l+eps<r){
        double mid=(l+r)/2.0;
        if(check(mid)) l=mid;
        else r=mid;
    }
    cout<<setprecision(3)<<fixed<<l;
    return 0;
}