比赛链接:点击打开链接
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;
}
题意:
第一行输入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;
}