Problem A awesome

  给出一个序列$A_i$,任取序列中三个数组成三元组$(a_i , a_j , a_k)$。

  输出本质不同的且$abc \equiv 1 (mod  P)$且满足$a \leq b \leq c$的三元组$(a,b,c)$的组数。

  对于$100\%$的数据满足$n \leq 2333 , P \in Prime$

Soltuion :

  本题显然会卡常数,并且出了非常暧昧的数据范围。

  设$n$不去重前的数据规模,而$m$是去重前的数据规模。

  我们可以使用$O(n)$的暴力处理三元组中三个数都相同的情况。

  我们可以使用$O(n^2)$暴力处理三元组中两个数的情况。

  我们可以使用$O(n^2 log_2 n)$暴力处理三元组中所有数都不同的情况。

  我们将数组中每个数以其模$P$的余数为键值插入到hash表中。

  然后对于去重后的数组$O(m^2)$枚举两个不同的数,然后通过逆元从hash表中取出对应的数集即可。

  可以通过一个$lower_bound$来计数。

  时间复杂度为$O(n^2 log_2 n)$。

# pragma GCC optimize()
# include <bits/stdc++.h>
# define int long long
# define hash Hash
using namespace std;
const int N=3e3+;
int n,p,a[N],s[N],inv[N];
vector<int>tmp;
int Pow(int x,int n,int mo) {
int ans = ;
while (n) {
if (n&) ans =ans * x %mo;
x =x *x % mo;
n>>=;
}
return ans % mo;
}
struct Node {int key; vector<int>v;};
vector<Node> hash[];
void insert(int key,int val) {
int to = key % ;
for (int i=;i<hash[to].size();i++) {
if (key == hash[to][i].key) {
hash[to][i].v.push_back(val);
return ;
}
}
Node tmp; tmp.key=key; tmp.v.push_back(val);
hash[to].push_back(tmp);
}
vector<int>ttt;
bool find(int key) {
int to = key % ;
for (int i=;i<hash[to].size();i++) {
if (key == hash[to][i].key) {
ttt = hash[to][i].v;
return ;
}
}
return ;
}
signed main() {
scanf("%lld%lld",&n,&p);
for (int i=;i<=n;i++) scanf("%lld",&a[i]);
sort(a+,a++n);
for (int i=;i<=n;i++) {
for (int j=;j<=n;j++)
if (a[i] == a[j]) s[i]++;
}
for (int i=;i<=n;i++) inv[i] = Pow(a[i],p-,p);
for (int i=;i<=n;i++) tmp.push_back(a[i]);
tmp.erase(unique(tmp.begin(),tmp.end()),tmp.end());
for (int i=;i<tmp.size();i++) insert(tmp[i]%p,tmp[i]);
int ans = ;
for (int i=;i<=n;i++) {
if (a[i] == a[i-] && i!=) continue;
if (s[i]<) continue;
if (a[i] * a[i] % p * a[i] % p == ) ans++;
}
for (int i=;i<=n;i++) {
if (a[i] == a[i-] && i!=) continue;
if (s[i] < ) continue;
for (int j=;j<tmp.size();j++) if (tmp[j] != a[i] && a[i] * a[i] % p * tmp[j] % p == ) ans++;
}
for (int i=;i<=n;i++) {
if (a[i] == a[i-] && i!=) continue;
for (int j=i+;j<=n;j++) {
if (a[j] == a[j-] && j!=i+) continue;
if (a[i] >= a[j]) continue;
if (a[i] % p == || a[j] % p == ) continue;
int key = * inv[i] * inv[j] % p;
if (!find(key)) continue;
ans += ttt.end()-upper_bound(ttt.begin(),ttt.end(),a[j]);
}
}
printf("%lld\n",ans);
return ;
}

awesome.cpp

  Problem B bag

  有$n$个包和$m$个物品,每个包有容量$a_i$,每个物品有体积$w_i$价值$v_i$。

  选出$k$个物品,满足每个物品可以放置在$1$个包中,且可以通过排列所有包使得包内物品$w_i,v_i$都单调不增。

  输出最大的$k$。

  对于$100\%$的数据满足$1 \leq n,m \leq 10^5$

  Solution :

    首先将$w_i$按照降序排序,当$w_i$相同的时候按照$v_i$降序以最大化$k$。

    首先考虑到$k$个物品放入$k$个包,显然会选择$k$个最大的包放入。

    问题就转化为对价值$v_i$做一个最长不上升子序列。

    设$f[i]$表示考虑到第$i$的位置最长不上升子序列的值,且强制第$i$个人必须选。

    朴素的$dp$方程为$f[i] = max(f[i] , f[j] + 1)$,其中$j$能转移到$i$的条件是$v[j] \geq v[i]$且$t[f[j] + 1] \geq w[i]$

    这样的时间复杂度为$O(n^2)$

    可以通过改变状态为$f[i]$表示长度为$i$的最长不上升子序列最后一个数的最大值是多少。

    可以看出$f[i+1] \leq f[i]$ 所以$f$是单调的。

    所以可以二分出最后一个$j$使得$f[j] \geq v[i]$ ,用$v[i]$更新$f[j+1]$的值。

    所以,此时复杂度为$O(nlog_2n)$

