NEU2016年一月月赛回顾
月赛传送门
http://acm.neu.edu.cn/hustoj/contest.php?cid=1066
月赛已经结束十天了。。。才把题目补完真是大失误。。。
茅巨巨四天前就补完了,,,总结写得比我详细,附上传送门
http://blog.csdn.net/morejarphone/article/details/50659795
这次月赛感觉题目都不是很难(嗯,没有什么难的数学题或者牛逼的数据结构题目)
只有AEF卡我了,所以今天就只说这三道题目
A题
给你一个x,判断是否有n(n满足在十进制下每一位都为1),满足n可以整除x的,如果有,输出最小的n
样例输入
1
2
3
样例输出
1
-1
111
因为x的范围是10W的,所以一开始以为是数论,怎么想怎么不明白,感觉没有什么规律,提交个优化的暴力枚举上去果然WA,然后三个小时都没做,先去搞其他的题目了(此时已经有10+人A掉了。。。),后来转换思路,发现没必要真的去得到n,只要循环着让x乘一个数字加上余项后末尾为1就好了,当余项每一位都为1时停止循环,记录循环了几次。。。就是一个模拟。。。如果循环次数超过1W,就假设不存在(这里不知道怎么去证明),,,于是A掉了。。
E题
给你一个n*n的邻接矩阵,求所有点两两之间最短路长度平方的和(如果不存在最短路,则长度为n)
这个上来就可以想到floyd,但是是n^3的复杂度,交了一发果然T。。。
后来想到可以类似B题的写bfs,图的话转换成邻接表存储,感觉优化了很多,实际上复杂度应该还是n^3的。。。果然T掉
#include<cstdio>
#include<iostream>
using namespace std;
const int maxn=;
int a[][];
char ch[];
//int f[1001][1001];
int lin[],cnt=;
struct str
{
int next;
int y;
}e[maxn*maxn];
int q[maxn*maxn];
void insert(int x,int y)
{
e[++cnt].next=lin[x];
lin[x]=cnt;
e[cnt].y=y;
}
int main()
{
int n;
while(scanf("%d",&n)!=EOF)
{
int head=,tail=-;
for(int i=;i<n;i++)
{
scanf("%s",ch);
for(int j=;j<n;j++)
{
a[i][j]=ch[j]-'';
if(i==j)a[i][j]=;
if(a[i][j])
{
insert(i,j);
q[++tail]=i*n+j;
}
if(i!=j&&!a[i][j])a[i][j]=;
}
}
while(head<=tail)
{
int x=q[head]/n,y=q[head]%n;
for(int i=lin[y];i;i=e[i].next)
{
int u=e[i].y;
if(a[x][u]==)
{
a[x][u]=a[x][y]+;
q[++tail]=x*n+u;
} }
head++;
}
long long ans=;
for(int i=;i<n;i++)
{
for(int j=;j<n;j++)
{
if(a[i][j]==)a[i][j]=n;
//printf("%d ",a[i][j]); ans+=a[i][j]*a[i][j];
}
//printf("\n");
}
printf("%lld\n",ans);
}
return ;
}
T的代码
结束后膜拜了了刘巨巨的代码(所谓的dfs外加瞎几把剪枝),感觉很有道理的样子,他写的也是bfs,但是他依旧用邻接矩阵存储,每次bfs固定起点(比如从1号点出发),算出从某个点到所有点的最短路,每次都从小到大去走,于是保证了如果之前访问过编号小的,之后就没必要再次访问,于是他写了一个vis数组,记录被访问过的节点,还有一个head指针,记录下次寻找路径的起点,这就形成了一个简单的“最优化剪枝”,
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int tu[][];
bool vis[];
int head,tail;
int q[];
int lu[];
int n;
int main()
{
long long ans;
while(scanf("%d",&n)!=EOF)
{
ans=;
for(int i=;i<=n;i++)
{
char tem[];
scanf("%s",tem);
for(int j=;j<=n;j++)
{
if(tem[j-]=='')
tu[i][j]=false;
else tu[i][j]=true;
}
}
for(int i=;i<=n;i++)
{
memset(vis,false,sizeof(vis));
memset(lu,0x3f,sizeof(lu));
head=;
lu[i]=;
int tou=,wei=;
//-----------------------------------dfs?
int no=i;
q[++wei]=no;
vis[no]=true;
while(tou<wei)
{
int x;
tou++;
x=q[tou];
for(int ii=head;ii<=n;ii++)
if(!vis[ii]&&tu[x][ii])
{
lu[ii]=lu[x]+;
vis[ii]=true;
q[++wei]=ii;
}
while(vis[head])//这里是关键!!!
head++;
}
//---------------------------------
for(int j=;j<=n;j++)
if(lu[j]<=n)
ans+=lu[j]*lu[j];
else
ans+=n*n;
}
printf("%lld\n",ans);
}
return ;
}
刘巨巨的代码
思路很好,但是实际上是有bug的,如果1号节点被孤立出来,入度为0(出度随意),那么这个剪枝就废了,于是想出了正解,可以用list存储尚未访问的点,每次扫描list判断是否可以拓展,访问一个节点删除一个节点,于是复杂度降到n^2
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=;
int tu[][];
bool vis[];
int head,tail;
int q[];
int lu[];
int n;
struct str
{
int next;
int rev;
}e[];
void ini_e()
{
for(int i=;i<;i++)
e[i].next=i+;
e[].next=;
for(int i=;i<maxn;i++)
e[i].rev=i-;
e[].rev=;
}
void del_e(int xx)
{
int nextt=e[xx].next;
int revv=e[xx].rev;
e[nextt].rev=revv;
e[revv].next=nextt;
}
int main()
{
long long ans;
while(scanf("%d",&n)!=EOF)
{
ans=;
for(int i=;i<=n;i++)
{
char tem[];
scanf("%s",tem);
for(int j=;j<=n;j++)
{
if(tem[j-]=='')
tu[i][j]=false;
else tu[i][j]=true;
}
}
for(int i=;i<=n;i++)
{
memset(vis,false,sizeof(vis));
memset(lu,0x3f,sizeof(lu));
lu[i]=;
int tou=,wei=;
//-----------------------------------dfs?bfs!
ini_e();
int no=i;
q[++wei]=no;
vis[no]=true;
while(tou<wei)
{
int x;
tou++;
x=q[tou];
for(int ii=e[].next;ii;ii=e[ii].next)
if(!vis[ii]&&tu[x][ii])
{
lu[ii]=lu[x]+;
vis[ii]=true;
del_e(ii);
q[++wei]=ii;
}
}
//---------------------------------
for(int j=;j<=n;j++)
if(lu[j]<=n)
ans+=lu[j]*lu[j];
else
ans+=n*n;
}
printf("%lld\n",ans);
}
return ;
}
最终代码
实际上这题目是有标程的,写的很迷,看不懂。。。还在研究(不过据说出题人想卡刘巨巨代码失败了,反而卡死了标程,偶莫西罗依),谁看懂的话麻烦教导我。。。
#include <map>
#include <queue>
#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
using namespace std;
typedef unsigned long long ull;
const ull one=;
ull lowbit(ull n)
{
return n&(-n);
}
const int perbit=;
map<ull,int> mp;
struct Bitset
{
vector<ull> vec;
ull bitmap;
void init(int n)
{
vec.clear();
bitmap=;
while(n>=perbit)
{
vec.push_back(-);
n-=perbit;
bitmap<<=;
}
if(n) vec.push_back((one<<n)-),bitmap<<=;
bitmap-=;
}
void remove(int i)
{
vec[i/perbit]^=(one<<(i%perbit));
if(vec[i/perbit]==)
bitmap^=(one<<(i/perbit));
}
int getNext()
{
if(bitmap==) return -;
int cur=mp[lowbit(bitmap)];
int ans=cur*perbit+mp[lowbit(vec[cur])];
remove(ans);
return ans;
}
};
const int maxn=1e3+;
char g[maxn][maxn];
ull dis[maxn];
ull bfs(int s,int n)
{
memset(dis,-,sizeof(dis));
queue<int> que;
que.push(s);
dis[s]=;
Bitset bit;
bit.init(n);
bit.remove(s);
while(!que.empty())
{
Bitset cur=bit;
int u=que.front();que.pop();
int v;
while((v=cur.getNext())!=-)
{
if(g[u][v]=='')
{
bit.remove(v);
dis[v]=dis[u]+;
que.push(v);
}
}
}
ull ans=;
for(int i=;i<n;i++)
if(dis[i]==-)
ans+=n*n;
else
ans+=dis[i]*dis[i];
return ans;
}
int main()
{
for(int i=;i<;i++)
mp[one<<i]=i;
int n;
while(scanf("%d",&n)!=EOF)
{
for(int i=;i<n;i++)
scanf("%s",g[i]);
ull ans=;
for(int i=;i<n;i++)
ans+=bfs(i,n);
printf("%llu\n",ans);
}
return ;
}
E题迷之标程
F题
题面写的超级迷,要不是后来在讨论版有了详细解释真的是无从下手
题目就是说给你一个n,然后给你n个数字,保证这n个数字是各不相同的且都大于0小于n+1(这意味着这n个数字是1……n的一个排列),然后问你有多少个长度为n的排列,在从小到大sort后保证该排列中数字的相对顺和给定排列中数字顺序相同
(可能我解释的也有点儿迷,我们来看样例)
样例输入
3
2 1 3
样例输出
15
注释
/*
I'd like to interpret the second test case.
3
2 1 3
As we know, there are 27 kinds of permutation of {1,2,3} .
They are
{1,1,1}{1,1,2}{1,1,3}{1,2,1}{1,2.2}{1,2,3}{1,3,1}{1,3,2}{1,3,3}
{2,1,1}{2,1,2}{2,1,3}{2,2,1}{2,2,2}{2,2,3}{2,3,1}{2,3,2}{2,3,3}
{3,1,1}{3,1,2}{3,1,3}{3,2,1}{3,2,2}{3,2,3}{3,3,1}{3,3,2}{3,3,3}
Now, define a new way to sort (given in the test case):2 1 3
which means for each permutation above put all the '2' first,
put all the '1' after all the '2', and then put all the '3' after all the '1'.
After that, if this kind permutation is in ascending order.It can be count!
So {1,1,1}{1,1,3}{1,3,1}{1,3,3} {2,2,2}{2,2,3}{2,3,2}{2,3,3}
{3,1,1}{3,1,3}{3,2,2}{3,2,3}{3,3,1}{3,3,2}{3,3,3} THIS 15 kinds can be counted.
*/
(以上摘自官方解释)
{1,3,1}为什么可以?因为sort后是{1,1,3},1在3的前面,和给定的排列是一样的
{2,1,1}为什么不行,因为sort后是{1,1,2},1在2的前面,和给定的排列相左。
题目理解了我们开始想思路。
其实初步的思路很简单,很容易看出来和给定排列的上升子序列还有排列组合容斥原理有关,不需要枚举具体的排列(废话),具体关系又是什么呢?
/*
F Sort It
首先我们考虑这样一件事情,怎样的一个串是p sortable的。
(1)串中数字的位置是无关紧要的,重要的是这个串是由哪些数字组成的
(2)串中组成的数字必定是p中的一个上升子序列
因此这个题目的答案就是g[i]*f[i] (for i = 1 to n),g[i]是长度为i的上升子序列的个数,f[i]是用i个数字组成长度为n的串(其中每个数至少出现一次)的方法数。
对于g[i],我们考虑这样一个dp,dp[i][j],代表以第i个数为结尾,并且长度为j的上升子序列有多少个,一个朴素的实现是O(n^3)的,显然要优化,自己去想= =
对于f[i],容斥即可。
*/
(以上摘自谢学长题解)
谢学长帮我理顺了思路,让我不至于一头雾水,但是接下来任务依旧不简单,需要求解g数组和f数组
--------------------------------------------------------------------------------------------------------------------
先说f数组
一开始我也以为是容斥原理,或者是高级的组合数学,推了很久推不出来,后来我去百度了一下,莫名其妙的百度到了“第二类斯特林数”,然后为递推方法求解f数组提供了思路
(PS:第二类Stirling数实际上是集合的一个拆分,表示将n个不同的元素拆分成m个集合的方案数,第二类其实是和我们有区别的,他的集合之间没有区别,我们的有)
stirl[i][j]表示i种数字组成j个数字的排列,且每种数字至少出现一次的方法数
那么stirl[i][j]=(stirl[i-1][j-1]+stirl[i-1][j-1])*i;
我们考虑最后一位的数字,如果这个数字不同于前面所有的数字,那么前面的数字就是i-1个数排j-1位;否则就是i个数排j-1位。因为最后一个数字可以是任意的i个数之一,所以要乘i。
于是,stirl[i][n]就是题解中提到的f[i],求解复杂度为n^2,可以
------------------------------------------------------------------------------------------------------------------------
好,接下来是求解g数组
朴素的dp方法是n^3的,原理简单,f[i][j]以第j个数字结尾的长度为i的上升子序列的个数
f[i][j]=sum(f[i-1][k])(k<j且第k位的数字小于第j位的数字)
这个复杂度是n^3的,2000的数据范围受不了
可以优化么?答案是可以的,最内层的循环(就是累加的那个)可以用树状数组压缩一下
(每次求和的花销由n降至logn,复杂度可接受)
计算该层的时候,把上一层的数据压到树状数组里面
于是f[i][j]=sum(a[j]);add(a[j],f[i-1][j]);
于是 g[i]=sum(f[i][1……n])。
--------------------------------------------------------------------------------------------------------------------------
不得不承认,这个题解写的好迷。。。直接看代码吧。。。发着烧补的。。。能过掉自己都感觉神奇
(PS茅巨巨内层的写法和我的不太一样。。。)
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
#define ll long long
const ll modd=;
const int maxn=;
ll stirl[maxn][maxn];//i种数
ll f[maxn][maxn];
ll bit[maxn];
inline int lowbit(int x)
{
return x&(-x);
}
int n=;
int a[maxn];
ll read(int x)
{
ll sum=;
while(x)
{
sum+=bit[x];
x-=lowbit(x);
}
sum%=modd;
return sum;
}
void add(int x,ll num)
{
while(x<=n)
{
bit[x]+=num;
bit[x]%=modd;
x+=lowbit(x);
}
}
int main()
{
//freopen("test.out","w",stdout);
for(int i=;i<maxn;i++)
{
stirl[][i]=;
}
for(int i=;i<maxn;i++)
{
for(int j=;j<maxn;j++)
{
stirl[i][j]=(stirl[i-][j-]+stirl[i][j-])*i;
stirl[i][j]%=modd;
}
}
while(~scanf("%d",&n))
{
for(int i=;i<=n;i++)
{
scanf("%d",&a[i]);
}
memset(f,,sizeof(f));
ll ans=;
for(int i=;i<=n;i++)
{
f[][i]=;
}
ans=n;
for(int i=;i<=n;i++)
{
memset(bit,,sizeof(bit));
ll sum=;
for(int j=;j<=n;j++)
{
ll temp=read(a[j]);
f[i][j]=temp;
//printf("%lld ",temp);
sum+=temp;
add(a[j],f[i-][j]);
}
//printf("\n");
sum%=modd;
sum*=stirl[i][n];
sum%=modd;
ans+=sum;
ans%=modd;
}
/*
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
printf("%lld ",stirl[i][j]);
}
printf("\n");
}
*/
printf("%lld\n",ans);
}
return ;
} /*
7
7 2 1 4 6 3 5
*/
F的最终代码
这个代码我一开始怎么提交怎么错,但是感觉我的stirl和f数组都没有写错,当我学习了对拍后,发现n超过7我就开始出错,于是打表,发现stirl[1][7]==0;
然后就发现stirl数组第一行循环的上界,我把maxn错写成n。。。。然而n的初值我定义为6.。。
另外一个错误是sum函数和add函数,我认为在树状数组中数字不会超过int,但是实际上会的,注意随时取余。
------------------------------------------------------------------------------------------------------------------------------
NEU2016年一月月赛回顾的更多相关文章
- CTF-安恒19年一月月赛部分writeup
CTF-安恒19年一月月赛部分writeup MISC1-赢战2019 是一道图片隐写题 linux下可以正常打开图片,首先到binwalk分析一下. 里面有东西,foremost分离一下 有一张二维 ...
- zstu19一月月赛 duxing201606的原味鸡树
duxing201606的原味鸡树 题意: 给定一颗有n(n<=1e9)个节点的完全二叉树,1e5次询问,问某个节点有几个子节点. 思路: 自己在月赛上没有思路,问了zfq才知道. 设两个指标, ...
- 2015年NEUACM一月月赛题解
A Money , money 时间限制: 1 Sec 内存限制: 128 MB 提交: 15 解决: 14 题目描述 Small K seen recently stock market rea ...
- Code Chef DARTSEGM(计算几何+凸包)
题面 传送门 题解 好眼熟丫-- 一月月赛最后一题--,代码都不用改-- //minamoto #include<bits/stdc++.h> #define R register #de ...
- USACO月赛数据
终于找到了usaco月赛的数据…… 根据月赛的名称,我们可以写出数据地址.比如08年一月的月赛即是:http://contest.usaco.org/JAN08 这里要注意区分大小写.
- MSSQL 简单练习回顾
这段时间,报了浦软培训的.NET,现在整理回顾下,算是个小小总结吧 为了便于操作,我没有在多个数据库间切换数据库实例,以一个总的数据库实例 test_demo为源进行的相关操作,代码的注释根据我的理解 ...
- 【洛谷】【洛谷月赛】4月月赛Round 1/2
洛谷月赛“月”来“月”丧了,一月更比一月丧,做得我十分不“月”…… 4月的两轮月赛,都只会T1,就写一下吧,等待后续更新…… 先看看Round1的T1: [R1T1] 网址:点我 [题意简述] 给定一 ...
- 安恒月赛WP
一月 一叶飘零大佬的WP:安恒月赛一月 二进制部分:zjgcjy大佬的WP reverse1更容易理解的一种解法 pwn1详解 二月 一叶飘零WP 二进制部分: reverse Pwn 三月 ...
- JavaScript: 2015 年回顾与展望
链接:http://www.sitepoint.com/javascript-2015-review/ JavaScript经历了一个不平凡的一年.尽管到5月份已经20年了,关于JS的新闻.项目和兴趣 ...
随机推荐
- window下svn开机自动启动
- jQueryDOM操作模块
DOM操作模块 1.复习选择器模块(选择器模块结束) 目的:学而时习之 复习和总结选择器模块 2.DOM的基本操作方法 目标:回顾DOM操作的基本方法 3.1 DOM操作 -创建节点 练习 1:创建1 ...
- ESLint 规范项目代码
ESLint 由 JavaScript 红宝书 作者 Nicholas C. Zakas 编写, 2013 年发布第一个版本. NCZ 以可扩展.每条规则独立.不内置编码风格为理念编写了一个 lint ...
- 转载:关于 python ImportError: No module named 的问题
关于 python ImportError: No module named 的问题 今天在 centos 下安装 python setup.py install 时报错:ImportError: N ...
- 换个语言学一下 Golang (2)——基础语法
Go 标记 Go 程序可以由多个标记组成,可以是关键字,标识符,常量,字符串,符号.比如下面的hello world就是由 6 个标记组成: 行分隔符 在 Go 程序中,一行代表一个语句结束.每个语句 ...
- Java中 ArrayList类常用方法和遍历
ArrayList类对于元素的操作,基本体现在——增.删.查.常用的方法有: public boolean add(E e) :将指定的元素添加到此集合的尾部. public E remove(in ...
- 【转】linux 增加用户 useradd 用法小结及配置文件说明
原文地址:https://blog.csdn.net/warden2010/article/details/5132290 在Linux 系统中,所有的用户和组像一个国家.如果国家要繁荣昌盛的话,需要 ...
- BZOJ 3940: [Usaco2015 Feb]Censoring AC自动机_栈
Description Farmer John has purchased a subscription to Good Hooveskeeping magazine for his cows, so ...
- 前端开发—CSS
CSS 基础概念 致命三问: 它是什么? 层叠样式表,主要作用是对html标签进行装饰. 它的作用:再 html 框架的基础上 ,对标签内容做美化工作. 注释方法:/*单行注释*/ 多行注释同理与h ...
- RESTful API设计方法
1.如果已经开始逐步的接触到了RESTful API设计方法的朋友,首先要对HTTP/HTTPS有一个大致的了解,虽然本身和RESTful API没有什么关系.但是对于增加网站的安全性还是十分重要的, ...