这场比赛是前几天洛谷上 暮雪﹃紛紛dalao的个人公开赛,当时基本上都在水暴力分......也没有好好写正解(可能除了T1)

过了几天颓废的日子之后,本蒟蒻觉得应该卓越一下了qwq,所以就打算写一个解题报告qwq(其实就是详细注释啦~~~)

T1 JerryC Loves Driving(数学)

首先T1已经给我们简化了,(我才不会告诉你前面的题目我都没看呢)

题目要求:给出A,B,求出下面式子的值——

\(\sum_{i=A}^B \sum_{j=1}^i \lfloor\frac{i}{j}\rfloor{}*(-1)^j\)

虽然有的dalao说这个就是普及-的签到题,(我真的好弱啊,我怎么普及-的题都不会做啊)但是我还是借鉴(qwq)了 大佬 BLUESKY007的思路才过了这个题qwq。

当然上来我们可以纯模拟,比如说这样:

for(int i=a;i<=b;i++)
{
int curans=0;
for(int j=1;j<=i;j++)
{
int cur=i/j;
if(j&1) cur=-cur;
curans+=cur;
}
ans+=curans;
}

这代码垃圾的一匹啊,(我怎么还有脸把它放上来献丑啊qwq)......稳稳地T飞。之后我们就要想优化——

这时,考虑到有关于我们可以打个表输出每次增加的值来看一看.......然后......就发现了规律。

比如我们分别以a=1,b=10和a=3,b=10来打表,输出如下(第一行是输入,最后一行是答案)



论我们发现了什么:

  • 有没有发现后面那个是总的三角形减去上面的那个小三角形?qwq
  • 有没有发现每一列都是等差数列?qwq

所以说......我们可以使用等差数列来进行O(n)时间复杂度的加和计算。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
long long a,b,ans1,ans2,yu,end;
int main(){
scanf("%lld%lld",&a,&b);
a--;//根据查分的思想:sum[i~j]=ans[j]-ans[i-1]
for(int i=1;i<=b;i++){
end=((b+1)/i)-1;
//这个计算的是完整的到了数字几(比如说在第三列上,每个数字出现三次才能算是完整的)
//至于为什么要b+1,是因为观察上表可以发现,每一列如果空行的上面再在上面加上一列空行(就是补成一个长方形),空行数正好等于数字需要出现的次数
//因为我们在上面补了空行,所以注意之后要减去一
yu=(b+1)%i;
//求不完整的数的个数
ans1+=(i&1?-1:1)*(i*((end+1)*end/2)+yu*(end+1));}
//等差数列求和公式
//注意"-"的处理,这里使用三元运算符
for(int i=1;i<=a;i++){
end=((a+1)/i)-1;
yu=(a+1)%i;
ans2+=(i&1?-1:1)*(i*((end+1)*end/2)+yu*(end+1));}
printf("%lld\n",ans1-ans2);
return 0;
}

T2 Jerry Loves Lines(排序,模拟)

这个题在比赛的时候调了好久还是调炸了qwq......最后......最后还是交了一个暴力上去qwq。(暴力好像是有一半的分)

做这个题应该是有一个直观的感受,就是两条直线相交之后他们的rank一定会互相交换,因为两个直线如果相交则rank一定相邻,所以将低的那个rank++,高的rank--即可。(如果有多条直线的话同理,可以尝试手动模拟一下,(懒得画图解释了)因为节点记录的时候都是两条两条记录的,所以同上操作即可)

之后就是对询问的处理,为了方便我们以O(n)的复杂度进行x坐标从左到右扫描交换处理,我们可以先将询问存下来,按照x进行从小到大的排序,然后方便后来的一并处理。

