2016.4.17 南理工校赛题解

偷吃糖果

Time Limit: 1000Ms

Memory Limit: 65536KB

Description

小鱼喜欢吃糖果。他有两盒糖果,两盒糖果分别仅由小写字母组成的字符串s和字符串t构成。其中'a'到'z'表示具体的某种糖果类别。他原本打算送给他喜欢的女生,但是要送给女孩子的话两盒糖果不能有差别 (即字符串s和t完全相同)。所以,他决定偷吃几块,他吃糖果的策略是每次选出一盒糖果中两个连续的同种类别的糖果,然后吃掉其中一块。 该策略可以使用多次。例如一盒糖果是'rrrjj',他可以把这盒糖果变成'rrjj'或者'rrrj'。现在你要告诉小鱼,经过他多次偷吃糖果之后,两盒糖果能否送给他喜欢的女孩子。如果可以输出'Yes',如果不行输出'No'。

Input

第一行一个T,表示T组测试数据。每组测试数据格式如下。第一行表示字符串s,第二行表示字符串t。1 ≤ T ≤ 100Each character of s, t will be between 'a' and 'z'.1 ≤ length of string s ≤ 10001 ≤ length of string t ≤ 1000

Output

对于每组测试数据,输出Yes,或者No。

Sample Input

2
rrrjj
rrrj
rj
jr

Sample Output

Yes
No

Hint

题目中的第一个样例:
第一盒糖果:rrrjj -> rrjj -> rjj -> rj
第二盒糖果:rrrj -> rrj -> rj
删除两个字符串所有连续重复字符,只剩下一个。在判定两个是否相同

#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>
#include <cstring>
#include <string>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <stack>

using namespace std;

string remove_duplicates(string s) {
    return string(s.begin(), unique(s.begin(), s.end()));
}

int main(){
  int T;
  cin >> T;
  string s,t;
  while (T--) {
    cin >> s;
    s = remove_duplicates(s);
    cin >> t;
    t = remove_duplicates(t);
    if (s.compare(t) == 0){
      puts("Yes");
    }else {
      puts("No");
    }
    // cout << s << endl << t<< endl;;
  }
}

banana

Time Limit: 1000ms

Memory Limit: 65536KB

Description

给出两个点P、Q,点A绕着点P顺时针旋转,线段BC绕着点Q顺时针旋转。点A与线段BC的角速度相等,问在旋转的过程中有没有可能某一瞬间点A在线段BC上。

Input

第一行输入一个整数T(1 <= T <= 100),表示有T组数据,对于每组数据,有2行。第一行表示点P和点A的坐标;第二行表示点Q,和线段两个端点B,C的坐标(-200 <= 对于所有的x、y <= 200)。

Output

如果可能相交,则输出yes,否则输出no。

Sample Input

2
0.0000000000 0.0000000000 -3.0000000000 -3.0000000000
5.0000000000 5.0000000000 6.0000000000 6.0000000000 8.0000000000 8.0000000000
0.0000000000 0.0000000000 -2.0000000000 -2.0000000000
5.0000000000 5.0000000000 6.0000000000 6.0000000000 7.0000000000 7.0000000000

Sample Output

yes
no



相交的充要条件为:
如果∠BCA’或∠A’BC有一个是钝角,则min(|BA’|, |CA’|) <= |AA’| <= max(|BA’|, |CA’|);
否则,|C’A’| <= |AA’| <= max(|BA’|, |CA’|)
中等想法题

/*======================================================
# Author: whai
# Last modified: 2016-01-09 17:29
# Filename: banana.cpp
======================================================*/
#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>
#include <cstring>
#include <string>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <stack>

using namespace std;

#define LL long long
#define PB push_back
#define X first
#define Y second

const double eps = 1e-8;

