比赛链接:点击打开链接

A:点击打开链接

题意:

有2种矩阵1*x和2*x, 用最小的矩阵2*m来把这些框住,使得m最小,输出最小的m

输入:

n个矩阵

下面n行给出wi, xi, wi的取值只有1,2两种,且矩阵不能旋转重叠。

思路:

矩阵宽为2就直接加到答案上,所以只考虑矩阵宽为1。

dp[i]表示第一行能放的宽度,类似背包求出这个dp

然后if(dp[i] is ok) ans = min(ans, max(sum-i, i) );

#include <iostream>
#include <cstdio>
#include <set>
#include <cstring>
#include <map>
#include <algorithm>
#include <vector>

using namespace std;

vector<int>G;
int dp[10005];
int main() {
	int T, n; scanf("%d", &T);
	while (T-->0) {
		scanf("%d", &n);
		int ans = 0, i, j;
		G.clear();
		int sum = 0;
		while(n-->0){
			scanf("%d%d", &i, &j);
			if(i==2)ans+=j;
			else { sum += j; G.push_back(j); }
		}
		memset(dp, -1, sizeof dp);
		dp[0] = 0;
		for(int i = 0; i < G.size(); i++){
			for(int j = sum-G[i]; j>=0;j--)
			if(dp[j]!=-1)
			dp[j+G[i]] = 0;
		}
		int tmp = sum;
		for(int i = 0; i <= sum; i++)
		if(dp[i]!=-1)
		tmp = min(tmp, max(i, sum-i));
		printf("%d\n", ans+tmp);
	}
	return 0;
}


B: 点击打开链接

题意:

第一行输入n, (x, y), (c1, c2)

一个二维平面,开始人在(0,0),终点在(x, y), 平面上有2种地形:陆地和河流,在陆地上走花费为c1每单位,河流上走花费为c2每单位。

下面n行给出n条河流。

xi, wi, 河流是无限长,宽度为wi,且平行于y轴,从xi点开始。

思路:

把河流都集中在右边。

然后设这个人是在y=d穿过河流的,则这个人一定会走出2条折线。

列出花费与d的方程,会发现由2个带根号的式子相加。分别画出这两个式子的曲线,合成函数就是一个单峰函数,所以三分答案。

#include <cstdio>
#include <cmath>
using namespace std;

double sum, c1, c2, x, y;
double check(double d) {
	return c2*sqrt(d*d+sum*sum)+c1*sqrt((y-d)*(y-d)+(x-sum)*(x-sum));
}
int main() {
	int n;
	while (~scanf("%d", &n)) {
		scanf("%lf%lf%lf%lf", &x, &y, &c1, &c2);
		sum = 0;
		for(int i = 0, l, w; i < n; i ++) {
			scanf("%d%d", &l, &w);
			sum += w;
		}
		
		double l = 0, r = y;
		for(int i = 0; i < 100; i ++) {
			double lmid = l+(r-l)/3.0;
			double rmid = r-(r-l)/3.0;
			if(check(lmid) < check(rmid)) {
				r = rmid;
			} else {
				l = lmid;
			}
		}
		printf("%.2f\n", check(l));
	}
	return 0;
}

C: 点击打开链接

题意:

给定n条线段(保证是1-n首尾相接的) 常数D

从第一条线段左边开始,沿着线段每个长度D输出这个点的坐标(相当于一个人在线段上运动了D长度,就输出这个人的坐标)

若D大于所有线段长度,则输出No Such Points.

模拟一下。

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

using namespace std;

const int MAX_N = 1007;
const double pi = acos(-1.);

struct Point {
	double x, y, rad, len;
	Point() {
		
	}
	Point (double _x, double _y, double _r, double _l) {
		x = _x;
		y = _y;
		rad = _r;
		len = _l;
	}
};

int n;
double dist;
double sum[MAX_N];
Point p[MAX_N];

double dis(int i, int j) {
	return sqrt((p[i].x - p[j].x) * (p[i].x - p[j].x) + (p[i].y - p[j].y) * (p[i].y - p[j].y));
}