具体细节见代码及注释。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
//因为int*int会爆int 所以我们要记得开long long
//感觉无论何时开long long 都是一个良好的习惯(才不是因为出题人提醒了呢)
#define N 2010
#define M 500500
using namespace std;
inline ll read() {
ll s = 0, t = 1; char ch = getchar();
while(ch > '9' || ch < '0') { if(ch == '-') t = -1; ch = getchar(); }
while(ch <= '9' && ch >= '0') { s = s * 10 + ch - '0'; ch = getchar(); }
return s * t;
}
//快速读入
struct Line {ll k,b,js; int id;}L[N];
inline ll cal_x(Line a, Line b) {return (b.b-a.b)/(a.k-b.k);} //求x值
inline ll cal_y(Line a, ll x) {return a.k*x+a.b;} //求y值
struct Ask {int id;ll x;}Q[M]; //储存询问
struct Swap {int x,y; ll p;};
bool operator < (Ask a, Ask b) {return a.x < b.x;}
//重载运算符
bool cmp(Swap a, Swap b) {
if(a.p != b.p) return a.p < b.p;
return a.x < b.x;
}
bool cmp1(Line a, Line b) {return a.js < b.js;}
int n,m,k,cnt,rank[N],num[N];
ll res[M];
Line L[N];
Swap S[N * N];
int main() {
n=read(); m=read(); k=read();
for(int i = 1; i <= n; i++)
{
L[i].k=read();
L[i].b=read();
}
for(int i = 1; i <= m; i++) {
Q[i].x=read();
Q[i].id = i;
}
sort(Q + 1, Q + 1 + m);
for(int i = 1; i <= n; i++) L[i].js = cal_y(L[i], Q[1].x);
sort(L + 1, L + 1 + n, cmp1);
//按照y值计算在第一个询问x时候的排名
//接下来开始处理节点信息
for(int i = 1; i <= n; i++) {
L[i].id = num[i] = rank[i] = i;
//初始化直线的id,排名所对应的直线编号,该线段所对应的排名(因为刚初始化,所以都一样)
for(int j = i + 1; j <= n; j++)
if(L[i].k != L[j].k && cal_y(L[i], Q[m].x) >= cal_y(L[j], Q[m].x)) {
//注意平行线情况需要跳过
ll d = cal_x(L[i], L[j]);
if(d >= Q[1].x) {
//因为刚才已经处理过第一个询问时候的直线排名了,
//所以现在第一个询问之前就算交换了也没有意义,就不需要处理了
cnt++;
S[cnt].x = i;
S[cnt].y = j;
//记录节点的相交线信息
S[cnt].p = d;
//记录x值+1
}
}
}
sort(S + 1, S + 1 + cnt, cmp);
//将节点进行排序
for(int i = 1, id = 1; i <= m; i++) {
while(id <= cnt && S[id].p < Q[i].x) {
//如果节点的信息在当前询问之前,就要处理
rank[S[id].x]++;
rank[S[id].y]--;
num[rank[S[id].x]] = S[id].x;
num[rank[S[id].y]] = S[id].y;
id++;
//rank记录的是当前直线在x=i时刻的排名
//num记录的是当前x=i时哪个排名对应的是哪个直线
}
res[Q[i].id] = cal_y(L[num[k]], Q[i].x);
//处理完当前询问之前的节点交换信息之后,将答案计入res数组中
//这时候注意因为我们将询问排序过了,但是输出的时候还是要按照初始询问次序输出
//所以要以Q[i].id为关键字输入
}
for(int i = 1; i <= m; i++) printf("%lld\n", res[i]);
return 0;
}

T3 Samcompu Loves Water(并查集)

比赛的时候看错题了qwq,结果打了一个树剖上去......(我的语文阅读能力啊qwq)。最后一看发现貌似是统计不同的两点之间任意边权不大于某个值的方案数.......如果没有失效边的话肯定比较简单,直接对边进行排序一点一点加上去,并查集合并的时候size相乘即可。但是这道题需要处理失效边.......

打比赛的时候因为没有时间了所以就直接放了个很暴力的并查集上去,只拿了一半的分数(代码见下)

#include<iostream>
#include<cstdio>
#include<cstring>
#define MAXN 10010
using namespace std;
int t,n,ti[MAXN],ki[MAXN],cnt,ans;
int u[MAXN],v[MAXN],w[MAXN],fa[MAXN],size[MAXN];
int find(int x)
{
if(x==fa[x]) return x;
else return fa[x]=find(fa[x]);
}
int main()
{
scanf("%d%d",&t,&n);
for(int i=1;i<=n-1;i++)
scanf("%d%d%d",&u[i],&v[i],&w[i]);
for(int i=1;i<=t;i++)
scanf("%d%d",&ti[i],&ki[i]);
for(int p=1;p<=t;p++)
{
ans=0;
memset(fa,0,sizeof(fa));
memset(size,0,sizeof(size));
for(int i=1;i<=n;i++) fa[i]=i;
for(int i=1;i<=n-1;i++)
{
if(i==ki[p]||w[i]>=ti[p])
continue;
int a=find(u[i]),b=find(v[i]);
if(a!=b)
fa[a]=b;
}
for(int i=1;i<=n;i++)
{
size[find(i)]++;
// printf("i=%d fa=%d\n",i,find(i));
}
for(int i=1;i<=n;i++)
{
if(size[i]==0) continue;
ans+=size[i]*(size[i]-1);
// printf("i=%d size=%d\n",i,size[i]);
}
printf("%d\n",ans);
}
return 0;
}

