• SDUWH 第九届程序设计大赛


    A题 数学期望 待补

    B题 分块莫队 待补

    F题 FFT 待补

    I 题 待补

    J题 待补

    C题:

    题目大意:S1和S2都要到T去 最小化修路的费用

    分析:也就是尽量能重合走的就重合走 一定存在中间点mid S1到T 和 S2到T 都要经过mid

    所以 最小化dis(S1,mid)+dis(S2,mid)+dis(mid,T) 就好

    建正边和反边分别跑一次最短路就好

    注意!!!一定不要再用spfa 他死了!!!!但是比赛就是用的spfa坑死了

    #include <bits/stdc++.h>
    using namespace std;
    using LL = long long;
    using PII = pair<int, int>;
    using PLI = pair<LL, int>;
    const LL INF = 1e18;
    
    int main() {
    	ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
    	int n, m, s1, s2, t;
    	cin >> n >> m >> s1 >> s2 >> t;
    	vector<vector<PII>> G(n), rG(n);
    	for(int i = 0; i < m; ++ i) {
    		int u, v, w;
    		cin >> u >> v >> w;
    		G[u].push_back({v, w});
    		rG[v].push_back({u, w});
    	}
    	auto dijkstra = [&](int s, vector<vector<PII>> &g) -> vector<LL> {
    		vector<bool> vis(n, 0);
            vector<LL> d(n, INF);
            priority_queue<PLI, vector<PLI>, greater<PLI>> Q; 
            d[s] = 0;
            Q.push({0, s});
            while(!Q.empty()) {
                auto u = Q.top().second; Q.pop();            
                if(vis[u]) continue;
                vis[u] = 1;
                for(auto &[v, w] : g[u]) {
                    if(d[v] > d[u] + w) {
                        d[v] = d[u] + w;
                        Q.push({d[v], v});
                    }
                }
        	}
        	return d;
    	};
    	auto d1 = dijkstra(s1, G);
    	auto d2 = dijkstra(s2, G);
    	auto d3 = dijkstra(t, rG);
    	LL ans = INF;
        for(int i = 0; i < n; ++ i) 
        	ans = min(ans, d1[i] + d2[i] + d3[i]);
        cout << (ans >= INF ? -1 : ans) << "\n";
    	return 0;
    }
    
    

    D题

    大意:一棵树 取一个节点就要把子树全部取掉 求取不超过m个节点的最大值

    一道非常典型的树上背包问题啊!!!!当时脑子抽了 居然没打出来 好气啊!!!

    dp[i,j] 表示以i为根的子树 保留连续的j个最小值

    因为要保证连续 所以先设初始值 dp[i,1]=val[i] 这样后面转移保留比1大的数量时候一定都会加上i节点本身

    code:

    #include<bits/stdc++.h>
    using namespace std;
    #define lowbit(x) x&(-x)
    #define ll long long
    const int maxn=505;
    ll dp[maxn][maxn],val[maxn],sz[maxn],sum;
    vector<int>Q[maxn];
    ll n,m;
    void dfs(int,int);
    void dfs2(int,int);
    int main(){
    	memset(dp,0x3f,sizeof(dp));
    	cin>>n>>m;
    	for(int i=1;i<n;i++){
    		int aa,bb;
    		scanf("%d%d",&aa,&bb);
    		Q[aa].push_back(bb);
    		Q[bb].push_back(aa);
    	}
    	for(int i=1;i<=n;i++)scanf("%lld",&val[i]),sum+=val[i];
    	dfs(1,1);
    	dfs2(1,1);
    	if(m>=n)m=n-1;
    	cout<<sum-dp[1][n-m]<<endl;
         return 0;
    }
    void dfs(int u,int fa){
    	sz[u]=1;
    	for(int i=0;i<Q[u].size();i++){
    		int to=Q[u][i];
    		if(to==fa)continue;
    		dfs(to,u);
    		sz[u]+=sz[to];
    	}
    }
    void dfs2(int u,int fa){
    	dp[u][1]=val[u];
    	for(int i=0;i<Q[u].size();i++){
    		int to=Q[u][i];
    		if(to==fa)continue;
    		dfs2(to,u);
    		for(ll S=n-m;S>0;S--)
    		for(int k=0;k<=min(sz[to],S);k++)
    		dp[u][S]=min(dp[to][k]+dp[u][S-k],dp[u][S]);
    	}
    }
    

    E题

    大意:一个序列 给定一个最大值max 最小值min 求有多少子序列满足最大值为max 最小值为min

    分析:这应该算一个非常经典的问题

    考虑容斥 设函数 calc(l,r) 统计都数值都在 内的区间个数。

    calc函数还是很好算 直接线性扫一遍即可

    经典容斥 :ans=calc(l,r)- calc(l+1,r) - calc(l,r-1) +calc(l+1,r-1)

    code:

    #include <bits/stdc++.h>
    using namespace std;
    using LL = long long;
    const int N = 200010;
     
    LL n, a[N];
    LL ans;
     
    void solve(LL x, LL y, LL t) {
    	int lst = 1;
    	for(int i = 1; i <= n; ++ i) {
        	if(a[i] > x or a[i] < y) {
        		lst = i + 1;
        	}
        	ans += (i - lst + 1) * t;
        }
    }
     
    int main() {
        ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
        LL x, y;
        cin >> n >> x >> y;
        for(int i = 1; i <= n; ++ i) cin >> a[i];
        solve(x, y, 1);
        solve(x - 1, y, -1);
        solve(x, y + 1, -1);
        solve(x - 1, y + 1, 1);
        cout << ans << "\n";
        return 0;
    }
    
    

    G题

    大意:一个字符串 求每个子序列的值 定义一个子序列的值为不同字符的个数

    分析:
    也是一道非常经典的计数问题

    很明显考虑每个字符单独的贡献为多少

    贡献:

    左边界在上一个相同的该字母之后,在它之前(左开右闭)

    右边界在它之后(闭区间)

    和之前有个算有多少个子序列乘积为0一模一样!!!!!!!!!!!!

    但是当时比赛就是没想出来唉 这是一个模型 一定要记住!!!!

    这样只需要记录一下last数组就好了

    code:

    #include <bits/stdc++.h>
    using namespace std;
    using LL = long long;
    const int N = 200010;
     
    LL n, a[N];
    LL ans;
     
    void solve(LL x, LL y, LL t) {
    	int lst = 1;
    	for(int i = 1; i <= n; ++ i) {
        	if(a[i] > x or a[i] < y) {
        		lst = i + 1;
        	}
        	ans += (i - lst + 1) * t;
        }
    }
     
    int main() {
        ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
        LL x, y;
        cin >> n >> x >> y;
        for(int i = 1; i <= n; ++ i) cin >> a[i];
        solve(x, y, 1);
        solve(x - 1, y, -1);
        solve(x, y + 1, -1);
        solve(x - 1, y + 1, 1);
        cout << ans << "\n";
        return 0;
    }
    
    

    H题

    大意:一个01矩阵 对角线反转 行反转 列反转 问能否经过一系列操作将矩阵全部转化为0 并记录过程

    分析:

    首先不需要最优化步骤

    每行或者每列要么反转一次 要么不反转

    对角线特殊判断一下

    默认第一行不反转,那么每一个1所在的列一定是翻转了。检查每一行是否能够不导致冲突。

    有方案的话把记录下来的内容输出。

    #include <bits/stdc++.h>
    using namespace std;
    
    int main() {
    	ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
    	int T;
    	cin >> T;
    	while(T -- ) {
    		int n;
    		cin >> n;
    		vector<vector<int>> A(n, vector<int> (n));
    		for(int i = 0; i < n; ++ i) 
    			for(int j = 0; j < n; ++ j)
    				cin >> A[i][j];
    		vector<int> row, col;
    		auto solve = [&]() -> bool {
    			row.clear(); 
    			col.clear();
    			for(int i = 1; i < n; ++ i) {
    				int c = 0;
    				for(int j = 0; j < n; ++ j)
    					c += (A[i][j] ^ A[0][j]);
    				if(c > 0 and c < n) return 0;
    				if(c == n) row.push_back(i);
    			}
    			for(int i = 0; i < n; ++ i)
    				if(A[0][i]) col.push_back(i);
    			return 1;
    		};
    		if(solve()) {
    			cout << "YES\n";
    			cout << row.size() + col.size() << "\n";
    			for(int &i : row) cout << "row " << i + 1 << "\n";
    			for(int &i : col) cout << "col " << i + 1 << "\n";
    			continue;
    		};
    		for(int i = 0; i < n; ++ i) A[i][i] ^= 1;
    		if(solve()) {
    			cout << "YES\n";
    			cout << row.size() + col.size() + 1 << "\n";
    			cout << "diagonal\n";
    			for(int &i : row) cout << "row " << i + 1 << "\n";
    			for(int &i : col) cout << "col " << i + 1 << "\n";
    			continue;
    		};
    		cout << "NO\n";
    	}
    	return 0;
    } 
    
    

    K题:打表找规律

    L题:模拟多项式除法

  • 相关阅读:
    css3skew
    github如何使用
    互联网历史
    html知识点
    人类的终极目标是什么?
    如何提高自我学习能力?
    为什么富人越富,穷人越穷?
    关于游戏小说与学习知识的不同
    关于写代码的一点体会
    监听多行文本框字数输入
  • 原文地址:https://www.cnblogs.com/wzxbeliever/p/16477068.html
Copyright © 2020-2023  润新知