struct Point {
	double x, y;
	Point() {}
	Point(double _x, double _y) { x = _x; y = _y; }
	void scan() {
		scanf("%lf%lf", &x, &y);
	}
	Point operator + (Point a) {
		return Point(x + a.x, y + a.y);
	}
	Point operator - (Point a) {
		return Point(x - a.x, y - a.y);
	}
	double operator * (Point a) {
		return x * a.x + y * a.y;
	}
	double operator ^ (Point a) {
		return x * a.y - y * a.x;
	}
	Point rotate() {
		return Point(-y, x);
	}
	double len() {
		return *this * *this;
	}
};

Point P, Q;
Point A[1], B[2];

int sgn(double x) {
	if (x == 0) return 0;
	if (x < 0) return -1;
	return 1;
}

double get_min_d(Point O, Point C, Point D) {
	double min_d = min((O - C).len(), (O - D).len());
	Point OC = C - O;
	Point OD = D - O;
	Point OH = (D - C).rotate();
	if(sgn(OH ^ OC) * sgn(OH ^ OD) != -1) //trick
		return min_d;
	double s = fabs(OC ^ OD);
	double h2 = s * s / (C - D).len();
	return h2;
}

bool gao(Point O, Point C, Point D, double R) {
	double max_d = max((O - C).len(), (O - D).len());
	double min_d = get_min_d(O, C, D);

	//cerr<<min_d<<' '<<R<<' '<<max_d<<endl;
	if(min_d - eps <= R && R <= max_d + eps)
		return true;

	return false;
}

int main() {
	int T;
	cin>>T;
	while(T--) {
		P.scan(); A[0].scan();
		Q.scan(); B[0].scan(); B[1].scan();
		Point PQ = Q - P;
		double R = PQ.len();
		Point O = A[0] + PQ;
		bool ans = gao(O, B[0], B[1], R);
		if(ans) {
			puts("yes");
		} else {
			//cerr<<P.x<<' '<<P.y<<' '<<A[0].x<<' '<<A[0].y<<endl;
			//cerr<<Q.x<<' '<<Q.y<<' '<<B[0].x<<' '<<B[0].y<<' '<<B[1].x<<' '<<B[1].y<<endl;
			puts("no");
		}
	}
	return 0;
}

count_prime

Time Limit: 1000ms

Memory Limit: 65536KB

Description

给定你一个数n,请你统计出在[a,b]这个区间中和n互质的数的个数。两个数互质当且仅当他们除了1之外没有其他的公共因子或者他们最大的公共因子是1。1和任何数是互素的。

Input

第一行输入一个整数T(1 <= T <= 100),表示T组测试数据。接下来T行,每行3个整数a,b,n(1 <= a <=b <=10^15, 1<= n <= 10^9),用空格隔开。

Output

输出一个整数表示 和n互质 的数的个数。

Sample Input

2
1 10 2
3 10 5

Sample Output

5
6

容斥原理、先对n分解质因数,分别记录每个质因数,那么所求区间内与某个质因数不互质的个数就是n/r(i),假设r(i)是r的某个质因子 假设只有三个质因子,总的不互质的个数应该为p1+p2+p3-p1 p2-p1 p3-p2 p3+p1 p2*p3, 及容斥原理,pi代表n/r(i),即与某个质因子不互质的数的个数,当有更多个质因子的时候,可以用状态压缩解决,二进制位上是1表示这个质因子被取进去了。如果有奇数个1就相加,反之则相减。
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long ll;
int T,cas;
ll a,b,n;
vector<ll> adj;

ll solve(ll x,ll n)
{
    adj.clear();
    ll i,j;
    for (i=2;i*i<=n;i++)     
        if (n%i==0)
        {
            adj.push_back(i);
            while (n%i==0)
                n/=i;
        }
    if (n>1)
        adj.push_back(n);

    ll sum=0,value,cnt;
    for (i=1;i<(1<<adj.size());i++)  
    {
        value=1;
        cnt=0;
        for (j=0;j<adj.size();j++)
            if (i&(1<<j))      
            {
                value*=adj[j];
                cnt++;
            }
        if (cnt&1)      
        	sum+=x/value;
        else
        	sum-=x/value;
    }
    return x-sum;
}