但是这样肯定不行啊......1e4的平方肯定跑不过,所以......我们注意到出题人提示了ki最多只有1e3个,所以我们可以将这些无效的边加到一个数组中储存下来,失效的时候就不用,恢复之后暴力加边。

代码和注释如下;

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 10001
#define ll long long
using namespace std;
struct Ask {int tim,pos,id;}S[N];
struct Edge {int u,v,w,id;}E[N],a[1001];
bool operator < (Ask a, Ask b) {return a.tim < b.tim;}
bool operator < (Edge a, Edge b) {return a.w < b.w;}
//重载运算符
struct bcj {int f,id; ll si;}s[N],tmp[N];
int T,n,L=1,cnt=0;
bool flag[N],done[N];
//flag标记是否可能会失效
long long ans[N],sum=0;
inline int find(bcj &x) { return x.f = x.id == x.f ? x.f : find(s[x.f]); }
//并查集的find操作
//注意这里要对x进行取地址,要不然x.f的赋值操作无法生效
void Add(bcj a, bcj b, ll &ans) {
//取地址进行修改
bcj fu = s[find(a)];
bcj fv = s[find(b)];
if(fu.id == fv.id) return ;
ans += fu.si * fv.si;
//根据乘法原理
fu.f = fv.id;
fv.si += fu.si;
s[find(a)]=fu;
s[find(b)]=fv;
//因为我们是将可能无效的边和一直有效的边分开处理的,
//在同一个循环中还要继续使用size
//所以修改了之后记得重新放回来
//当然这里也可以在前面使用 bcj& fu,bcj& fv
}
int main() {
scanf("%d%d",&T,&n);
for(register int i = 1; i < n; i++) {
scanf("%d%d%d",&E[i].u,&E[i].v,&E[i].w);
E[i].id = i;
}
for(register int i = 1; i <= T; i++) {
scanf("%d%d",&S[i].tim,&S[i].pos);
S[i].id = i;
flag[S[i].pos] = 1;
//将可能无效的边进行标记
}
for(register int i = 1; i <= n; i++) {
if(flag[E[i].id])
a[++cnt] = E[i];
//这里就是上述所说将可能无效的边放在一起(a数组其实只用开1001大就可以)
s[i].f = i;
s[i].si = 1;
s[i].id = i;
//并查集初始化
}
sort(a + 1, a + 1 + cnt);
sort(E + 1, E + n);
sort(S + 1, S + 1 + T);
for(register int i = 1; i < n; i++)
{
//添加储存的无效边
while(L <= T && E[i].w >= S[L].tim)
{
ans[S[L].id] = sum;
memcpy(tmp, s, sizeof(s));
for(register int j = 1; j <= cnt; j++)
{
if(a[j].w >= S[L].tim) break;
//如果无效边大于当前所需要的,因为排序过了,所以直接break
if(S[L].pos == a[j].id || !done[a[j].id]) continue;
//如果当前添加边在此次无效,跳过
Add(s[a[j].u], s[a[j].v], ans[S[L].id]);
}
memcpy(s, tmp, sizeof(tmp));
//添加之后可能s数组的size值会改变
//因为每次的无效边都不一样
//为了防止算重,只能先储存s值到tmp中,之后再复制回来
//以避免中间的更改
L++;
}
if(L>T) break;
done[E[i].id] = 1;
if(flag[E[i].id]) continue;
//如果是会失效的边,跳过不进行添加
Add(s[E[i].u], s[E[i].v], sum);
//添加正常边
}
for(register int i = 1; i <= T; i++) printf("%lld\n", ans[i]*2);
//因为题目中有说x-y和y-x不一样,所以要*2
return 0;
}

T4 Zrz_orz Loves Secondary Element

此题留坑,这几天就补上


P.S 吐槽一下, 对于此场比赛的难度, 暮雪﹃紛紛dalao是这样说的:



然后洛谷的标签是这样的qwq:

