Problem A 钥匙

有$n$个人和$m$个钥匙在数轴上,人的坐标为$a_i$,钥匙的坐标为$b_i$

而门的坐标为$p$,要让所有人获得一把不同钥匙,并且到达门,最长时间最短是多少。

对于$100\%$的数据满足$10^3 \leq n \leq 10^3 , n \leq k \leq 2\times 10^3$

Solution :

  对于部分数据,可以二分答案然后进行二分图匹配,实测可以通过$80\%$的数据。

  事实上,对上面算法的极限复杂度是$O(n^2 k log_2 10^9)$

  事实上,我们可以将$nk$中人和钥匙的配对方案求出,排序后,直接从小往大贪心。

  这样子复杂度是$O(n \times k \times (\ log_2 n + log_2 k))$的。

# include <bits/stdc++.h>
# define int long long
using namespace std;
const int N=1e6+;
int n,k,p,a[N],b[N];
inline int read() {
int X=,w=; char c=;
while(c<''||c>'') {w|=c=='-';c=getchar();}
while(c>=''&&c<='') X=(X<<)+(X<<)+(c^),c=getchar();
return w?-X:X;
}
int calc(int i,int j) {
if (a[i]<=b[j] && b[j]<=p) return p-a[i];
if (a[i]<=p && p<=b[j]) return b[j]-a[i]+b[j]-p;
if (b[j]<=a[i] && a[i]<=p) return a[i]-b[j]+p-b[j];
if (b[j]<=p && p<=a[i]) return a[i]-b[j]+p-b[j];
if (p<=a[i] && a[i]<=b[j]) return b[j]-a[i]+b[j]-p;
if (p<=b[j] && b[j]<=a[i]) return a[i]-p;
}
namespace Subtask1 {
vector<int>E[N];
bool vis[N];
int pre[N];
bool find(int u) {
int sz = E[u].size();
for (int i=;i<sz;i++) {
int v=E[u][i]; if (vis[v]) continue;
vis[v]=true;
if (pre[v]==- || find(pre[v])) {
pre[v]=u;
return true;
}
}
return false;
}
int solve() {
int ans = ;
memset(pre,-,sizeof(pre));
for (int i=;i<=n;i++) {
memset(vis,false,sizeof(vis));
if (find(i)) ans++;
}
return ans;
}
bool check(int Mid) {
for (int i=;i<=n;i++) E[i].clear();
for (int i=;i<=n;i++)
for (int j=;j<=k;j++)
if (calc(i,j)<=Mid) E[i].push_back(j);
int ans = solve();
return (ans==n);
}
void main() {
int l=,r=2e9,ans;
while (l<=r) {
int mid = (l+r)>>;
if (check(mid)) r=mid-,ans=mid;
else l=mid+;
}
cout<<ans<<'\n';
}
}
namespace Subtask2 {
bool vis1[N],vis2[N];
struct node {
int u,v,val;
}v[N];
bool cmp (node a,node b) {
return a.val<b.val;
}
void main() {
int cnt = ;
for (int i=;i<=n;i++)
for (int j=;j<=k;j++)
v[++cnt]=(node){i,j,calc(i,j)};
sort(v+,v++cnt,cmp);
int ans = ;
for (int i=;i<=cnt;i++) {
node tmp = v[i];
if (vis1[tmp.u] || vis2[tmp.v]) continue;
ans=max(tmp.val,ans);
vis1[tmp.u] = vis2[tmp.v] = ;
}
printf("%lld\n",ans);
}
}
signed main() {
// freopen("key.in","r",stdin);
//freopen("key.out","w",stdout);
n=read();k=read();p=read();
for (int i=;i<=n;i++) a[i]=read();
for (int i=;i<=k;i++) b[i]=read();
if (n*k <= //n) Subtask1::main();
else Subtask2::main();
return ;
}

A.cpp

Problem B 汪哥图

给出$n\times m$的$01$矩阵,保证这个矩阵中任意两个$1$之间最多有一条路径不经过$0$。

给出$Q$组询问,求子矩阵联通块个数。

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

Solution :

  我们先假设这个子矩阵中所有的$1$都自成一格连通块,显然,有好多连通块可以合并。

  由于矩阵中的性质: 任意两个$1$之间最多有一条路径不经过$0$,所以合并的方式一定是从左到右或者从上到下。

  即如果$a[i][j] $和$a[i+1][j]$都是$1$,那么必然会减少一个连通块,如果$a[i][j]$和$a[i][j+1]$都是$1$,也必然会减少一个连通块。

  所以,我们定义$f[i][j] = [a[i][j] = 1 ] \times [a[i+1][j] = 1] , g[i][j] =  [ a[i][j] = 1 ] \times   [a[i][j+1] = 1] $

  对于左上角坐标为$(x_1,y_1)$,右下角坐标为$(x_2,y_2)$的子矩阵,答案是$ans = \sum\limits_{i=x_1}^{x_2} \sum\limits_{j=y_1} ^ {y_2} a[i][j] -  \sum\limits_{i=x_1}^{x_2 - 1} \sum\limits_{j=y_1} ^ {y_2}  f[i][j] -  \sum\limits_{i=x_1}^{x_2} \sum\limits_{j=y_1} ^ {y_2-1} g[i][j]$

  显然可以用二维前缀和优化。

  复杂度是$O(nm + Q)$

# include <bits/stdc++.h>
using namespace std;
const int N=2e3+;
int a[N][N],c1[N][N],c2[N][N],sum1[N][N],suma[N][N],sum2[N][N];
int n,m,q;
inline int read() {
int X=,w=; char c=;
while(c<''||c>'') {w|=c=='-';c=getchar();}
while(c>=''&&c<='') X=(X<<)+(X<<)+(c^),c=getchar();
return w?-X:X;
}
void write(int x) {
if (x>) write(x/);
putchar(''+x%);
}
int calca(int x1,int y1,int x2,int y2){
return suma[x2][y2]-suma[x2][y1-]-suma[x1-][y2]+suma[x1-][y1-];
}
int calc1(int x1,int y1,int x2,int y2){
if (x2<x1 || y2<y1) return ;
return sum1[x2][y2]-sum1[x2][y1-]-sum1[x1-][y2]+sum1[x1-][y1-];
}
int calc2(int x1,int y1,int x2,int y2) {
if (x2<x1 || y2<y1) return ;
return sum2[x2][y2]-sum2[x2][y1-]-sum2[x1-][y2]+sum2[x1-][y1-];
}
int main() {
n=read();m=read();q=read();
for (int i=;i<=n;i++)
for (int j=;j<m;j++) {
char op=; while (op!='' && op!='') op=getchar();
a[i][j+]=op-'';
}
for (int i=;i<=n;i++)
for (int j=;j<=m;j++)
c1[i][j]=a[i][j] && a[i+][j],
c2[i][j]=a[i][j] && a[i][j+];
for (int i=;i<=n;i++)
for (int j=;j<=m;j++)
suma[i][j]=suma[i-][j]+suma[i][j-]-suma[i-][j-]+a[i][j],
sum1[i][j]=sum1[i-][j]+sum1[i][j-]-sum1[i-][j-]+c1[i][j],
sum2[i][j]=sum2[i-][j]+sum2[i][j-]-sum2[i-][j-]+c2[i][j];
while (q--) {
int x1=read(),y1=read(),x2=read(),y2=read();
int ans = calca(x1,y1,x2,y2)-calc1(x1,y1,x2-,y2)-calc2(x1,y1,x2,y2-);
write(ans); putchar('\n');
}
return ;
}

B.cpp

Problem C 时空阵

本题时间限制$4000 ms$

有$n$个点,用最多$\frac{n(n-1)}{2}$条边权为$1$的双向边连接成一个图,

给出$m$,要求在构造出的图中,有$1 - n$的最短路为$m$的图的个数$mod \ 10^9 + 7$的值。

当且仅当两个图中连边集合不同,则两个图不同。

对于$100\%$的数据满足$1 \leq m  < n\leq 100 $。

Solution :

  假设把这些点中的若干个点分成$m+1$个集合,编号为$[0,m]$ , 每个集合中至少有$1$个点,并且$1$个点最多在$1$个集合中。

  并且保证,任取$i \in [0,m-1]$,第$i$个集合和第$i+1$个集合中的点直接的距离是$1$。强制第$0$个集合为出发点,只有一个元素$1$.

  为了体现$1$开始到其他点的最短路,特殊的,要求$1-n$的最短路为$m$,我们强制编号为$0$的集合为出发集,只有一个元素$1$,$n$必须在编号为$m$的集合中

  这样一来,我们只需要计算划分集合的方案数了。

  我们从编号为$1$的集合开始考虑。设$f[i][j][k]$表示考虑到集合$i$,已经放置了$j$个数字,第$i$个集合放置了$k$个数字的方案数。

  我们可以枚举上一个集合放置元素个数$s$,从$f[i-1][j-k][s]$转移过来。

  我们考虑构成当前集合的情况:

    • 要求之前被安放的数字不重复:除了元素$n$之外还剩余$n-1-(j-k)$个数字,有$\binom{n-1-(j-k)}{k}$种可能。
    • 当前集合里元素之间可以无限制连边:有$2 ^ \binom{k}{2} $种可能。
    • 之前一个集合和当前集合至少需要有$1$条连边:有$(2^s - 1) ^ k$种可能

  注意到,要求之前被安放的数字不重复中,若$i = m$的时候,由于最后一个集合中已经被强制放入元素$n$,所以需要另外选择的元素是$k-1$个,所以有$\binom{n-1-(j-k)}{k-1}$种可能。

  所以转移方程为:

  $f[i][j][k] =\left\{\begin{matrix}\sum\limits_{s = 1} ^ {j-k} f[i-1][j-k][s] \times \binom{n-j+k-1}{k} \times 2 ^ {\binom{k}{2}} \times (2^s - 1) ^ k  & (1 \leq i <  m )\\ \sum\limits_{s = 1} ^ {j-k} f[i-1][j-k][s] \times \binom{n-j+k-1}{k-1} \times 2 ^ {\binom{k}{2}} \times (2^s - 1) ^ k  & (i=m) \end{matrix}\right.$

  统计答案的时候,设当前已经将$j$个元素放置在分层图当中了,最后一层中放置了$k$个,那么考虑这些点直接互相连边,这些点和最后一层中所有点之间互相连边的可能即可。

  最后的答案就是$ans = \sum\limits_{j = m+1}^{n} \sum\limits_{k = 1}^{j-m} f[m][i][j] \times 2^{\binom{n-j}{2} + (n-j) \times k}$

  复杂度是$O(n^4)$

# include <bits/stdc++.h>
# define int long long
# define fp(i,s,t) for (int i=s;i<=t;i++)
using namespace std;
const int mo=1e9+;
const int N=;
int c[N][N],n,m,f[N][N][N],Max;
int Pow(int x,int n)
{
Max = max(Max,n);
int ans = ;
while (n) {
if (n&) ans=ans*x%mo;
x=x*x%mo;
n>>=;
}
return ans;
}
signed main()
{
scanf("%lld%lld",&n,&m);
c[][]=;
fp(i,,) {
c[i][]=c[i][i]=;
fp(j,,i-) c[i][j]=(c[i-][j-]+c[i-][j])%mo;
}
f[][][]=;
fp(i,,m) fp(j,,n) fp(k,,j) fp(s,,j-k) {
if (i!=m) f[i][j][k]+=Pow(,c[k][]) * Pow((Pow(,s)-+mo)%mo,k)%mo * c[n-j+k-][k] % mo * f[i-][j-k][s] %mo;
else f[i][j][k]+=Pow(,c[k][]) * Pow((Pow(,s)-+mo)%mo,k)%mo * c[n-j+k-][k-] % mo * f[i-][j-k][s] %mo;
f[i][j][k]%=mo;
}
int ans = ;
fp(j,m+,n) fp(k,,j-m) {
ans=(ans+f[m][j][k]*Pow(,c[n-j][]+(n-j)*k)%mo)%mo;
}
cout<<ans<<'\n';
return ;
}

C.cpp

HGOI 20190830 题解的更多相关文章

  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. Elastic Search中Query String常见语法

    1 搜索所有数据timeout参数:是超时时长定义.代表每个节点上的每个shard执行搜索时最多耗时多久.不会影响响应的正常返回.只会影响返回响应中的数据数量.如:索引a中,有10亿数据.存储在5个s ...

  2. X86逆向13:向程序中插入Dll

    本章我们将学习Dll的注入技巧,我们将把一个动态链接库永久的插入到目标程序中,让程序在运行后直接执行这个Dll文件,这一章的内容也可以看作是第八课的加强篇,第八课中我们向程序中插入了一个弹窗,有木有发 ...

  3. 使用python操作kafka

    使用python操作kafka目前比较常用的库是kafka-python库 安装kafka-python pip3 install kafka-python 生产者 producer_test.py ...

  4. 关于CPU的一些操作(CPU设置超频)

    常见的几种CPU模式: .ondemand:系统默认的超频模式,按需调节,内核提供的功能,不是很强大,但有效实现了动态频率调节,平时以低速方式运行,当系统负载提高时候自动提高频率.以这种模式运行不会因 ...

  5. jq之display:none与visible:hidden

    http://www.cnblogs.com/linxiong945/p/4075146.html 今天学习到jquery的hide()部分时,突然有一个想法,jquery中的隐藏/显示部分的实现是给 ...

  6. 【原创】大数据基础之Gobblin(2)持久化kafka到hdfs

    gobblin 0.10 想要持久化kafka到hdfs有很多种方式,比如flume.logstash.gobblin,其中flume和logstash是流式的,gobblin是批处理式的,gobbl ...

  7. JS基础_基本数据类型和引用数据类型

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  8. Android中如何判断内存卡是否存在

    if (Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED)) { /* 得到SD卡得路 ...

  9. phpcms修改重置后台账号和密码

    通过Phpmyadmin等工具,打开数据库中找到v9_admin表: 把password字段值改为: 0b817b72c5e28b61b32ab813fd1ebd7f再把encrypt字段值改为: 3 ...

  10. JS遍历对象和数组总结

    在日常工作过程中,我们对于javaScript遍历对象.数组的操作是十分的频繁的,今天把经常用到的方法总结一下! 一.遍历对象 1.使用Object.keys()遍历 返回一个数组,包括对象自身的(不 ...