# include<bits/stdc++.h>
# define int long long
using namespace std;
const int N=1e5+;
struct rec{int w,v;}a[N<<];
bool cmp(rec a,rec b) {
if (a.w == b.w) return a.v>b.v;
else return a.w > b.w;
}
int n,m,t[N],f[N];
signed main() {
int tt; scanf("%lld",&tt);
while (tt--) {
scanf("%lld",&n);
for (int i=;i<=n;i++) {
scanf("%lld%lld",&a[i].w,&a[i].v);
}
sort(a+,a++n,cmp);
scanf("%lld",&m);
for (int i=;i<=m;i++) {
scanf("%lld",&t[i]);
}
memset(f,,sizeof(f));
sort(t+,t++m); reverse(t+,t++m);
for (int i=;i<=n;i++) {
int l=,r=n,p=;
while (l<=r) {
int mid=(l+r)>>;
if (f[mid]>=a[i].v) p=mid,l=mid+;
else r=mid-;
}
if (a[i].w<=t[p+] && p+<=m) f[p+] = max(f[p+],a[i].v);
}
for (int i=n;i>=;i--) if (f[i] != ) {
printf("%lld\n",i);
break;
}
}
return ;
}

bag.cpp

  Problem C subtree

 给出$n,k$,和限制$a_1 , a_2 , ... , a_k$

  输出有根树的个数,使得不存在任意一个节点的size,在集合$a_1 , a_2 , ... , a_k$中。

  设该树的深度为$i$,给出$[l,r]$,输出$i \in [l,r]$的答案。

  对于$100\%$的数据$1 \leq n \leq 500$.

   Solution:

    设$f[i][j]$表示有$n$个节点的有根树,设其根为$1$,深度为$j$的方案数。

    考虑将一棵含有$j$个节点的有根子树的编号强制为$2$ , 拼接到根节点$1$上。

    那么就划分为$f[j][d-1]$($2$为根节点的子树)和$f[i-j][d]$(另外一部分树)两个子问题。

    同时考虑到除去编号为$1$和编号为$2$两个节点,其他的节点可以任意划分,所以有一个组合数$\binom{i-2}{j-1}$

    所以动态规划方程为$f[i][d] = \sum\limits_{j = 1} ^{i-1} f[j][d-1] \times f[i-j][d] \times \binom{i-2}{j-1} $   

    时间复杂度为$O(n^3)$

# include<bits/stdc++.h>
# define int long long
using namespace std;
const int N=;
const int mo=;
int n,k;
int f[N][N],c[N][N];
bool flag[N];
signed main()
{
// freopen("data.in","r",stdin);
scanf("%lld%lld",&n,&k);
for (int i=;i<=n;i++) {
c[i][] = c[i][i] = ;
for (int j=;j<i;j++)
c[i][j] = (c[i-][j-] + c[i-][j])%mo;
}
for (int i=;i<=k;i++) {
int t; scanf("%lld",&t); flag[t] = ;
}
if (flag[]) f[][]=; else f[][]=;
for (int d=;d<=n;d++) {
f[][d] = ;
for (int i=;i<=n;i++) {
for (int j=;j<=i-;j++)
(f[i][d]+=f[j][d-]*f[i-j][d]%mo*c[i-][j-]%mo)%=mo;
}
for (int i=;i<=n;i++) if (flag[i]) f[i][d] = ;
}
int l,r; scanf("%lld%lld",&l,&r);
for (int i=l;i<=r;i++) printf("%lld ",((f[n][i]-f[n][i-])%mo+mo)%mo);
return ;
}

subtree.cpp