int main() {
	while (2 == scanf("%d%lf", &n, &dist)) {
		for (int i = 0; i <= n; ++i)
			scanf("%lf%lf", &p[i].x, &p[i].y);
		for (int i = 0; i < n; ++i) {
			p[i].rad = atan2(p[i + 1].y - p[i].y, p[i + 1].x - p[i].x);
			p[i].len = dis(i, i + 1);
		//	printf("%.f\n", 180 * p[i].rad / pi);
		}
		sum[0] = 0;
		for (int i = 1; i <= n; ++i)
			sum[i] = sum[i - 1] + p[i - 1].len;
		if (dist > sum[n]) {
			puts("No Such Points.");
			continue;
		}
		double nowD = dist;
		while (true) {
			int id = lower_bound(sum, sum + n + 1, nowD) - sum;
			if (id > n) break;
			double len = p[id - 1].len - (sum[id] - nowD);
			double nx = p[id - 1].x + len * cos(p[id - 1].rad);
			double ny = p[id - 1].y + len * sin(p[id - 1].rad);
			printf("%.2f,%.2f\n", nx, ny);
			nowD += dist;
		}
	}
	return 0;
}

/*
3 2.1
0.00 0.00
1.00 0.00
1.00 1.00
2 2

3 0.3
0.00 0.00
1.00 0.00
1.00 1.00
2 2

3 3.42
0.00 0.00
1.00 0.00
1.00 1.00
2 2

*/
D: 点击打开链接

题意:

给出3个长度相等且长度是偶数的字符串

在第一个串中取一半的字母,第二个串中取一半的字母,问任意顺序能否拼接出第三个串

思路:

首先要保证每种字母的个数和要大于第三个串对应的字符。

再统计出,对于每个字符第一个串最少需要贡献的个数和最多需要贡献的个数=>(l[i], r[i])

若忽略掉每个串恰好出一半的字母这个条件

则此时第一个串的每种字母任意出l[i],r[i]内都能保证 能够拼出第三个串

为了能拼出一半的字母:

26 个区间内任意出一个数使得和=n,因为区间是连续的,所以只需要把区间上下界相加,看是否包括 strlen/2 即可。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <string>
#include <cmath>
#include <cstring>
#include <queue>
#include <map>
#include <vector>
using namespace std;
const int N = 100000 + 10;

int len;
char s[3][N];
int a[3][26];
int l[26], r[26];
bool ok(){
	int las = 0, maxx = 0;
	for (int i = 0; i < 26; i++){
		if (a[0][i] + a[1][i] < a[2][i])return false;
		l[i] = max(0, a[2][i] - a[1][i]);
		r[i] = min(a[2][i], a[0][i]);
		maxx += r[i];
		las += l[i];
	}
	return las <= len / 2 && len / 2 <= maxx;
}
void cal(char *c, int*d){
	for (int i = 0; i < 26; i++)d[i] = 0;
	for (int i = 0; i < len; i++)d[c[i] - 'A']++;
}
int main() {
	while (~scanf("%s", s[0])){
		len = strlen(s[0]);
		scanf("%s", s[1]);
		scanf("%s", s[2]);
		for (int i = 0; i < 3; i++)cal(s[i], a[i]);
		ok() ? puts("YES") : puts("NO");
	}
	return 0;
}

E:点击打开链接

题意:

给出一个序列

删除任意一段连续的数(也可以不删除)

使得删完后 最长严格递增子段(序列必须是连续的)最长

输出这个长度

思路:

dp[i][0] 表示不删除时,i点作为序列最后一个数时最长长度

dp[i][1] 表示删除时,i点的最长长度

首先我们求出不删除时,每个点作为序列最后一个数时的最长长度:dp[i][0]

