• noi.ac七一模拟赛


    打的时候感觉很阴间,期望是104,但是只打到了84,今天又把T2,T3做了一遍发现还是挺好的

    T2

    简述题意:

    y打败x可以获得x的全部分数并额外获得奖励分数,奖励分数分为两个部分,此次获胜的奖励分数w,和y获胜j次(本次为j+1次)的奖励分数n-j

    设Y为一次都没有获胜过的人数,X为所有幸存的人人数(被打败后人消失,一个人不会被打败多次),最大化(frac{X^k}{Y})(kin (1,2))

    思路:

    看数据范围和题目的一些特征应该可以想到网络流,这样问题就在于怎样连边(以下不特殊说明流量全为1)

    • 对于第一条,y打败x可以获得x的全部分数

    那么就是一条路径的总费用呗,x消失但是费用全加在y身上

    • 对于第二条,奖励分数w

    网络流的经典套路建虚点,从x向y`连一条费用为w的边

    • 对于第三条获胜次数的奖励分数

    我们注意到这个奖励是递减的(这个性质是有必要的),根据费用流贪心的想,我一定会先流满赢上一次的,才会流赢这一次的,那么从虚点连n,n-1,……,n-deg的边到终点就好了

    • 还有就是起点连向每个人的边,每个人连向终点的边(可能一个人都没打败)

    • 最后要处理的就是这个Y了

    他很烦人,我们考虑他的补集:多少个人打败过别人,这也不好求,那我们直接枚举他

    怎么处理呢?建一个虚终点,从每个人的虚点向这个虚终点连一条n+inf的边,然后从虚终点向终点连一条容量为y的边

    这样再求最大费用的时候一定会优先走几条n+inf的边直到容量y全部流满

    这样如果虚终点向终点的边的反边流满的话,就证明可以有y个人打败过别人,且现在的X最大,直接求解就好啦

    感觉这个套路和思想还是要熟悉一下的

    代码:

    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <cstdio>
    #include <queue>
    #define int long long
    #define B cout<<"Breakpoint"<<endl;
    #define O(x) cout<<#x<<" "<<x<<endl;
    #define o(x) cout<<#x<<" "<<x<<" ";
    using namespace std;
    int read(){
    	int x = 1,a = 0;char ch = getchar();
    	while (ch < '0'||ch > '9'){if (ch == '-') x = -1;ch = getchar();}
    	while (ch >= '0'&&ch <= '9'){a = a*10+ch-'0';ch = getchar();}
    	return x*a;
    }
    const int maxn = 1e5+10,inf = 1e18+7;
    int n,m,k;
    struct node{
    	int u,v,to,nxt,w,lim;
    }ed[maxn << 1],edge[maxn << 1];
    int head[maxn],tot = 1,cnt;
    void add(int u,int to,int lim,int w){
    	ed[++tot].w = w;
    	ed[tot].to = to;
    	ed[tot].nxt = head[u];
    	ed[tot].lim = lim;
    	head[u] = tot;
    }
    int s,t,t0,sum,a[maxn];
    int dis[maxn],cur[maxn],vis[maxn],pre1[maxn],pre2[maxn];
    bool SPFA(){
    	memset(vis,0,sizeof(vis));
    	for (int i = s;i <= t;i++) dis[i] = -inf;
    	queue<int> q;q.push(s);
    	dis[s] = 0,vis[s] = 1;
    	while (!q.empty()){
    		int x = q.front();q.pop();
    		vis[x] = 0;
    		for (int i = head[x];i;i = ed[i].nxt){
    			int to = ed[i].to;
    			if (!ed[i].lim) continue;
    			if (dis[to] < dis[x]+ed[i].w){
    				dis[to] = dis[x]+ed[i].w;
    				pre1[to] = x,pre2[to] = i;
    				if (!vis[to]){
    					vis[to] = 1;
    					q.push(to);
    				}
    			}
    		}
    	}
    	return dis[t] != -inf;
    }
    int deg[maxn];
    void add_ed(int u,int v,int lim,int w){
    	edge[++cnt].u = u,edge[cnt].v = v,edge[cnt].lim = lim,edge[cnt].w = w;
    }
    void init(){
    	memset(head,0,sizeof(head));
    	for (int i = 2;i <= tot;i++) ed[i].to = ed[i].nxt = ed[i].lim = ed[i].w = 0;
    	tot = 1;
    	for (int i = 1;i <= cnt;i++) add(edge[i].u,edge[i].v,edge[i].lim,edge[i].w);
    }
    double ans;
    signed main(){
    	n = read(),m = read(),k = read();
    	for (int i = 1;i <= n;i++) a[i] = read(),sum += a[i];
    	s = 0,t0 = n*2+1,t = n*2+2;
    	for (int i = 1;i <= n;i++) add_ed(s,i,1,0),add_ed(i,s,0,0);
    	for (int i = 1;i <= n;i++) add_ed(i,t,1,0),add_ed(t,i,0,0);
    	for (int i = 1;i <= m;i++){
    		int x = read(),y = read(),w = read();
    		deg[y]++;
    		add_ed(x,y+n,1,w),add_ed(y+n,x,0,-w);
    	} 
    	for (int i = 1;i <= n;i++){
    		add_ed(i+n,t0,1,inf+n),add_ed(t0,i+n,0,-inf-n);
    		for (int j = 2;j <= deg[i];j++){
    			add_ed(i+n,t,1,n-j+1),add_ed(t,i+n,0,j-n-1);
    		}
    	}
    	for (int i = 1;i < n;i++){
    		init();
    		add(t0,t,i,0),add(t,t0,0,0);
    		int res = 0;
    		while (SPFA()){
    			int tmp = inf;
    			for (int i = t;i != s;i = pre1[i]) tmp = min(tmp,ed[pre2[i]].lim);
    			res += tmp*dis[t];
    			for (int i = t;i != s;i = pre1[i]) ed[pre2[i]].lim -= tmp,ed[pre2[i]^1].lim += tmp;
    		}
    	//	cout<<res<<endl;
    		if (ed[tot].lim == i){
    			if (k == 1) ans = max(ans,1.0*(res+sum-1ll*i*inf)/(n-i));
    			if (k == 2) ans = max(ans,1.0*(res+sum-1ll*i*inf)*(res+sum-1ll*i*inf)/(n-i));
    		}
    		else break;
    	}
    	printf("%.10lf",ans);
    	return 0;
    }
    

    T3

    借着这道题我也想好好的思考一下决策单调性优化dp,感觉自己再分治这块一直都是弱项,比如整体二分,CDQ分治什么的,最近再好好练练

    怎么发现决策单调性呢?我们可以打表或者感性理解

    决策单调性的概念

    假如对于某一个dp方程,dp(i)的最优转移是dp(k),那么称k为i的决策点

    而dp方程满足决策单调性指的是,决策点k随着i的增大保持单调不减

    1. 被决策点不会成为决策点

    一般的遇到这种情况时,dp方程有两维

    比如,dp(i,j)表示在第i个阶段、对j做决策,dp(i,j)由dp(i−1,k)转移得来

    void solve(int x,int l,int r,int nl,int nr){
    	if (l > r) return;
    	int mid = (l+r >> 1),pos;
    	for (int i = nl;i <= min(mid-1,nr);i++){
    		int val = dp[x-1][i]+w(i,mid);
    		if (val < dp[x][mid]) dp[x][mid] = val,pos = j;
    	}
    	solve(x,l,mid-1,nl,pos),solve(x,mid+1,r,pos,nr); 
    }
    

    2. 被决策点可能会成为决策点

    很简单,我们分治套分治就好了,因为cdq分治的时候我们就是在考虑[l,mid]对[mid+1,r]的贡献,所以我们这样是很符合分治的思想的

    void solve(int l,int r,int L,int R){
    	if (l > r||L > R) return;
    	int pos,lst = inf,w = 0;
    	for (int i = L;i <= R;i++) if ((w = calc(i,mid)) < lst) lst = w,pos = i;
    	dp[mid] = min(dp[mid],lst);
    	solve(l,mid-1,L,pos);solve(mid+1,r,pos,R);
    }
    void cdq(int l,int r){
    	if (l == r) return;
    	cdq(l,mid);solve(mid+1,r,l,mid);cdq(mid+1,r);
    }
    
  • 相关阅读:
    野生前端的数据结构基础练习(3)——链表
    野生前端的数据结构基础练习(3)——链表
    野生前端的数据结构基础练习(3)——链表
    Spring MVC之LocaleResolver详解
    Winfrom 屏蔽Alt+F4
    最简单的单例模式
    Eclipse的优化
    Eclipse的优化
    用PULL解析器解析XML文件
    用PULL解析器解析XML文件
  • 原文地址:https://www.cnblogs.com/little-uu/p/14979624.html
Copyright © 2020-2023  润新知