【UOJ】UOJ Easy Round #8

记错时间了,,,开始了一个小时才进来orz,然后第一题第一次做这种通信题,发现挺有趣的,和平时做的题思维都不太一样,考后发现自己算法是假的,只有50,然后B题只会暴力网络流(实施证明暴力网络流只给了5分orz,以为网络流很好可以过20的,就没有写DP),特判分又写挂。然后第三题感觉似乎可做,问先序遍历哪个其实就是序列的删除和插入嘛,然后,,十分自闭地想了50多分钟怎么处理k级祖先。按照常理,维护好了先序遍历和已知中序遍历应该处理出k级祖先应该不难的啊,然而orz,也没来得及写暴力了.最后菜鸡55分,rank50+收场,果然还是太菜了orz.

UER #8  题解

A题 打雪仗

题意:两个人在通信(就是这边的输入等于那边的输出,那边的输出等于这边的输入,提交两个程序),A有一个长度2000的01字符串,B有1000个下标,B顺序输出下标对应的所有字符串,通信两边都不能超过1350,只能输出0或1. 想法十分有趣,我们把2000长度分成三块,那么对于B一定知道哪块里面有超过块长一半的答案(即>334),这样的话,他只要传达消息给A让他把这一块全部发过来,然后B就一一把剩下块的每个字符是不是答案发给A,对于A,还需要发的一定不到2n/3,总共发送4/3n,对于B,要完全发送剩下两块,也恰好是4/3n,这样,我们就卡着死亡边缘通过了此题。 alice :
#include <iostream>
#include <fstream>
#include <string>

using namespace std;

ifstream fin;
char get_bit() {
	return getchar();
}
void send_bit(char ch) {
	putchar(ch);
	fflush(stdout);
}

int n, m;
string s;
struct init_t {
	init_t() {
		fin.open("alice.in");
		fin >> n >> m >> s;
	}
} init_t;

int main() {
	string s1,s2,s3;
	s1 = s.substr(0,668); s2 = s.substr(668,668); s3 = s.substr(1336,664);
	char t1 = get_bit(); char t2 = get_bit();
	int t = 1;
	if(t1=='0'&&t2=='1') {
		t = 1;
	} else if(t1=='1'&&t2=='0') {
		t = 2;
		swap(s1,s2);
	} else {
		t = 3;
		swap(s1,s3);
	}
	for(int i=0;i<s1.size();i++) {
		send_bit(s1[i]);
	}
	for(int i=0;i<s2.size();i++) {
		char o = get_bit();
		if(o=='1') send_bit(s2[i]);
	}
	for(int i=0;i<s3.size();i++) {
		char o = get_bit();
		if(o=='1') send_bit(s3[i]);
	}
}
Bob:
#include <iostream>
#include <fstream>
#include <string>
#include<cstring>
using namespace std;

ifstream fin;
char get_bit() {
	return getchar();
}
void send_bit(char ch) {
	putchar(ch);
	fflush(stdout);
}
ofstream fout;
void answer(string s) {
	fout << s << endl, exit(0);
}

const int N = 1000;

int n, m, pos[N + 1];
struct init_t {
	init_t() {
		int x;
		fin.open("bob.in");
		fout.open("bob.out");
		fin >> n >> m;
		for (x = 1; x <= n; ++x) fin >> pos[x];
	}
} init_t;

int getcnt(string s) {
	int ans = 0;
	int le = s.size();
	for(int i=0;i<le;i++) if(s[i]=='1') ans++;
	return ans;
}