int main()
{
   	//cas=0;
    scanf("%d",&T);
    while (T--)
    {
        scanf("%lld%lld%lld",&a,&b,&n);
        printf("%lld\n",solve(b,n)-solve(a-1,n));
    }
    return 0;
}


triple

Time Limit: 3000MS

Memory Limit: 65536KB

Description

给出一个整数n,表示1,2,...,n。从这n个数中任意选择3个不同的数字x,y,z,问x,y,z的最大公约数等于m的方案有多少种?(注意:(1,2,3),(1,3,2),(2,1,3),(2,3,1),(3,1,2),(3,2,1)属于同一种方案)

Input

第一行输入一个整数T(1 <= T <= 100),表示有T组数据,接下来T行,每行2个整数n, m(1 <= m <= n <= 10^5)

Output

输出一个整数表示答案

Sample Input

1
5 1

Sample Output

10


简单的莫比乌斯反演
令f(d)表示x,y,z的gcd=d的个数
则F(d)表示x,y,z的gcd是d的倍数的个数

/*======================================================
# Author: whai
# Last modified: 2016-01-11 13:13
# Filename: triple.cpp
======================================================*/
#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>
#include <cstring>
#include <string>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <stack>

using namespace std;

#define LL long long
#define PB push_back
#define P pair<int, int>
#define X first
#define Y second

const int N = 1e5 + 5;
int mu[N];
void getMu() {
	for (int i = 1; i < N; ++i) {
		int target = i == 1 ? 1 : 0;
		int delta = target - mu[i];
		mu[i] = delta;
		for (int j = 2 * i; j < N; j += i)
			mu[j] += delta;
	}
}

int cnt[N];

void deal(int x) {
	for(int i = 1; i * i <= x; ++i) {
		if(x % i == 0) {
			++cnt[i];
			int tmp = x / i;
			if(tmp != i) {
				++cnt[tmp];
			}
		}
	}
}

LL F(int x) {
	LL tmp = cnt[x];
	if(tmp < 3) return 0;
	return tmp * (tmp - 1) * (tmp - 2) / 6;
}

void gao(int n, int m) {
	LL ans = 0;
	for(int i = 1; i * m < N; ++i) {
		ans += mu[i] * F(i * m);
	}
	cout<<ans<<endl;
}

void init() {
	memset(cnt, 0, sizeof(cnt));
}

int main() {
	getMu();
	int T;
	scanf("%d", &T);
	while(T--) {
		int n, m;
		scanf("%d%d", &n, &m);
		init();
		for(int i = 1; i <= n; ++i) {
			deal(i);
		}
		gao(n, m);
	}
	return 0;
}

sad

Time Limit: 8000MS

Memory Limit: 65536KB

Description

金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间他自己专用的很宽敞的房间。更让他高兴的是,妈妈昨天对他说:“你的房间需要购买哪些物品,怎么布置,你说了算”。今天一早金明就开始做准备,但是他想买的东西太多了,共N件。于是,他决定分组买这些东西。他把每件物品规定了一个重要度C,此外他还从因特网上查到了每件物品的价格(都是整数元)F,并且给每件物品都编了号,为1到N。金明的分组规则如下:1、每一组中的物品编号必须连续,即要类似这样分:(1,2,....i),(i+1,i+1,...j)....(k,k+1...n)2、对于任意一对编号(i,j i<j且1<=i,j<=n),如果物品i被分在p组,j被分在在q组中,且(p<q),那么必须要有Fi>Cj。3、对于每一组K,金明定义这一组中物品的重要度的最大值为这一组的重要度Mi,这一组中物品所有物品价格之和为这一组的价格Si。在妈妈的限制下,所有的组的Mi之和不能超过一个上限limit。而金明在分组的过程中则希望所有的Si中的最大值又要尽量小,这样妈妈就不会太在意钞票这个问题了,哈哈哈哈,现在的问题是请你帮助金明设计一个满足要求的分组单。

