2016长乐夏令营 Day5

68 篇文章 0 订阅
57 篇文章 0 订阅

T1:

f[i]为从i点开始到终点的最大边数期望

考虑不删边时 f[i] = ∑(f[j]+1)/tot j为i点的后继,tot为总边数

令b[j] = f[j]+1 a[j] = 0,1 表示是否选择第j条边

于是f[i] = ∑(b[j]*a[j])/∑a[j] = ans   (01分数规划)

二分ans,移项得  ∑a[j]*(b[j]-ans) > 0 也就是说,左式大于零时,ans就还有增大的可能

如果没有限制条件,那么留下所有b[j]-ans>0的边即可

但是题中存在限制条件,即删去y边必须删去x边

将所有边看成点,那么每个点有一个权值(b[j]-ans)  

对于每个条件,从x向y连一条边,留着x就必须留下y那么原图变成求出最大权闭合子图

对于所有b[j]-ans>0的点,Add(S,j,b[j]-ans)

对于所有b[j]-ans<0的点,Add(j,T,ans-b[j])

对于每个条件,Add(x,y,INF)

跑一遍最大流,选取s-割中所有点,舍去t-割中所有点,剩下的权值就是左边的式子

(因为x,y的边容量为INF,所以该边一定不存在于最小割,而对于每个(x,y)的条件,因而满足了得到x必得到y,舍去y必舍去x)

。。。总之,左边的式子 = 正权点权值和 - 最小割


#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<stack>
#include<cstdlib>
#include<cmath>
#include<algorithm>
using namespace std;

typedef double DB;
const int maxn = 2E3 + 20;
const int T = 2000;
const int S = 0;
const DB INF = 1234567;
const DB eps = 1E-10;

struct E{
	int from,to; DB cap,flow;
	E(int _from = 0, int _to = 0, DB _cap = 0, DB _flow = 0){from = _from; to = _to; cap = _cap; flow = _flow;}
}edgs[maxn*20];

int n,m,k,co,cnt,d[60],D[maxn],cur[maxn],vis[maxn];
DB f[60];

vector <int> v[60];
vector <int> num[60];
vector <int> argue[510];
vector <int> p[maxn]; 
vector <int> v2[60];
queue <int> q;

bool Equal(DB x,DB y)
{
	return abs(x - y) <= eps;
}

void Add(int x,int y,DB w)
{
	p[x].push_back(co); edgs[co++] = E(x,y,w,0.00); 
	p[y].push_back(co); edgs[co++] = E(y,x,0.00,0.00);
}

bool BFS()
{
	D[S] = 1; ++cnt;
	queue <int> Q; Q.push(S); vis[S] = cnt;
	while (!Q.empty()) {
		int k = Q.front(); Q.pop();
		for (int i = 0; i < p[k].size(); i++) {
			int Num = p[k][i];
			if (Equal(edgs[Num].cap,edgs[Num].flow)) continue;
			int to = edgs[Num].to;
			if (vis[to] == cnt) continue;
			D[to] = D[k] + 1; vis[to] = cnt;
			Q.push(to);
		}
	}
	return vis[T] == cnt;
}

DB Dicnic(int x,DB a)
{
	if (x == T || Equal(a,0.00)) return a;
	DB flow = 0.00;
	for (int &i = cur[x]; i < p[x].size(); i++) {
		int Num = p[x][i];
		if (Equal(edgs[Num].cap,edgs[Num].flow)) continue;
		if (D[edgs[Num].to] != D[x] + 1) continue;
		DB F = Dicnic(edgs[Num].to,min(a,edgs[Num].cap - edgs[Num].flow));
		if (F > 0.00) {
			flow += F;
			a -= F;
			edgs[Num].flow += F;
			edgs[Num^1].flow -= F;
			if (Equal(a,0.00)) return flow;
		}
	} 
	return flow;
}

bool Judge(int x,DB now)
{
	DB tot = 0; co = 0;
	for (int i = 0; i < v[x].size(); i++) {
		int to = v[x][i],Num = num[x][i];
		DB t = f[to] + 1.00 - now;
		if (t > 0) Add(S,Num,t),tot += t;
		else Add(Num,T,-t);
		for (int j = 0; j < argue[Num].size(); j++) 
			Add(Num,argue[Num][j],INF);
	}
	
	while (BFS()) {
		for (int i = 0; i < num[x].size(); i++) cur[num[x][i]] = 0; cur[S] = cur[T] = 0;
		tot -= Dicnic(S,INF);
	}
	p[S].clear(); p[T].clear();
	for (int i = 0; i < v[x].size(); i++) p[num[x][i]].clear();
	return tot > 0; 
}

void Work(int k)
{
	DB L = 0,R = 500.00;
	for (int i = 0; i < 20; i++) {
		DB mid = (L+R) / 2.00;
		if (Judge(k,mid)) L = mid;
		else R = mid;
	}
	f[k] = L;
}

int main()
{
	#ifdef DMC
		freopen("DMC.txt","r",stdin);
    #else
		freopen("trip.in","r",stdin);
		freopen("trip.out","w",stdout);
	#endif
	
	cin >> n >> m >> k;
	for (int i = 1; i <= m; i++) {
		int x,y; scanf("%d%d",&x,&y);
		v[x].push_back(y); ++d[x];
		num[x].push_back(i); v2[y].push_back(x);
	}
	for (int i = 1; i <= k; i++) {
		int x,y; scanf("%d%d",&x,&y);
		argue[x].push_back(y);
		//argue[y].push_back(x);
	}
	
	for (int i = 1; i <= n; i++)
		if (!d[i]) q.push(i);
		
	while (!q.empty()) {
		int k = q.front(); q.pop();
		Work(k);
		for (int i = 0; i < v2[k].size(); i++) {
			int F = v2[k][i];
			--d[F];
			if (!d[F]) q.push(F);
		}
	}
	printf("%.5f",f[1]);
	return 0;
}