dp[i][1] 可以从 dp[i][0]转移来 (即不删除

if(a[i]>a[i-1]) dp[i][1] = dp[i-1][1]+1; 即从前一个点转移来

还有就是从[1, i-1] 中比 a[i]小的数且从未删除过的dp[j][0]+1 转移来 (j 满足 : a[j]<a[i] && j<i)

用线段树维护一下前i个点的dp[i][0]的值即可。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <string>
#include <cmath>
#include <cstring>
#include <queue>
#include <set>
#include <map>
#include <vector>
template <class T>
inline bool rd(T &ret) {
	char c; int sgn;
	if (c = getchar(), c == EOF) return 0;
	while (c != '-' && (c<'0' || c>'9')) c = getchar();
	sgn = (c == '-') ? -1 : 1;
	ret = (c == '-') ? 0 : (c - '0');
	while (c = getchar(), c >= '0'&&c <= '9') ret = ret * 10 + (c - '0');
	ret *= sgn;
	return 1;
}
template <class T>
inline void pt(T x) {
	if (x <0) {
		putchar('-');
		x = -x;
	}
	if (x>9) pt(x / 10);
	putchar(x % 10 + '0');
}
using namespace std;

typedef long long ll;
const int N = 10005;
#define L(x) tree[x].l
#define R(x) tree[x].r
#define Max(x) tree[x].max
#define ls (id<<1)
#define rs (id<<1|1)
struct node{
	int l, r, max, val;
}tree[N << 2];
void Up(int id){
	Max(id) = max(Max(ls), Max(rs));
}
void Down(int id){

}
void build(int l, int r, int id){
	L(id) = l; R(id) = r;
	Max(id) = 0;
	if (l == r)	return;

	int mid = (l + r) >> 1;
	build(l, mid, ls); build(mid + 1, r, rs);
	Up(id);
}
int query(int l, int r, int id){
	if (l == L(id) && R(id) == r)return Max(id);
	Down(id);
	int mid = (L(id) + R(id)) >> 1, ans;
	if (r <= mid)ans = query(l, r, ls);
	else if (mid < l)ans = query(l, r, rs);
	else {
		ans = max(query(l, mid, ls), query(mid + 1, r, rs));
	}
	Up(id);
	return ans;
}
void update(int pos, int val, int id){
	if (L(id) == R(id)){
		Max(id) = max(Max(id), val);
		return;
	}
	Down(id);
	int mid = (L(id) + R(id)) >> 1;
	if (pos <= mid)update(pos, val, ls);
	else update(pos, val, rs);
	Up(id);
}
int n, a[N], dp[N][2];

vector<int>G;
void input(){
	G.clear();
	for (int i = 1; i <= n; i++){
		rd(a[i]);
		G.push_back(a[i]);
	}
	sort(G.begin(), G.end());
	G.erase(unique(G.begin(), G.end()), G.end());
	for (int i = 1; i <= n; i++)a[i] = lower_bound(G.begin(), G.end(), a[i]) - G.begin() + 1;
}
int main() {
	while (~scanf("%d", &n)) {
		input();
		for (int i = 1; i <= n; i++){
			dp[i][0] = 1;
			if (i > 1 && a[i]>a[i - 1])
				dp[i][0] = dp[i - 1][0] + 1;
		}
		build(1, n, 1);
		int ans = 1;
		for (int i = 1; i <= n; i++){
			dp[i][1] = 1;
			if (a[i] != 1 && i>1){
				if (a[i]>a[i - 1])dp[i][1] = max(dp[i][1], dp[i - 1][1] + 1);
				dp[i][1] = max(dp[i][1], query(1, a[i] - 1, 1) + 1);
			}
			update(a[i], dp[i][0], 1);
			dp[i][1] = max(dp[i][1], dp[i][0]);
			ans = max(ans, max(dp[i][0], dp[i][1]));
		}
		printf("%d\n", ans);
	}
	return 0;
}
/*
5
0 0 0 1 0
6
1 2 1 1 2 2
6
1 2 1 4 2 3
8
1 2 3 4 5 6 7 8
*/
F: 点击打开链接

题意:

有n个人

下面n行给出每个人的权值

若2个人的权值和是素数则2个人可以结婚(饿。。不考虑性别,反正一个人只能和一个人结婚,且这个人不能是自己)

思路:

显然是一个二分匹配,米勒素数判一下,然后建一个无向的二分图,答案/2即可

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

using namespace std;typedef long long AL;

const int N = 107;

AL  cnt, gcd, lcm, sqr, A;

AL GCD(AL a, AL b) { return b ? GCD(b, a % b) : a; }

AL MultiMod(AL a, AL b, AL n) { // a * b % n
	AL res = 0;
	a %= n;
	while (b > 0) {
		if (b & 1) {
			res += a;
			if (res >= n) res -= n;
		}
		a <<= 1;
		if (a >= n) a -= n;
		b >>= 1;
	}
    return res;
}

AL QuickMod(AL a, AL b, AL n) { // a ^ b % n
	AL res = 1;
	a %= n;
	while (b > 0) {
		if (b & 1) res = MultiMod(res, a, n);
		a = MultiMod(a, a, n); b >>= 1;
	}
	return res;
}

bool MillarRabin(AL n) { // 判断是否素数
	if (n == 2 || n == 3 || n == 5 || n == 7 || n == 11) return true;
	if (n == 1 || !(n & 1) || !(n % 3) || !(n % 5) || !(n % 7) || !(n % 11)) return false;
	AL t = 0, m = n - 1, x, y;
	while (!(m & 1)) { m >>= 1; t++; }
	for (int i = 0; i < 10; i++) {
		AL a = rand() % (n - 2) + 2;
		x = QuickMod(a, m, n);
		for (AL j = 0; j < t; j++) {
			y = MultiMod(x, x, n);
			if (y == 1 && x != 1 && x != n - 1) return false;
			x = y;
		}
		if (y != 1) return false;
	}
	return true;
}


int lef[N], pn;//lef[v]表示Y集的点v 当前连接的点 , pn为x点集的点数
bool T[N];     //T[u] 表示Y集 u 是否已连接X集
vector<int>G[N]; //匹配边  G[X集].push_back(Y集)  注意G 初始化

bool match(int x){ // x和Y集 匹配 返回x点是否匹配成功
	for(int i=0; i<G[x].size(); i++)
	{
		int v = G[x][i];
		if(!T[v])
		{
			T[v] = true;
			if(lef[v] == -1 || match( lef[v] ))   //match(lef[v]) : 原本连接v的X集点 lef[v] 能不能和别人连,如果能 则v这个点就空出来和x连
			{
				lef[v] = x;
				return true;
			}
		}
	}
	return false;
}

int solve(){
	int ans = 0;
	memset(lef, -1, sizeof(lef));
	for(int i = 1; i<= pn; i++)//X集匹配,X集点标号从 1-pn 匹配边是G[左点].size()
	{
		memset(T, 0, sizeof(T));
		if( match( i ) ) ans++;
	}
	return ans;
}

int n;
long long a[N];

void Clear() {
	for (int i = 0; i <= n; ++i) G[i].clear();
}

int main() {
	while (1 == scanf("%d", &n)) {
		Clear();
		pn=n;
		for (int i = 1; i <= n; ++i)
			scanf("%lld", &a[i]);
		for (int i = 1; i <= n; ++i) {
			for (int j = i + 1; j <= n; ++j) {
				long long num = a[i] + a[j];
				if (MillarRabin(num)) G[i].push_back(j), G[j].push_back(i);
			}
		}
		int ans = solve() / 2;
		printf("%d\n", ans);
	}
	return 0;
}

G: 点击打开链接

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

using namespace std;

const int MAX_N = 10007;

long long n, k;
long long a[MAX_N];

set<long long> s;

int main() {
	while (2 == scanf("%lld%lld", &n, &k)) {
		for (int i = 0; i < n; ++i) {
			scanf("%lld", &a[i]);
		}
		s.clear();
		int j = 0, ans = 0;
		for (int i = 0; i < n; ++i) {
			s.insert(a[i]);
		//	printf("%lld %lld\n", *s.rbegin(), *s.begin());
			while (*s.rbegin() - *s.begin() > k)
				s.erase(a[j++]);
			ans = max(ans, i - j + 1);
		}
		printf("%d\n", ans);
	}
	return 0;
}
H: 点击打开链接

题意:

n个操作

1 val 在集合中插入val

2 查询当前集合 通过任意数求和不能得到的最小正整数
思路:

空集合时ans=1

且插入数字后ans只能增加,所以维护这个ans

ans是 a1+a2+a3···+ai < ans < ai+1 的最小的i

所以ans是最小的前缀和+1且<ai+1

用multiset记录ai+1 ··an

插入的数<=ans时才会更新答案,所以复杂度是nlogn

#include <cstdio>
#include <iostream>
#include <cstring>
#include <queue>
#include <algorithm>
#include <map>
#include <cmath>
#include <set>
template <class T>
inline bool rd(T &ret) {
	char c; int sgn;
	if(c=getchar(),c==EOF) return 0;
	while(c!='-'&&(c<'0'||c>'9')) c=getchar();
	sgn=(c=='-')?-1:1;
	ret=(c=='-')?0:(c-'0');
	while(c=getchar(),c>='0'&&c<='9') ret=ret*10+(c-'0');
	ret*=sgn;
	return 1;
}
template <class T>
inline void pt(T x) {
    if (x <0) {
        putchar('-');
        x = -x;
    }
    if(x>9) pt(x/10);
    putchar(x%10+'0');
}
using namespace std;
typedef long long ll;


int n;
ll ans;
multiset<ll>s;
multiset<ll>::iterator it;

int main() {
    while(cin>>n){
    	s.clear();
    	int op; ll val;
    	ans = 1;
		while(n-->0){
			rd(op);
			if(op == 2) pt(ans), putchar('\n');
			else {
				rd(val);
				if(val <= ans){
					ans += val;
					while((int)s.size()){
						it = s.begin();
						if(ans>=(*it))
						{
							ans += *it;
							s.erase(it);
						}
						else break;
					}
				}
				else	s.insert(val);
			}
		}
    }
    return 0;
}
/*
99
2
1 3
2
1 2
2
1 1
2
1 7
2


99
2
1 1
2
1 2
2
1 4
2
1 7
2


*/

I: 点击打开链接
题意:

给出逆序数的值,求原序列(一个1-N的排列)

1, 2, 0, 1, 0 表示1的逆序数是1,2的逆序数是2,3的逆序数是0···

思路:

从最后一个数开始插,每次插到当前序列的第a[i]个数。。

splay模拟

== 这个方法比较直(wu)观(nao),别的方法并没有想出来。。

#include <cstdio>
#include <iostream>
#include <cstring>
#include <queue>
#include <algorithm>
#include <map>
#include <cmath>
template <class T>
inline bool rd(T &ret) {
	char c; int sgn;
	if(c=getchar(),c==EOF) return 0;
	while(c!='-'&&(c<'0'||c>'9')) c=getchar();
	sgn=(c=='-')?-1:1;
	ret=(c=='-')?0:(c-'0');
	while(c=getchar(),c>='0'&&c<='9') ret=ret*10+(c-'0');
	ret*=sgn;
	return 1;
}
template <class T>
inline void pt(T x) {
    if (x <0) {
        putchar('-');
        x = -x;
    }
    if(x>9) pt(x/10);
    putchar(x%10+'0');
}
using namespace std;
inline int Mid(int a,int b){return (a+b)>>1;}
#define N 100010
#define L(x) tree[x].ch[0]
#define R(x) tree[x].ch[1]
#define Siz(x) tree[x].siz
#define Father(x) tree[x].fa
#define Max(x) tree[x].max
#define Val(x) tree[x].val
#define Pt(x) tree[x].pt()
struct node{
    int ch[2], siz, fa;
    int max, val;
    void pt(){printf("val:%d max:%d siz:%d fa:%d child{%d,%d}\n", val,max,siz,fa,ch[0],ch[1]);}
}tree[N*2];
int tot, root;
void Newnode(int &id, int val, int fa, int siz = 1){
    id = ++tot;
    L(id) = R(id) = 0;
    Father(id) = fa;
    Siz(id) = siz;
    Max(id) = Val(id) = val;
}

void push_up(int id){
    Siz(id) = Siz(L(id)) + Siz(R(id)) +1;
    Max(id) = max(Max(R(id)), Max(L(id)));
    Max(id) = max(Val(id), Max(id));
}
void push_down(int id){}

void Rotate(int id, int kind){
    int y = Father(id);
    push_down(y); push_down(id); //here
    tree[y].ch[kind^1] = tree[id].ch[kind];
    Father(tree[id].ch[kind]) = y;
    if(Father(y))
        tree[Father(y)].ch[R(Father(y))==y] = id;
    Father(id) = Father(y);
    Father(y) = id;
    tree[id].ch[kind] = y;
    push_up(y);
}
void splay(int id, int goal){
    push_down(id);
    while(Father(id) != goal){
        int y = Father(id);
        if(Father(y) == goal)
            Rotate(id, L(y)==id);
        else
        {
            int kind = L(Father(y)) == y;
            if(tree[y].ch[kind] == id)
            {
                Rotate(id, kind^1);
                Rotate(id, kind);
            }
            else
            {
                Rotate(y, kind);
                Rotate(id,kind);
            }
        }
    }
    push_up(id);
    if(goal == 0)root = id;
}
int Get_kth(int kth, int sor){//找到在sor后面的第k个数
    push_down(sor);
    int id = sor;
    while(Siz(L(id)) != kth){
        if(Siz(L(id)) > kth)
            id = L(id);
        else
        {
            kth -= (Siz(L(id))+1);
            id = R(id);
        }
        push_down(id);
    }
    return id;
}
void init(){
	Father(0) = L(0) = R(0) = Siz(0) = 0;
	Max(0) = 0;
	tot = 0;
	Newnode(root, 0, 0);
	Newnode(R(root), 0, root);
	push_up(root);
}
void debug(int x){
	printf("%d:\n", x);
	Pt(x);
	if(L(x)){
		printf("L:");
		debug(L(x));
		printf("return to %d:\n", x);
	}
	if(R(x)){
		printf("R:");
		debug(R(x));
		printf("return to %d:\n", x);
	}
}
void insert(int pos, int val){
	splay(1, 0);
	int u = Get_kth(pos, 1);
//	if(pos == 2){cout<<"=="; debug(root);}
	int v = Get_kth(pos+1, 1);
//	printf("在(%d,%d)之间:", u, v);	Pt(u); Pt(v);	puts("*****");
	splay(u, 0);
	splay(v, root);
//	if(pos == 2){cout<<"=="; debug(root);}
	Newnode(L(v), val, v);
	push_up(v);
	push_up(u);
//	printf("[%d,%d]\n", u, v);
}

int n;
int a[100005];
vector<int>G;
void dfs(int u){
	if(L(u))
		dfs(L(u));
	G.push_back(Val(u));
	if(R(u))
		dfs(R(u));
}
int main() {
    while(cin>>n){
    	init();
	//		puts("init debug begin:");debug(root);
		for(int i = 1; i <= n; i++)rd(a[i]);
		bool ok = true;
		for(int i = n; i; i--){
			if(a[i]>n-i){ok = false; break;	}
			insert(a[i] , i);
	//		printf("   %d debug begin:", i);debug(root);
		}
		if(false == ok){ puts("No solution");continue; }
		G.clear();
		dfs(root);
		for(int i = 1; i < G.size() -1; i++)
		printf("%d%c", G[i], i+2==(int)G.size()?'\n':' ');
    }
    return 0;
}
/*
1
7
0 1 1 1 0 4 1

*/

J: 点击打开链接

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

using namespace std;

const int MOD =(int)1e9 + 7;

long long Pow(long long x, long long n) {
	long long res = 1;
	while (n > 0) {
		if (n & 1) res = (res * x) % MOD;
		x = (x * x) % MOD;
		n >>= 1;
	}
	return res;
}

int main() {
	long long n, m;
	while (2 == scanf("%lld%lld", &n, &m)) {
		long long ans = 0;
		for (int i = 1; i <= n; ++i) {
			ans = (ans + Pow(i, m) % MOD) % MOD;
		}
		printf("%lld\n", ans);
	}
	return 0;
}