Input

多组测试数据。对于每一组测试数据:第一行包括两个正整数N,limit(0<=N<=10^5,limit<=2^31-1)接下来的N行每行包括两个正整数Ci,Fi分别表示第i件物品的价格和重要度(0<= Ci, Fi<=10000)

Output

为了简单起见,金明不需要你输出整个分组单啦,输出你的分组中最大的S就行了。如果没有合适的方案请输出 -1每个Case占一行

Sample Input

4 6
4 3
3 5
2 5
2 4

Sample Output

9

Hint

n=0输出0


三个条件一个一个干

对于第一个和第二个条件,其实就是说有一些组必须在一起
那我们就让他们在一起(在一起!在一起!)。。。。
因为他要求每一对都要满足fi, ci所以搞一个最大值的后缀就好了。。。
具体见标程reset函数

然后就是第三个条件和最后要求的东西
显然直觉上二分,二分答案S
然后题目就变成了,所有分组的的最大值的和需要尽量小ANS。
并且在这个尽量小的情况下,判断一下ANS和limit的大小
logn

最后,那么得到 【所有分组的最大值的和的最小值】 呢?
显然DP,并且DP答案是单调上升的,然后单调队列优化。
由于过程中有删除插入操作所以上multiset。当然你可以手敲平衡树。
具体就是:
设DP[i]为,当到i为止的 【分组的最大值的和的最小值】。
那么有: dp[i] = min(dp[j] + max(C[j+1], ..., C[i])) (j<i) 然后因为有二分出的答案金钱的限制,所以j也有限制,先假设这里 j >= head
而 F[head-1] + F[head] + F[head+1] + ... + F[i] > S (我们二分的答案金钱上限)
对于head ~ i因为每一个DP要求的是区段的最大值,所以我们维护一个递减的队列q,
记录一个递减的ci
c[q[l]] > c[q[l+1]] > ....c[q[r]]
而任何在head ~ i之间的dp[j] + max(xx)这个max(xx)都在这个序列中。
显然这时候,因为dp[i]是一个递增的序列,所以对于每一个q[i],总是与尽量前面的dp[i]合体,能得带更小的值。
dp[i] = min(c[q[l]] + dp[head-1],
c[q[l+1]] + dp[que[l]], c[q[l+2]] + dp[que[l+1]], ..., c[que[r]]+dp[que[r-1]]) ;
而这个东西用set维护,与队列共存
nlogn

总复杂度 n(logn)(logn)

#include <iostream>
#include <string>
#include <string.h>
#include <cstdio>
#include <set>
// #include <ctime>

using namespace std;
const int maxn = 1e5+5;
const int inf = 1e9+7;

int n, limit;
int c[maxn], f[maxn];

void reset(){
	static int c_max[maxn], cc[maxn], ff[maxn];

	c_max[n+1] = -1;
	for(int i=n;i>=1;i--)
		c_max[i] = max(c[i], c_max[i+1]);

	int nn = 0, f_min = inf, st = 1;
	for(int i=1;i<=n;i++){
		f_min = min(f_min, f[i]);

		if ( f_min > c_max[i+1] || i==n ){
			nn++; cc[nn] = 0, ff[nn] = 0;
			for(int j=st;j<=i;j++){
				cc[nn] = max(cc[nn], c[j]);
				ff[nn] += f[j];
			}
			st = i+1; f_min = inf;
		}
	}

	n = nn;
	for(int i=1;i<=n;i++)
		c[i] = cc[i], f[i] = ff[i];
}