HGOI 20191101am 题解的更多相关文章

  1. HGOI 20181028 题解

    HGOI 20181028(复赛备考) /* 真是暴力的一天,最后一题MLE?由于数组开得太大了!!! 270滚粗 考场上好像智商高了很多?!(假的) */ sol:暴力求解,然后没有数据范围吐槽一下 ...

  2. HGOI 20190310 题解

    /* 又是又双叒叕WA的一天... 我太弱鸡了... 今天上午打了4道CF */ Problem 1 meaning 给出q组询问,求下列函数的值$ f(a) = \max\limits_{0 < ...

  3. HGOI 20190303 题解

    /* 记一串数字真难. 5435 今天比赛又是hjcAK的一天. 今天开题顺序是312,在搞T1之前搞了T3 昨天某谷月赛真是毒瘤. 但是讲评的同学不错,起码T4看懂了... 构造最优状态然后DP的思 ...

  4. HGOI 20180224 题解

    /* The Most Important Things: ljc chat with fyh on QQTa说期末考Ta数学74分感觉不好但是我觉得fyh是地表最强的鸭~~(of course en ...

  5. HGOI 20190218 题解

    /* 又是AK局... hjc又双叒叕AK了... Hmmm...我侥幸 */ Problem A card 给出无序序列a[]可以选择一个数插入到合适的位置作为一次操作,至少多少次操作后可以把序列变 ...

  6. HGOI 20190217 题解

    /* for me,开训第一天 /beacuse 文化课太差被抓去补文化课了... 看一眼题 : AK局? 但是,Wa on test #10 in problem C 290! (就差那么一咪咪) ...

  7. HGOI 20181103 题解

    problem:把一个可重集分成两个互异的不为空集合,两个集合里面的数相乘的gcd为1(将集合中所有元素的质因数没有交集) solution:显然本题并不是那么容易啊!考场上想了好久.. 其实转化为上 ...

  8. HGOI 20181101题解

    /* 又是爆0的一天(不知道今年高考难不难,反正今天(信息学)真的难!) */ solution:对于两个数相加,有一个显然的结论就是要么不进位(相对于位数大的),要么(进最多一位) 然后对于整个数组 ...

  9. HGOI 20191108 题解

    Problem A 新婚快乐 一条路,被$n$个红绿灯划分成$n+1$段,从前到后一次给出每一段的长度$l_i$,每走$1$的长度需要$1$分钟. 一开始所有红绿灯都是绿色的,$g$分钟后所有红绿灯变 ...

随机推荐

  1. PHP和js判断访问设备是否是微信浏览器实例

    PHP和js判断访问设备是否是微信浏览器实例,代码非常精简,适合新手学习. js判断是否是微信浏览器: 1 function is_weixin() { 2 var ua = window.navig ...

  2. spring-data-redis 2.0 的使用

    在使用Spring Boot2.x运行Redis时,发现百度不到顺手的文档,搞通后发现其实这个过程非常简单和简洁,觉得有必要拿出来分享一下. Spring Boot2.x 不再使用Jedis,换成了L ...

  3. Web文件上传靶场 - 通关笔记

    Web应用程序通常会提供一些上传功能,比如上传头像,图片资源等,只要与资源传输有关的地方就可能存在上传漏洞,上传漏洞归根结底是程序员在对用户文件上传时控制不足或者是处理的缺陷导致的,文件上传漏洞在渗透 ...

  4. Python 面向对象编程详解

    Python 的创始人为吉多·范罗苏姆(Guido van Rossum).1989年的圣诞节期间,吉多·范罗苏姆为了在阿姆斯特丹打发时间,决心开发一个新的脚本解释程序,作为ABC语言的一种继承.Py ...

  5. 牛客 72C 小H和游戏 (动态点分治)

    大意: 给定树, 每个点初始权值0, 每次询问给出$x$, $x$权值+1, 求距离$x$不超过2的权值和. 这题数据范围过大, 动态点分治卡不过去, 考虑其他做法 考虑每次只加范围$1$, c[0] ...

  6. Git FLS的使用

    克隆git地址后,一些文件内容被隐藏. 显示如下: version https://git-lfs.github.com/spec/v1oid sha256:xxxxxxxxxxxxxxxxxxxxx ...

  7. VisualVM的使用

    1.解压压缩包(如visualvm143.zip) 2.修改etc/visualvm.conf 中的visualvm_jdkhome配置 3.双击bin/visualvm.exe 4.安装插件,可能一 ...

  8. vue打包后找不到资源路径问题

    问题描述: 使用webpack打包vue项目后,前后端联调无法找到资源 解决方案: 一. 改为相对路径,去除axios中地址的第一个“/” onProxyReq: function (proxyReq ...

  9. JS-实现横向手风琴

    横向手风琴-- 鼠标悬浮某一张图片,图片显示,其他图片影藏. <div class="content"> <ul> <li class="c ...

  10. shake.js实现微信摇一摇功能

    项目要求实现点击摇一摇图片,图片摇一摇,并且摇一摇手机,图片也要摇一摇. 关于用js怎样实现摇一摇手机图片摇一摇,我在网络上找了一些方法,真正有用的是shake.js. 接下来,上shake.js源码 ...