标签:
比赛链接:点击打开链接
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; }
题意:
给定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; }
#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 */
给出逆序数的值,求原序列(一个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 */
#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; }
湖南多校对抗赛(2015.03.28)CSU1547~1536 题解
标签:
原文地址:http://blog.csdn.net/qq574857122/article/details/44874155