bool test(int s){
	static int dp[maxn];
	static int que[maxn], l, r;
	static multiset<int> myset;

	for(int i=1;i<=n;i++)
		if ( f[i]>s ) return false;

	r = 0, l = 1; myset.clear();
	int sumf = 0, head = 1;  dp[0] = 0;
	for(int i=1;i<=n;i++){
		sumf += f[i];
		// ->
		while ( head<i && sumf>s ) {
			sumf -= f[head++];
		}

		// <-
		while ( l<=r && c[que[r]]<=c[i] ){
			if ( l<r )
				myset.erase( dp[que[r-1]] + c[que[r]] );
			r--;
		}
		if( l<=r )
			myset.insert( dp[que[r]] + c[i] );
		que[++r] = i;

		// ->
		while ( l<=r && que[l]<head ){
			if ( l<r )
				myset.erase( dp[que[l]] + c[que[l+1]] );
			l++;
		}

		dp[i] = dp[head-1] + c[ que[l] ];
		if ( !myset.empty() )
			dp[i] = min(dp[i], *myset.begin() );
	}

	return dp[n] <= limit ;
}

int main(){
	// double st = clock();
	while ( scanf("%d%d", &n, &limit)!=EOF ){
		for(int i=1;i<=n;i++)
			scanf( "%d%d", &c[i], &f[i] );

		if ( n==0 ){
			puts("0"); continue;
		}

		reset();

		int l = 1, r = 0, mid;
		for(int i=1;i<=n;i++) r += f[i];
		while ( l<r-1 ){
			mid = (l+r)>>1;
			if ( test(mid) ) r = mid;
			else l = mid;
		}

		if ( test(r) ) mid = r;
		else if ( test(l) ) mid = l;
		else mid = -1;
		printf("%d\n", mid);
	}
	// double en = clock();
	// cerr<<(en - st) / CLOCKS_PER_SEC<<endl;

	return 0;
}

sequence

Time Limit: 1000MS

Memory Limit: 65536KB

Description

将一个给定的数列,拆分成K个不降序列,每个数出现且只出现一次,且在各序列中各个数相对于原数列的相对顺序不变。如7 6 9 8 10可以拆成 7 9 10和6 8。求最小的K值。

Input

第一行输入一个整数T(1 <= T <= 100),表示接下来T组测试数据,每组两行,第一行为n,代表数列长度(1<=n<=10000)接下来一行有n个数,空格分隔(每个数<=50000)。

Output

对每组数据输出一个最小的K值。

Sample Input

2
5
7 6 9 8 10
5
5 4 3 2 1

Sample Output

2
5


经典贪心问题,从前往后扫描原数组,每到一个数,找个当前最大值最大的不降数组加入其中,否则新建一个不降数组。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <map>
#include <queue>
#include <stack>
#include <cmath>
using namespace std;
int height[10000];
int main()
{
	int T;cin>>T;
	while(T--)
	{
		int n;cin>>n;
		int number;
		int index = 0;
		while(n--)
		{
			scanf("%d",&number);
			int tmp_index = -1;
			for (int i = 0; i < index; ++i)
			{
				if(number>=height[i] && (tmp_index == -1 || height[tmp_index] < height[i]))
				{
					tmp_index = i;
				}
			}
			if(tmp_index != -1)
			{
				height[tmp_index] = number;
			}
			else height[index++] = number; 
		}
		cout<<index<<endl;
	}
}

琪露诺的算术教室

Time Limit: 1000ms

Memory Limit: 65536KB

Description

给出一个非负整数A,将这个数字的最低位移动到最高位(原来的最高位变为次高位,次低位变成最低位),得到非负整数B,发现B恰好是A的k倍。现给出A的最低位的值n,和倍数k,求最小的非负整数B。

Input

第一行输入一个正整数T(1 <= T <= 1000),表示有T组测试数据。对于每组测试数据:输入两个整数n,k(0<=n<=9 , 0<=k<=9)。

Output

对于每组测试数据,输出一个非负整数B,若无解,请输出-1。

