Attempted_简单篇
详解
怎么说呢,做的时候总是觉得自己的思路完全正确,但是就是一直WA。其实就是思路被限制了,然后最后也没过。最后一个小时本来打算把G题和D题过了,但是就是没什么新的思想碰撞,想不到该改哪里,就是觉得自己的没错。还好最后半个小时开了另外一道题给一发过了。
思路:
My_Feeling:(贼简单的数学解方程QAQ,就做不出来,猜了个答案没过,就没做了,tcl)
#include <iostream>
#include <cstdio>
using namespace std;
typedef long long ll;
int main()
{
ll n;
while(~scanf("%lld", &n))
{
printf("%lld\n", -n);
}
return 0;
}
题意:最长上升子序列(单调不下降)模板【详解】
思路:时间复杂度要求我们必须控制在O(n * logn)内。但是有所不同的是单调不下降,所以我们要保证可以存在重复的数字,所以说我们的 low 数组就可以存在重复的数字,并且当我们遇到较小的数字时我们要放在 low 数组中第一个大于它的数字的位置上。(必须是不能替代小于等于的数字,否则答案就会偏小)
样例:(用 lower_bound就会错)
6
1 2 5 2 2 2
wrong answer: 3
:)就是直接2每次都覆盖2,所以长度不会变
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxN=1e5+5;
int ans, a[maxN], low[maxN] , n;
void init()
{
ans = 0;
memset(low, 0 , sizeof(low));
}
int LIS()
{
for(int i = 1; i <= n ; i++)
{
if( a[i] >= low[ans])
low[++ans] = a[i];
else
low[ upper_bound(low, low + ans + 1, a[i]) - low ] = a[i];
}
return ans;
}
int main()
{
while(~scanf("%d", &n))
{
init();
for(int i = 1; i <= n ; i++)
scanf("%d", &a[i]);
printf("%d\n", LIS());
}
return 0;
}
Unattempted_简单篇
思路:一道简单的高数题:求极限。如果分子的次幂大,那么为无穷;如果分母的次幂大,那么为0;如果相等那么就等于系数之比。
My_Feeling: 怎么说呢,这道题连看都没有看,看题面就感觉做不了。现在补题的话,还把求和当成求乘积做了。而且开4个longlong的1 e 5,编译器就给我报错QAQ,生气。挺简单的,只用到了一个gcd,其他就没有了。QAQ……
#include <iostream>
#include <cstdio>
using namespace std;
typedef long long ll;
const int maxN = 1e5 + 5;
ll gcd(ll x, ll y)
{
return y == 0 ? x : gcd(y, x % y);
}
int main()
{
int n, m;
while(~scanf("%d%d", &n, &m))
{
ll a[maxN], b[maxN];
ll up = 0, down = 0;
ll up_index = 0, down_index = 0;
for(int i = 0 ; i < n ; i ++ )
{
scanf("%lld%lld", &a[i], &b[i]);
up_index = max( up_index, b[i]);
}
for(int i = 0 ; i < n ; i ++)
if( b[i] == up_index )
up += a[i];
for(int i = 0; i < m ; i ++)
{
scanf("%lld%lld", &a[i], &b[i]);
down_index = max(down_index, b[i]);
}
for(int i = 0 ; i < m ; i ++)
if( b[i] == down_index )
down += a[i];
if(up_index > down_index)
printf("Orz\n");
else if(up_index < down_index)
printf("0\n");
else
{
ll _gcd = gcd(up, down);
up /= _gcd;
down /= _gcd;
printf("%lld/%lld\n", up, down);
}
}
return 0;
}
官方题面:现在有一个字符串s我们称为文本串。然后给出m个字符串,这m个字符串我们称为模式串。对于每一个模式串t,我们用p表示在s中有多少个前缀的后缀中出现了t,我们用q表示在s中有多少个后缀的前缀中出现了t。他现在想知道p和q中的最大值。
【读了之后感觉……???这是什么鬼】
我最初理解的题意:p:多少个前缀中出现了模式串。q:多少个后缀中出现了模式串。取其中的较大者。
实际的题意:文本串中有多少个模式串。
【惊不惊喜?!意不意外?!】
实际上题面中的那个出现是:前缀的后缀中有一个后缀是模式串,后缀的前缀中有一个前缀是模式串,那么这样的话,p和q总是相同。也就是找文本串中有多少个模式串。
【说是简单题叭,但是卡了题面QAQ,我是比赛的时候也没读懂,比赛后也没读懂,还是问了人】
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxN = 2e3 + 5;
int main()
{
char s[maxN];
while(~scanf("%s", s))
{
int len = strlen(s);
int m; scanf("%d", &m);
while(m -- )
{
char tmp[maxN];
scanf("%s", tmp);
int tlen = strlen(tmp);
int ans = 0;
for(int i = 0; i < len ; i++)
{
if(s[i] == tmp[0])
{
int j = 0;
for( ; j < tlen ; j ++)
{
if(s[i + j] == tmp[j])
continue;
break;
}
if(j == tlen)
ans++;
}
}
printf("%d\n", ans);
}
}
return 0;
}
AC篇
题意:按规则输出一个三角形
思路:直接看代码叭
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <cmath>
#include <cstring>
#include <string>
#include <vector>
#include <set>
#include <stack>
#include <list>
#include <map>
#define INF 0x3f3f3f3f
#define MID l + r >> 1
#define lsn rt << 1
#define rsn rt << 1 | 1
#define Lson lsn, l, mid
#define Rson rsn, mid+1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
using namespace std;
typedef long long ll;
typedef vector<int>:: iterator VITer;
const ll maxN=1e5+5;
int main()
{
int T; scanf("%d", &T);
while(T--)
{
int N; scanf("%d", &N);
if( N == 0)
continue;
if(N == 1)
{
printf("*\n");
continue;
}
//第一行
int tmp = N - 1;
while(tmp -- )
putchar(' ');
printf("*\n");
//中间
for(int i = N-1 ; i > 1 ; i -- )
{
tmp = i - 1;
while(tmp -- )
putchar(' ');
printf("*");
for(int j = 1; j < 2*(N - i) ; j ++)
putchar(' ');
printf("*\n");
}
//最后一行
tmp = N + N -1;
while(tmp--)
putchar('*');
printf("\n");
}
return 0;
}
题意:可以跳2 ~ k 层的倍数层,那么2 ~ n层楼不能到达的楼层数最大是多少。
思路:由于数据很大,所以用不了暴力。我们知道相邻两个质数相差是不大的,(悄悄咪咪地:听学长说不超过100),所以我的做法就是:首先我们知道质数是一定不会被跳到的,除非这个质数<=k。所以我们找到n内最大的质数(倒着找,快),然后从这个质数跑到n是外层循环,内层是1e6的循环(其实也跑不了那么多),也就是1e6乘以一个大常数,所以时间复杂度完全可以。
需要注意的就是当最大质数可以被跳到,也就是说是p之内的数的时候,tmp就更新为1,输出为1嘛,全部可以跳到的时候。这时候我们不用担心剩余的楼层不会被跳到,因为我们输出的是tmp和ans中的较大者,不会被跳到的楼层我们是用ans来存最大值的。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <cmath>
#include <cstring>
#include <string>
#include <vector>
#include <set>
#include <stack>
#include <list>
#include <map>
#define INF 0x3f3f3f3f
#define MID l + r >> 1
#define lsn rt << 1
#define rsn rt << 1 | 1
#define Lson lsn, l, mid
#define Rson rsn, mid+1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
using namespace std;
typedef long long ll;
typedef vector<int>:: iterator VITer;
const int maxN=1e6+5;
const int maxE = 1e4 +5;
int prime[maxN], vis[maxN], cnt;
void init()
{
memset(prime, 0, sizeof(prime));
memset(vis, 0, sizeof(vis));
vis[0] = 1;
vis[1] = 1;
cnt = 0;
}
void Prime()
{
init();
for(int i = 2; i <= 1e6 ; i++)
{
if(! vis[i])
prime[ ++ cnt] = i;
for(int j = 1 ; j <= cnt ; j++)
{
if(prime[j] * i > 1e6) break;
vis[prime[j] * i] = 1;
if(i % prime[j] == 0) break;
}
}
}
int main()
{
Prime();
int p, n;
int T; scanf("%d", &T);
while(T--)
{
int tmp = 1;
scanf("%d%d", &p, &n);
for(int i = n ; i > 0 ; i--)
if(!vis[i])
{
tmp = i;
break;
}
int ans = 1;
for(int i = tmp; i <= n ; i++)
{
int flag = 0;
for(int j = 2 ; j <= p ; j++)
{
if(i % j == 0)
{
flag = 1;
break;
}
}
if(i == tmp && flag)
tmp = 1;
if(!flag)
ans = i;
}
printf("%d\n", max(tmp, ans));
}
return 0;
}
题意:我们打两个不同血量的怪兽,最短的天数是多少。
思路:首先的话我们打一个怪兽,总是有一个血量少的,一个血量大的,或者两个怪兽血量相同。所以我们总能找到和血量较少的相同的伤害值的那天,所以我们就直接将两者血量相加变成一个超级大怪兽,然后能够打死它的那天就可以了。因为数据比较大,所以我们用到二分。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <cmath>
#include <cstring>
#include <string>
#include <vector>
#include <set>
#include <stack>
#include <list>
#include <map>
#define INF 0x3f3f3f3f
#define MID l + r >> 1
#define lsn rt << 1
#define rsn rt << 1 | 1
#define Lson lsn, l, mid
#define Rson rsn, mid+1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
using namespace std;
typedef long long ll;
typedef vector<int>:: iterator VITer;
const ll maxN = 7e4+5;
ll minn, maxx;
bool judge(ll x)
{
return x * (x + 1) / 2 >= maxx;
}
int main()
{
ll A, B;
while(~scanf("%lld%lld", &A, &B) && (A || B))
{
maxx= A + B;
if( maxx == 1)
{
printf("1\n");
continue;
}
ll ans;
ll l = 0, r = maxN, mid;
while(l < r)
{
mid = (l + r) / 2;
if(judge(mid))
{
r = mid;
ans = mid;
}
else
l = mid +1;
}
printf("%lld\n", ans);
}
return 0;
}
题意:【最短路】求起点到终点的路费最小值。有两种优惠:(1)每条道路免费 (2)在给定的可免费的道路中任选一条免费。(可不选择优惠)
思路:肯定要用优惠啊,不用优惠那肯定贵。
第(1)种优惠:直接求出最短路,然后取一半的值即可。
第(2)种优惠:那么我们如何保证只选择一条道路免费呢?首先我们用二维dis[ ]来维护各点到起点以及各点到终点的最短路。然后我们遍历一遍可免费的道路,如果用上这条路的免费比之前的最短路更优,那么我们就更新dis 。其余的话就是最短路模板了。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <cmath>
#include <cstring>
#include <string>
#include <vector>
#include <set>
#include <stack>
#include <list>
#include <map>
#define INF 0x3f3f3f3f
#define MID l + r >> 1
#define lsn rt << 1
#define rsn rt << 1 | 1
#define Lson lsn, l, mid
#define Rson rsn, mid+1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
using namespace std;
typedef long long ll;
typedef vector<int>:: iterator VITer;
const int maxV=1e3+5;
const int maxE = 1e4 +5;
int n, m, s, e;
int head[maxV], cnt;
void init()
{
memset(head, -1 , sizeof(head));
cnt = 0;
}
struct EDGE{
int pre,from, to, w;
EDGE(int a = 0, int b = 0, int c = 0, int d= 0) : pre(a),from(b), to(c), w(d){}
}edge[maxE << 1];
void add_edge(int u, int v, int val)
{
edge[++cnt] = EDGE(head[u], u, v, val);
head[u] = cnt;
}
struct node{
int point, val;
node(int a= 0, int b = 0) : point(a), val(b){}
friend bool operator < (node n1, node n2) { return n1.val > n2.val; }
};
int vis[maxV], dis[2][maxV];
void dij(int st,int mark)
{
memset(vis, 0, sizeof(vis));
for(int i = 1; i <= n ; i++ )
dis[mark][i] = (i == st ? 0 : INF);
priority_queue<node> pq;
pq.push(node(st, dis[mark][st]));
while(!pq.empty())
{
node tmp = pq.top();
pq.pop();
if(vis[tmp.point])
continue;
for(int i = head[tmp.point]; ~i ; i = edge[i].pre)
{
if(dis[mark][edge[i].to] > dis[mark][tmp.point] + edge[i].w)
{
dis[mark][edge[i].to] = dis[mark][tmp.point] + edge[i].w;
pq.push(node(edge[i].to, dis[mark][edge[i].to]));
}
}
vis[tmp.point] = 1;
}
}
int main()
{
while(~scanf("%d%d%d%d", &n, &m, &s, &e))
{
init();
for(int i = 1; i <= m; i++)
{
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
add_edge(a, b, c);
add_edge(b, a, c);
}
dij(s, 0);
dij(e, 1);
int tmp = dis[0][e] / 2;
int k; scanf("%d", &k);
for(int i = 1 ; i <= k ; i ++)
{
int num; scanf("%d", &num);
num *= 2;
if(dis[0][e] > dis[0][edge[num].from] + dis[1][edge[num].to])
{
dis[0][e] = dis[0][edge[num].from] + dis[1][edge[num].to];
}
else if(dis[0][e] > dis[1][edge[num].from] + dis[0][edge[num].to])
{
dis[0][e] = dis[1][edge[num].from] + dis[0][edge[num].to];
}
}
if(dis[0][e] == INF)
printf("-1\n");
else
printf("%d\n",min(dis[0][e], tmp));
}
return 0;
}
Attempted_线段树
题意:区间翻转更新,区间查询【这里的翻转是每一个数值二进制按位异或后得到的新的数值】
思路:就是翻转操作不太好弄叭。0按位翻转就是INT32_MAX,那么1翻转是什么呢?我们可以发现是INT32_MAX - 1. 那么2翻转是什么呢?是INT32_MAX - 2. 知道了这个我们就可以直接来敲线段树了。
当时敲了之后发现虽然区间和是m,但是如果有多个0进行翻转的话,那么我的操作就是不对的,因为我是直接INT32_MAX - 区间和就是更新的值了。当时想新开一个zero的数组来存0的个数,但是怎么想怎么没办法处理。QAQ
后来听了出题人的讲解就是只需要把INT32_MAX * 区间长度然后再 - 区间和即可。
听了之后……哭辽
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <cmath>
#include <cstring>
#include <string>
#include <vector>
#include <set>
#include <stack>
#include <list>
#include <map>
#define INF 0x3f3f3f3f
#define MID l + r >> 1
#define lsn rt << 1
#define rsn rt << 1 | 1
#define Lson lsn, l, mid
#define Rson rsn, mid+1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
using namespace std;
typedef long long ll;
const ll maxN=1e5+5;
ll num[maxN];
ll tree[maxN << 2], lazy[maxN << 2];
void pushup(ll rt) { tree[rt] = tree[lsn] + tree[rsn]; return ; }
void build_tree(ll rt, ll l, ll r)
{
if( l == r ) { tree[rt] = num[l]; return ; }
ll mid = MID;
build_tree(Lson);
build_tree(Rson);
pushup(rt);
}
void pushdown(ll rt, ll l, ll r)
{
if(lazy[rt])
{
ll mid = MID;
tree[lsn] = INT32_MAX * (mid - l +1) - tree[lsn];
tree[rsn] = INT32_MAX * (r - mid) - tree[rsn];
lazy[lsn] = lazy[lsn] ^ 1;
lazy[rsn] = lazy[rsn] ^ 1;
lazy[rt] = 0;
}
}
void update_range(ll rt, ll l, ll r, ll ql , ll qr)
{
if( ql <= l && qr >= r)
{
tree[rt] = INT32_MAX * (r - l + 1) - tree[rt];
lazy[rt] ^= 1;
return ;
}
pushdown(rt, l, r);
ll mid = MID;
if(qr <= mid) update_range(QL);
else if( ql > mid) update_range(QR);
else { update_range(QL); update_range(QR); }
pushup(rt);
}
ll query(ll rt, ll l, ll r, ll ql, ll qr)
{
if(ql <= l && qr >= r) { return tree[rt]; }
pushdown(rt, l, r);
ll mid = MID;
if( qr <= mid) return query(QL);
else if(ql > mid) return query(QR);
else return query(QL) + query(QR);
}
void init()
{
memset(lazy, 0, sizeof(lazy));
}
int main()
{
ll N, Q;
while(~scanf("%lld%lld", &N, &Q))
{
init();
for(ll i = 1 ; i <= N; i ++)
{
scanf("%lld", &num[i]);
}
build_tree(1, 1, N);
char s[10];
while(Q--)
{
scanf("%s", s);
ll a, b; scanf("%lld%lld", &a, &b );
if(s[0] == 'C')
update_range(1, 1, N, a, b);
else if(s[0] == 'Q')
printf("%lld\n", query(1, 1, N, a, b));
}
}
return 0;
}
Unattempted_字典树
Summary:
最后的比赛就4题,最后的时间基本就开不出题,很乱,也看不下去题意。尤其是看见大佬们chua chua 的过题,就一连好几道题就更着急了。其实也没有多么多么地着急但是还是会影响心态的。而且有些题限制的思路就怎么都想不到差了那么一点的正解。
很惭愧就是放了好几天假了才补起来题,然后还有就是真的很佩服几个同学,前几道题,被归为简单的题,这种做的很快,思路很快叭。ORZ,我呢就是虽然是简单题还是做不出来。我就是那种明明很菜有的时候还老是没有人家努力。
这一个月来其实很多时候都在划水,不是我想这样的,我也不想,但是我就是没办法找到自己的状态。超级烦躁,想哭,也哭过很多次,看别人状态很好就觉得自己超级……自愧不如叭。队长讲话的时候说:有些同学就只做一些模板题,有些就是补完了,一下就是十几道题的差距,题的难度还不一样,人家做的多还都是难题。我就是这样的叭。很多时候没有状态去做题,然后最后一周复习,人家都在补题的时候我也补不了题,因为一直在看从来没有怎么看过的数论。
但是我倒是不会因为他的这些话怎么样,自己有自己的想法和计划。我是想先什么都涉猎了熟悉了再来刷题,我的时间精力浪费了很多,但是会之后慢慢补回来培训比人家落下的。
回家了这么多天,发生了挺多事情的,心情也是很纠结很起伏。希望我可以坚持下去,也快回学校了。