(蒟蒻光速逃离

CYJian的水题大赛2 解题报告的更多相关文章

  1. CYJian的水题大赛

    实在没忍住就去打比赛了然后一耗就是一天 最后Rank19还是挺好的(要不是乐多赛不然炸飞),这是唯一一套在Luogu上号称水题大赛的而实际上真的是水题大赛的比赛 好了我们开始看题 T1 八百标兵奔北坡 ...

  2. 【洛谷】CYJian的水题大赛【第二弹】解题报告

    点此进入比赛 T1: JerryC Loves Driving 第一题应该就是一道水分题(然而我只水了130分),我的主要做法就是暴力模拟,再做一些小小的优化(蠢得我自己都不想说了). My Code ...

  3. 【洛谷】CYJian的水题大赛 解题报告

    点此进入比赛 \(T1\):八百标兵奔北坡 这应该是一道较水的送分题吧. 理论上来说,正解应该是DP.但是,.前缀和优化暴力就能过. 放上我比赛时打的暴力代码吧(\(hl666\)大佬说这种做法的均摊 ...

  4. 洛谷 P4514 上帝造题的七分钟 解题报告

    P4514 上帝造题的七分钟 题目背景 裸体(裸题)就意味着身体(神题). 题目描述 "第一分钟,X说,要有矩阵,于是便有了一个里面写满了\(0\)的\(n \times m\)矩阵. 第二 ...

  5. leetcode解题报告(2):Remove Duplicates from Sorted ArrayII

    描述 Follow up for "Remove Duplicates": What if duplicates are allowed at most twice? For ex ...

  6. Codeforces Round #256 (Div. 2/A)/Codeforces448A_Rewards(水题)解题报告

    对于这道水题本人觉得应该应用贪心算法来解这道题: 下面就贴出本人的代码吧: #include<cstdio> #include<iostream> using namespac ...

  7. [POJ 1000] A+B Problem 经典水题 C++解题报告 JAVA解题报告

        A+B Problem Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 311263   Accepted: 1713 ...

  8. Codeforces Round #378 (Div. 2) D题(data structure)解题报告

    题目地址 先简单的总结一下这次CF,前两道题非常的水,可是第一题又是因为自己想的不够周到而被Hack了一次(或许也应该感谢这个hack我的人,使我没有最后在赛后测试中WA).做到C题时看到题目情况非常 ...

  9. 无聊的活动/缘生意转(2018 Nova OJ新年欢乐赛B题)解题报告

    题目2(下面的太抓 我重新写了个背景 其他都一样) 无聊的活动 JLZ老师不情愿的参加了古风社一年一度的活动,他实在不觉得一群学生跳舞有什么好看,更不明白坐在身后的学生为什么这么兴奋(看小姐姐),于是 ...

随机推荐

  1. VNC Linux 远程桌面控制软件

    简介: VNC (Virtual Network Computer)是虚拟网络计算机的缩写. VNC 是一款优秀的远程控制工具软件,VNC 是在基于 UNIX 和 Linux 操作系统的免费的开源软件 ...

  2. PHP - 闭包Closure和lambda function

    现在的语言没有闭包简直都不好意思说出来. 想要了解闭包是什么,那么就必须知道匿名函数.其实看起来他们其实差不多一个意思. 见php RFC一句话:   End of 2007 a patch was  ...

  3. [Android]RecyclerView添加HeaderView出现宽度问题

    通过getItemViewType方式判断HeaderView方式添加HeaderView的,结果发现有几个界面HeaderView宽度不能满屏. 于是对比了几种布局,发现LinearLayout为根 ...

  4. windows版mongodb不知道安装在哪儿

    情景还原: 从官网:点击打开链接 下载了 MongoDB-win32-x86_64-2.6.12-signed.msi文件后, 右键安装,各种Next后,没有选择路径,就安装结束了!! 任务管理器里面 ...

  5. Writing Surface Shaders

    [Writing Surface Shaders] Writing shaders that interact with lighting is complex. There are differen ...

  6. Java-Properties文件读取工具类

    import org.apache.commons.configuration.ConfigurationException; import org.apache.commons.configurat ...

  7. Activiti 整合的小插曲

    虽然是令人头痛的小插曲,真不令人省心.2年不用它又忘了怎么配,这次一定记录下来,呵呵哒. 1.下载及运行设计器 官网下载源码压缩包,解压后找到设计器目录:Activiti-activiti-5.22. ...

  8. 554. Brick Wall最少的穿墙个数

    [抄题]: There is a brick wall in front of you. The wall is rectangular and has several rows of bricks. ...

  9. CloudFoundry 快速上手笔记

    1.登陆cf 2.登陆进入webservice 3.查看ruby版本 4.查看gem版本 5.安装CF 6.配置cf Download the CLI from github: https://git ...

  10. bootstrap导航条相关知识

    在导航条(navbar)中有一个背景色.而且导航条可以是纯链接(类似导航),也可以是表单,还有就是表单和导航一起结合等多种形式. 为导航条添加标题.二级菜单及状态 <div class=&quo ...