Sample Input

1
2 2

Sample Output

210526315789473684

首先,从最后一位开始,依次 k,注意进位,就可以很快的找到模拟的思路。
然后,从尾数范围10
进位范围10 = 100可知。答案应该是循环的,我们用vis数组标记即可。
最后有三个错的比较多的地方:
1.B的次高位不能是0,也就说A的最高位不能是0.
2.循环什么时候结束,需要仔细斟酌一下.
3.n=0的情况,n=1的情况,和k=0的情况 请仔细考虑。

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;

int ans[1005];
bool vis[10][10];
int n,k;
int main(){
	int cases;
	scanf("%d",&cases);

	for (int cas=1;cas<=cases;cas++){
		scanf("%d%d",&n,&k);
		memset(ans,0,sizeof(ans));

		int top=0;
		int x=n*k %10;
		int y=n*k /10;
		y+=(ans[top]+x)/10;
		x=(ans[top]+x)%10;
		//cout<<n<<'+'<<k<<endl;
		memset(vis,false,sizeof(vis));
		bool flag=true;
		while (y!=0 || x!=n || (top>0 && ans[top-1]==0)){
			ans[top]=x;
			ans[top+1]=y;
			if (vis[ans[top]][y]){
				flag=false;
				break;
			}
			vis[ans[top]][y]=true;
			x=ans[top]*k %10;
			y=ans[top]*k /10;
			top++;
			y+=(ans[top]+x)/10;
			x=(ans[top]+x)%10;
		}
		ans[top]=x;
		ans[top+1]=y;
		top++;
		if (!flag){
			puts("-1");
			continue;
		}
		for (int i=top-1;i>=0;i--)
			printf("%d",ans[i]);
		putchar('\n');
	}
	return 0;
}

谁才是最强战舰!

Time Limit: 1000MS

Memory Limit: 65536KB

Description

依阿华来到镇守府的第一件事情,就是找大和solo!然而这并不是什么好消息,说不定,镇守府,甚至佐伯湾就这么消失了。。。于是,提督君想了一个简单的游戏,来分出她们的胜负。游戏规则如下:这里有N堆石子,每堆石子有a[i](1<=i<=N)个,每人轮流从其中的某一堆石子中拿出任意个石子(只能在其中一堆拿,不能不拿),大和先手,谁拿出了最后一个石子,谁输。若大和必胜,输出“Yamato_Saikou!”,若依阿华必胜,输出“Meidikeji_Shijiediyi!”,若两边都无法必胜,输出“Sayonara_Konosekai!”.

Input

第一行输入一个正整数T(1 <= T <= 1000),表示有T组测试数据。对于每组测试数据:第一行一个正整数,N(N<=1000),表示有N堆石子。第二行N个整数a[i](1<=a[i]<=1000),表示每堆石子的数量。

Output

若大和必胜,输出“Yamato_Saikou!”,若依阿华必胜,输出“Meidikeji_Shijiediyi!”,若两边都无法必胜,输出“Sayonara_Konosekai!”.

Sample Input

3
1
5
2
1 2
3
1 1 1

Sample Output

Yamato_Saikou!
Yamato_Saikou!
Meidikeji_Shijiediyi!


奇异局势,所有堆xor和==0.

假定S是非奇异局势,T是奇异局势。
一堆中石子数量>=2,表示充裕堆, =1表示孤单堆。

S0即非奇异局势下,充裕堆为0的状态
S1即非奇异局势下,充裕堆为1的状态
S2即非奇异局势下,充裕堆>=2的状态

T0即奇异局势下,充裕堆为0的状态
T2即奇异局势下,充裕堆>=2的状态

1.奇异局势的定义可知,S能转移到T,能转移到S, T只能转移到S

2.S0必败,T0必胜

3.S1必胜,因为S1只需要转移到S0即可。