T2:

一次函数满足结合律不满足交换律,线段树维护一下即可

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<stack>
#include<cstdlib>
#include<cmath>
#include<algorithm>
using namespace std;

typedef long long LL;
const int maxn = 2E5 + 20;
const LL mo = 1000000007LL;

LL k[maxn*20],b[maxn*20];
int n,m;

void Insert(int o,int l,int r,int pos,LL K,LL B)
{
	if (l == r) {k[o] = K; b[o] = B; return;}
	int mid = (l+r) >> 1;
	if (pos <= mid) Insert(2*o,l,mid,pos,K,B);
	else Insert(2*o+1,mid+1,r,pos,K,B);
	k[o] = k[2*o]*k[2*o+1]%mo;
	b[o] = (k[2*o+1]*b[2*o]%mo + b[2*o+1])%mo;
}

int query(int o,int l,int r,int ql,int qr,LL x)
{
	if (ql <= l && r <= qr) return (k[o]*x%mo + b[o])%mo;
	int mid = (l+r) >> 1;
	int ret = x;
	if (ql <= mid) ret = query(2*o,l,mid,ql,qr,x);
	if (qr > mid) ret = query(2*o+1,mid+1,r,ql,qr,ret);
	return ret;
}

int main()
{
	#ifdef DMC
		freopen("DMC.txt","r",stdin);
    #else
		freopen("func.in","r",stdin);
		freopen("func.out","w",stdout);
	#endif
	
	cin >> n >> m;
	for (int i = 1; i <= n; i++) {
		int x,y; scanf("%d%d",&x,&y);
		Insert(1,1,n,i,x,y);
	}
	
	while (m--) {
		char ch = getchar();
		while (ch != 'Q' && ch != 'M') ch = getchar();
		if (ch == 'Q') {
			int l,r,x; scanf("%d%d%d",&l,&r,&x);
			printf("%d\n",query(1,1,n,l,r,x));
		} 
		else {
			int po,x,y; scanf("%d%d%d",&po,&x,&y);
			Insert(1,1,n,po,x,y);
		}
	}
	return 0;
}


T3:

求五维偏序

对于每科成绩的每个点维护一个bitset(比他低的标1比他高的标0),对于金将军的每次成绩,二分各科所在的bitset的位置,将五个bitset &,统计1的个数就是答案

但是,开不下n^2个bitset,于是每科开sqrt(n)个bitsest,二分所在后剩下一点位置暴力一下,(细节见代码),复杂度挺棒的

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<stack>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<bitset>
using namespace std;

const int maxn = 5E4 + 50;
const int maxm = 300;

struct S{
	int score,rank;
	bool operator < (const S &b) const {
		return score < b.score;
	}
}s[5][maxn];

int n,m,T,B,tot,bl[maxn],br[maxn],King[5];

bitset <maxn> bi[5][maxm];
bitset <maxn> Ans,ans[5];

int Read()
{
	int ret = 0;
	char ch = getchar();
	while (ch < '0' || '9' < ch) ch = getchar();
	while ('0' <= ch && ch <= '9') ret = ret*10 + ch - '0',ch = getchar();
	return ret;
}

int main()
{
	#ifdef DMC
		freopen("DMC.txt","r",stdin);
		freopen("test.txt","w",stdout); 
    #else
		freopen("score.in","r",stdin);
		freopen("score.out","w",stdout);
	#endif
	
	T = Read();
	while (T--) {
		n = Read(); m = Read(); B = sqrt(n);
		for (int i = 1; i <= n; i++)
			for (int j = 0; j < 5; j++)
				s[j][i].score = Read(),s[j][i].rank = i;
		tot = n/B; if (n%B != 0) ++tot;
		bl[1] = 1; br[tot] = n; br[1] = B;
		for (int i = 2; i <= tot; i++) bl[i] = bl[i-1] + B;
		for (int i = 2; i < tot; i++) br[i] = br[i-1] + B;
		for (int i = 0; i < 5; i++) {
			sort(s[i] + 1,s[i] + n + 1);
			int po = 1; bi[i][1].reset();
			for (int j = 1; j <= n; j++) {
				if (j > br[po]) bi[i][po+1] = bi[i][po],++po;
				bi[i][po][s[i][j].rank] = 1;
			}
		}
		 
		int Q,Lastans = 0; Q = Read();
		while (Q--) {
			for (int i = 0; i < 5; i++) King[i] = Read() ^ Lastans;
			Ans.set();
			for (int k = 0; k < 5; k++) {
				int L = 1,R = n;
				while (R - L > 1) {
					int mid = (L+R) >> 1;
					if (s[k][mid].score <= King[k]) L = mid;
					else R = mid;
				}
				int pos = s[k][R].score <= King[k]?R:L;
				if (s[k][L].score > King[k]) pos = 0; 
				ans[k] = bi[k][pos/B];
				if (pos%B != 0) {
					int P = pos/B + 1;
					for (int i = bl[P]; i <= pos; i++)
						ans[k][s[k][i].rank] = 1;
				}
				Ans &= ans[k];
			} 
			printf("%d\n",Lastans = Ans.count());
		}
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值