月赛传送门

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年一月月赛回顾的更多相关文章

  1. CTF-安恒19年一月月赛部分writeup

    CTF-安恒19年一月月赛部分writeup MISC1-赢战2019 是一道图片隐写题 linux下可以正常打开图片,首先到binwalk分析一下. 里面有东西,foremost分离一下 有一张二维 ...

  2. zstu19一月月赛 duxing201606的原味鸡树

    duxing201606的原味鸡树 题意: 给定一颗有n(n<=1e9)个节点的完全二叉树,1e5次询问,问某个节点有几个子节点. 思路: 自己在月赛上没有思路,问了zfq才知道. 设两个指标, ...

  3. 2015年NEUACM一月月赛题解

    A Money , money 时间限制: 1 Sec  内存限制: 128 MB 提交: 15  解决: 14 题目描述 Small K seen recently stock market rea ...

  4. Code Chef DARTSEGM(计算几何+凸包)

    题面 传送门 题解 好眼熟丫-- 一月月赛最后一题--,代码都不用改-- //minamoto #include<bits/stdc++.h> #define R register #de ...

  5. USACO月赛数据

    终于找到了usaco月赛的数据…… 根据月赛的名称,我们可以写出数据地址.比如08年一月的月赛即是:http://contest.usaco.org/JAN08  这里要注意区分大小写.

  6. MSSQL 简单练习回顾

    这段时间,报了浦软培训的.NET,现在整理回顾下,算是个小小总结吧 为了便于操作,我没有在多个数据库间切换数据库实例,以一个总的数据库实例 test_demo为源进行的相关操作,代码的注释根据我的理解 ...

  7. 【洛谷】【洛谷月赛】4月月赛Round 1/2

    洛谷月赛“月”来“月”丧了,一月更比一月丧,做得我十分不“月”…… 4月的两轮月赛,都只会T1,就写一下吧,等待后续更新…… 先看看Round1的T1: [R1T1] 网址:点我 [题意简述] 给定一 ...

  8. 安恒月赛WP

    一月 一叶飘零大佬的WP:安恒月赛一月 二进制部分:zjgcjy大佬的WP reverse1更容易理解的一种解法 pwn1详解 二月 一叶飘零WP 二进制部分: reverse Pwn 三月 ...

  9. JavaScript: 2015 年回顾与展望

    链接:http://www.sitepoint.com/javascript-2015-review/ JavaScript经历了一个不平凡的一年.尽管到5月份已经20年了,关于JS的新闻.项目和兴趣 ...

随机推荐

  1. .net中的母版页中使用FindControl的使用

    前几天,遇到一个字段比较多的用户填写的页面(数据库表中就将近100个字段),怎么讲这些input的标签的值,保存数据库了?(使用的是母版页下面的aspx,不包括前段获取input的值,传给后台) 作为 ...

  2. Centos7下Docker的使用

    一.安装Docker 1.1.查看原有系统是否已经安装docker yum list installed | grep docker 1.2.如果有则不需要继续安装,想重新安装,先卸载 yum -y ...

  3. 微信小程序怎么开发(小程序开发文档)

    微信小程序是一种全新的连接用户与服务的方式,它可以在微信内被便捷地获取和传播,同时具有出色的使用体验. 2017年1月9日~2018年1月9日,小程序正式上线一周年.上线以来,小程序不断地释放新能力, ...

  4. 第八课: - 从Microsoft SQL数据库读取

    第 8 课 如何从Microsoft SQL数据库中提取数据 In [1]: # Import libraries import pandas as pd import sys from sqlalc ...

  5. Mybatis xml约束文件的使用

    一:准备.DTD约束文件      核心配置文件约束文件:mybatis-config.dtd <?xml version="1.0" encoding="UTF- ...

  6. 查询 MySQL 库/表相关信息

    SHOW DATABASES //列出 MySQL Server 数据库. SHOW TABLES [FROM db_name] //列出数据库数据表. SHOW CREATE TABLES tbl_ ...

  7. 有关Gradle Network is unreachable: connect的报错

    项目Gradle   Errer:Network is unreachable: connect 同时还有as的 报错 Internal HTTP server disabled: Cannot st ...

  8. 基于ACE的TAO开发---一个简单的入门实例-----VS2008(二)

    上一节已经说了如何编译idl文件.现在就用编好的文件来写一个最小的corba小程序的.程序分为服务器程序和客户端程序. 说明下,代码是<基于C++CORBA高级编程>一书中的例子. 1.首 ...

  9. 用Navicat Prenium12连接Oracle数据库(oracle11g版本)时报错ORA-28547:connection to server failed,probable Oracle Net admin error.解决办法

    上网一查原来是oci.dll版本不对.因为Navicat是通过Oracle客户端连接Oracle服务器的,Oracle的客户端分为两种,一种是标准版,一种是简洁版,即Oracle Install Cl ...

  10. Project Euler 30 Digit fifth powers

    题意:判断一个数 N 的每一位的5次方的和是否为其本身 ,求出所有满足条件的数的和 思路:首先设这个数 N 为 n 位,可以简单的判断一下这个问题的上界 10 ^ n <= 9 ^ 5 × n, ...