4.S2必胜,T2必败。
1)T2只能转移到S1 和 S2
2)若T2转移到S1 则T2败,若T2转移到S2,S2只需要转回到T2即可。所以S2胜,T2败。

所以:
必胜态:T0,S1,S2
必败态:S0,T2

//必胜:T0,S1,S2
//必败:S0,T2
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
using namespace std;

int main(){
	int cases;
	scanf("%d",&cases);
	for (int cas=1;cas<=cases;cas++){
		int n; scanf("%d",&n);
		int sum=0,ones=0;
		for (int i=1;i<=n;i++){
			int x; scanf("%d",&x);
			sum^=x;
			if (x==1) ones++;
		}
		if (sum==0){
			if (n-ones==0) puts("Yamato_Saikou!");
			else puts("Meidikeji_Shijiediyi!");
		}
		else {
			if (n-ones==0){
				if (ones&1) puts("Meidikeji_Shijiediyi!");
				//必定是奇数堆,非奇异局势
			}
			else puts("Yamato_Saikou!");
		}
	}
	return 0;
}

puzzle

Time Limit: 1000MS

Memory Limit: 65536KB

Description

小明在玩一个闯关游戏,共n关, 必须按顺序通过,每一关会遇到两个数字(可能一样),他要选择其中一个,如果要选的这个数字是他之前选过的就直接过了这关。但是他如果在某一关选了数字X,那就不能在经过另一关时选择数字Y当X+Y=2*n-1时, 如果在某一关没有数可选的时候游戏结束 问在闯关游戏中小明最多可以通过几关。

Input

第一行输入一个整数T(1 <= T <= 100),表示接下来T组测试数据,每组测试数据第一行一个整数n(900<=n<=1000)接下来n行每行两个整数a,b代表每关遇到的数字(0<=a,b<=2*n-1)

Output

每组测试数据输出一个整数,小明最多的过关数。

Sample Input

2
3
0 0
1 1
2 2
3
0 0
1 1
5 5

Sample Output

3
2

首先二分一下可以闯过的关卡数,然后2sat建模就好

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <map>
#include <stack>
#include <assert.h>
using namespace std;
const int MAXN = 10000;
int n, m;
struct note
{
	int u, v, c;
	note() {}
	note(int u, int v, int c): u(u), v(v), c(c) {}
} p[MAXN];
int head[MAXN];
int next_[MAXN];
int dfn[MAXN];
int low[MAXN];
int instack[MAXN];
int belong[MAXN];
stack<int> sta;
int e = 0;
int indexx;
int cnt = 0;
int x[MAXN];
int y[MAXN];
void addnote(int u, int v, int c)
{
	p[e] = note(u, v, c);
	next_[e] = head[u];
	head[u] = e++;
}
void init()
{
	for (int i = 0; i < MAXN; ++i)
	{
		head[i] = next_[i] = -1;
	}
	while(!sta.empty()) sta.pop();
	memset(p, 0, sizeof(p));
	memset(dfn, 0, sizeof(dfn));
	memset(low, 0, sizeof(low));
	memset(belong, 0, sizeof(belong));
	memset(instack, 0, sizeof(instack));
	e = 0;
	cnt = 0;
	indexx = 0;
}
void tarjan(int u)
{
	//assert(u!=2*m);
	dfn[u] = low[u] = ++indexx;
	instack[u] = 1;
	sta.push(u);
	for (int i = head[u]; i != -1; i = next_[i])
	{
		int v = p[i].v;
		if (!dfn[v])
		{
			tarjan(v);
			low[u] = min(low[u], low[v]);
		}
		else if (instack[v])
			low[u] = min(low[u], dfn[v]);
	}
	if (dfn[u] == low[u])
	{
		int v;
		cnt++;
		do
		{
			v = sta.top();
			sta.pop();
			instack[v] = 0;
			belong[v] = cnt;
		} while (u != v);
	}
}
void solve()
{
	for (int i = 0; i < 2 * m; i++)
		if (!dfn[i])
			tarjan(i);
}
int main()
{
	int T; cin >> T;
	while (T--)
	{

		cin >> m;
		for (int i = 0; i < m; ++i)
		{
			scanf("%d %d", &x[i], &y[i]);
		}
		int lower = 0, upper = m;
		while (lower <= upper)
		{
			init();
			//cout<<"----------"<<endl;
			int mid = (lower + upper) >> 1;
			for (int i = 0; i < mid; ++i)
			{
				//printf("addnote(%d,%d)\n", 2 * n - 1 - x[i],y[i]);
				//printf("addnote(%d,%d)\n", 2 * n - 1 - y[i],x[i]);
				addnote(2 * m - 1 - x[i], y[i],1);
				addnote(2 * m - 1 - y[i], x[i],1);
			}
			solve();
			bool flag = true;
			for (int i = 0; i < m && flag; ++i)
			{
				//printf("belong[%d]=%d;belong[%d]=%d;\n",i,belong[i],2 * m - 1 - i,belong[2 * m - 1 - i]);
				if (belong[i] == belong[2 * m - 1 - i])
				{
					flag = false;
				}
			}
			if (flag) lower = mid + 1;
			else upper = mid - 1;
			//cout<<"----------"<<endl;
		}
		cout<<(lower+upper)/2<<endl;
	}
	return 0;
}