int main() {
	string s;
	string s1,s2,s3;
	for(int i=1;i<=2*n;i++) s+='0';
	for(int i=1;i<=n;i++) {
		s[pos[i]-1]='1';
	}
	s1 = s.substr(0,668); s2 = s.substr(668,668); s3 = s.substr(1336,664);
	int t;
	if(getcnt(s1)>334) {
		t = 1;
		send_bit('0'); send_bit('1');
	} else if(getcnt(s2)>334) {
		t = 2;
		swap(s1,s2);
		send_bit('1'); send_bit('0');
	} else {
		swap(s1,s3);
		t = 3;
		send_bit('1'); send_bit('1');
	}
	string ans1,ans2,ans3;
	for(int i=0;i<s1.size();i++) {
		char t = get_bit();
		if(s1[i]=='1') ans1+=t;
	}
	for(int i=0;i<s2.size();i++) {
		send_bit(s2[i]);
		if(s2[i]=='1') ans2+=get_bit();
	}
	for(int i=0;i<s3.size();i++) {
		send_bit(s3[i]);
		if(s3[i]=='1') ans3+=get_bit();
	}
	if(t==2) swap(ans1,ans2);
	if(t==3) swap(ans1,ans3);
	string ans = ans1 + ans2 + ans3;
	answer(ans);
}
 

B题 雪灾与外卖

题意:n个人m个商店 n m 1e5 , 每个人和每个商店都有坐标,每一个人都要走到一个商店里去,商店可以进的人有个数限制,且不同商店进一个人花的代价不同。总代价为n个人移动的总距离+总进入商店花的代价(题目可以看成一个费用流) 一个很神的后悔堆orz考场网络流瞬间爆炸。考虑将餐厅和送餐员一起放在坐标轴上排序之后,每次送餐员都考虑一个向左边的一个食品店进行匹配,从食品堆提出w-yi最小的(因为从左向右考虑就不需要绝对值)然后把反悔放进送餐员堆里,然后扫到餐厅的时候,看会不会比当前的以匹配的食品店堆顶好,如果好就再考虑替换掉前面的送餐员,然后放进食品店堆里。这时候,我们如果再将送餐员再次反悔,发现时间复杂度就不对了,因为一个送餐员被反悔替换之后又可能被反悔替换了多次。 事实上,我们的送餐员不需要再次反悔。感性理解就是我们选择两个餐厅的时候,反悔进入堆的东西是等权的(因为送餐员是不用权的,距离是同等生效的)
#include<stdio.h>
#include<cstdio>
#include<queue>
#include<algorithm>
#include<iostream>
#include<vector>
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef pair<ll,ll> pr;
priority_queue<pr,vector<pr>,greater<pr> >man,food;
const int maxn = 2e5+5;
int n,m;
ll ans = 0;
int x[maxn],y[maxn],w[maxn],c[maxn];
pr ve[maxn]; int tp;
int main() {
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) {
		scanf("%d",&x[i]);
		ve[++tp] = pr(x[i],0);
	}
	ll sm = 0;
	for(int i=1;i<=m;i++) {
		scanf("%d%d%d",&y[i],&w[i],&c[i]);
		if(c[i]>0) ve[++tp] = pr(y[i],i);
		sm += c[i];
	}
	if(sm<n) { puts("-1"); return 0; }
	food.push(pr(1e18,n));
	sort(ve+1,ve+1+tp);
	for(int i=1;i<=tp;i++) {
		pr now = ve[i];
		if(now.se == 0 ) {
			pr tt = food.top(); food.pop();
			ans += now.fi + tt.fi;
			if(--tt.se>0) food.push(tt);
			man.push( pr(-now.fi*2-tt.fi, 1 ) );
		} else {
			ll cc = c[now.se]; ll ww = w[now.se];
			if(food.top().fi+now.fi>ww) {
				ans += ww*cc;
				man.push(pr(-ww-now.fi,cc));
				while(cc) {
					pr orz = man.top();
					man.pop();
					ll oo = min(cc,orz.se);
					cc-=oo; orz.se-=oo;
					ans += oo*(now.fi+orz.fi);
					if(orz.se) man.push(orz);
					food.push(pr(-now.fi*2-orz.fi,oo));
				}
			} else food.push(pr(ww-now.fi,cc)); 
		}
	}
	printf("%lld",ans);
}
 

C题 许愿树和圣诞树

题意:n1e5给一颗中序遍历1->n连续的树,改变为选择一个点单旋到另一个的下面当儿子,询问1.x的k级祖先 2,先序遍历为k的点编号。 暂待填坑(随时咕咕咕)
posted @ 2018-12-22 20:32  Newuser233  阅读(6)  评论(0)    收藏  举报