water1

Time Limit: 1000MS

Memory Limit: 65536KB

Description

听说全球气候变暖,冰川融化,海水淹大地。着实好奇要融化多少冰川会淹没我的宿舍,哦不,淹没南京,反正NJUST应该总会是第一批被淹的。现将问题简化成一个二维坐标系的模型。有N个矩形块区域,每个区域有海拔(H)和宽度(W),区域按照顺序给出,比如有3个区域,第一个区域宽度为1,海拔为2,第二个区域宽度为5,海拔为6,第三个区域宽度为3,海拔为4,从图像上看就是像这样:(Y轴是海拔)8 7 6 +++++5 +++++4 ++++++++3 ++++++++2 +++++++++1 +++++++++ 123456789假设最左边和最右边都是无限高的墙。为了简化问题,假设水流下来的速度是每秒一个单位的水,并且总是往区域一降水(某沿海地区)。现在请问要淹没所有的区域至少要多少时间(秒)?淹没的定义是,所有的区域表面至少覆盖一层水。如果没有区域则至少要放一滴水。上图例子中,淹没完如下:87 wwwwwwwww6 w+++++www5 w+++++www4 w++++++++3 w++++++++2 +++++++++1 +++++++++ 123456789所以需要19秒。

Input

多CASE,测试数据以EOF结尾,对于每个CASE:第一行一个整数N(0 <= N <= 10^5)。接下去N行每行对应两个整数H(1 <= H <= 10^5),W(1 <= W <= 10^5)分别对应第N个区域的海拔和宽度。

Output

一个整数,表示所需要的时间。

Sample Input

3
2 1 
6 5
4 3

Sample Output

19

找一个最高点h、、
答案就是
(h+1)*sum(wi) - 各海拔

#include <iostream>
#include <string>
#include <string.h>
#include <cstdio>

#define LL long long 
using namespace std;
const int maxn = 1e5+5;

int n;
int h[maxn],w[maxn];

int main(){

	while ( scanf("%d",&n)!=EOF ){
		if ( n==0 ){
			puts("1");
			continue;
		}

		int maxh = -1;
		for(int i=1;i<=n;i++){
			scanf("%d%d", &h[i], &w[i]);
			maxh = max(maxh, h[i]);
		}
		LL ans = 0;
		for(int i=1;i<=n;i++)
			ans += (LL)w[i] * (maxh+1 - h[i]);
		printf("%lld\n", ans);
	}

	return 0;
}


你可能感兴趣的:(2016.4.17 南理工校赛题解)