T1 宝藏

解题思路

考场上一眼出 \(nlog^2\) 做法,然后没看见是 1s 3e5 的数据,我竟然以为自己切了??

考完之后尝试着把二分改为指针的移动,然后就过了??或许是数据水吧,感觉自己的做法指针好像并不满足单调性。。

口胡一下正解,做法差不多,只不过枚举的方式改变了,但是都需要先对于 w 进行排序,枚举每一种长度的序列,单调指针维护最大的合法的值。

这个是有单调性的,然后主席树或者权值线段树维护均可。

code

其实是假做法

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"RP++"<<endl
#define ls x<<1
#define rs x<<1|1
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int N=3e5+10,M=1e6+10;
int n,m,q,lim,ans[N];
struct Node{int w,t;}s[N];
struct Segment_Tree
{
int root,all;
struct node{int siz,dat;}tre[M<<2];
#define push_up(x) tre[x].dat=tre[ls].dat+tre[rs].dat,tre[x].siz=tre[ls].siz+tre[rs].siz
void insert(int x,int l,int r,int pos,int val)
{
if(l==r) return tre[x].siz+=val,tre[x].dat+=val*pos,void();
int mid=(l+r)>>1;
if(pos<=mid) insert(ls,l,mid,pos,val);
else insert(rs,mid+1,r,pos,val);
push_up(x);
}
int query(int x,int l,int r,int k)
{
if(!tre[x].siz||!k) return 0;
if(l==r) return tre[x].dat*k/tre[x].siz;
int mid=(l+r)>>1;
if(tre[ls].siz>k) return query(ls,l,mid,k);
return tre[ls].dat+query(rs,mid+1,r,k-tre[ls].siz);
}
}T1,T2;
bool comp(Node x,Node y){return x.w<y.w;};
#undef int
int main()
{
#define int long long
freopen("treasure.in","r",stdin); freopen("treasure.out","w",stdout);
n=read(); m=read(); q=read(); memset(ans,-1,sizeof(ans));
for(int i=1;i<=n;i++) s[i].w=read(),s[i].t=read(),lim=max(lim,s[i].t);
sort(s+1,s+n+1,comp); for(int i=1;i<=n;i++) T2.insert(1,0,lim,s[i].t,1);
for(int i=1,val=0;i<=n;i++)
{
T2.insert(1,0,lim,s[i].t,-1); val=min(val,min(i-1,n-i));
while(val<min(i-1,n-i)&&T1.query(1,1,lim,val+1)+T2.query(1,1,lim,val+1)+s[i].t<=m) val++;
while(val>0&&T1.query(1,1,lim,val)+T2.query(1,1,lim,val)+s[i].t>m) val--;
ans[val]=max(ans[val],s[i].w); T1.insert(1,0,lim,s[i].t,1);
}
for(int i=n/2;i>=0;i--) ans[i]=max(ans[i],ans[i+1]);
while(q--){int x;x=read();printf("%lld\n",ans[min(n,x/2)]);}
return 0;
}

T2 寻找道路

解题思路

首先考虑去除前导 0 的影响,直接搜索一遍查找所有到 1 节点距离为 0 的点记录下来就好了。

剩下的部分就是字典序以及长度的问题了,那么长度的问题直接 BFS 就可以了。

字典序大小的话,对于队列中长度一致并且数字序列相同的一起拿出来,然后优先扫 0 边权的边再扫 1 边权的边。

标记一下,保证每个节点只访问一次。

code

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"RP++"<<endl
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int N=1e6+10,mod=1e9+7,INF=1e18;
int n,m,top,sta[N],len[N],dis[N];
int tot=1,head[N],ver[N<<1],nxt[N<<1],edge[N<<1];
vector<int> dis0;
queue<int> q;
bool vis[N];
void add_edge(int x,int y,int val)
{
ver[++tot]=y; edge[tot]=val;
nxt[tot]=head[x]; head[x]=tot;
}
#undef int
int main()
{
#define int long long
freopen("path.in","r",stdin); freopen("path.out","w",stdout);
n=read(); m=read(); memset(len,0x3f,sizeof(len)); dis[1]=len[1]=0;
for(int i=1,x,y,z;i<=m;i++) x=read(),y=read(),z=read(),add_edge(x,y,z);
q.push(1); dis0.push_back(1);
while(!q.empty())
{
int x=q.front(); q.pop();
for(int i=head[x];i;i=nxt[i])
if(!vis[ver[i]]&&!edge[i])
vis[ver[i]]=true,dis0.push_back(ver[i]),q.push(ver[i]);
}
for(auto it:dis0) q.push(it),dis[it]=len[it]=0;
while(!q.empty())
{
top=0; sta[++top]=q.front(); q.pop();
while(!q.empty()&&len[q.front()]==len[sta[1]]&&dis[q.front()]==dis[sta[1]])
sta[++top]=q.front(),q.pop();
for(int i=1;i<=top;i++)
{
int x=sta[i];
for(int j=head[x];j;j=nxt[j])
{
int to=ver[j],val=edge[j];
if(val||vis[to]||len[to]<=len[x]+1) continue;
len[to]=len[x]+1; dis[to]=dis[x]*2%mod;
vis[to]=true; q.push(to);
}
}
for(int i=1;i<=top;i++)
{
int x=sta[i];
for(int j=head[x];j;j=nxt[j])
{
int to=ver[j],val=edge[j];
if(!val||vis[to]||len[to]<=len[x]+1) continue;
len[to]=len[x]+1; dis[to]=(dis[x]*2+1)%mod;
vis[to]=true; q.push(to);
}
}
}
for(int i=2;i<=n;i++) printf("%lld ",len[i]>=INF?-1ll:dis[i]);
return 0;
}

T3 猪国杀

解题思路

其实是个假期望,计数 DP 。

我们只需要知道每一种方案的总和了,最后乘上一个 \(A^n\) 。

设 \(g_{i,j,k}\) 表示有 多少个⻓度为 \(i\) 的正整数序列满足每一个数字不大于 \(j\) 且所有数字总和不超过 \(k\) 。

假设我们能够求出来这个值,考虑如何计算答案。

枚举选的牌中的最大值 \(j\) ,最大值个数 \(k\) ,以及选了 \(i\) 个小于 \(j\) 的牌,于是就有了:

\[\sum\limits_{i=0}^n\sum\limits_{j=1}^A\sum\limits_{k=1}^{n-i}g_{i,j-1,m-j\times k}\times \binom{n}{i}\sum\limits_{t=k}^{n-i}\binom{n-i}{t}\times(A-j)^{n-i-t}
\]

对于计算过方案数的两个序列就可以视为序列中的元素是等价的了,也就是再乘上一个可重集排列。

因为我们要选择 \(k\) 个 \(j\) 但是序列中不一定只有 \(k\) 个 \(j\) 因此我们需要让前面的牌的总和是 \(m-j\times k\) 然后枚举后面有多少个 \(j\) 同时计算剩下的取值的个数。

对于 \(g_{i,j,k}\) 可以通过枚举多少个大于 \(j\) 的数字进行计算,可以运用挡板法,由于挡板之间 1 的个数可能会超过 \(j\) 因此需要容斥一下:

\[g_{i,j,k}=\sum\limits_{t=0}^i(-1)^t\binom{i}{t}\binom{k-t\times j}{i}
\]

code

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"RP++"<<endl
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int N=110,M=1010,mod=998244353;
int n,m,lim,mx,ans,fac[M],ifac[M];
void add(int &x,int y){x+=y;if(x>=mod)x-=mod;}
int power(int x,int y,int p=mod)
{
int temp=1;
while(y)
{
if(y&1) temp=temp*x%p;
x=x*x%p; y>>=1;
}
return temp;
}
int C(int x,int y){return x<y?0:fac[x]*ifac[y]%mod*ifac[x-y]%mod;}
int g(int i,int j,int k)
{
int sum=0;
for(int p=0,sym=1;p<=i;p++,sym=-sym)
{
int temp=C(i,p)*C(k-p*j,i)%mod;
if(temp) add(sum,sym*temp+((~sym)?0:mod));
else break;
}
return sum;
}
#undef int
int main()
{
#define int long long
freopen("legend.in","r",stdin); freopen("legend.out","w",stdout);
n=read(); m=read(); lim=read(); mx=max(n,max(m,lim));
fac[0]=ifac[0]=1; for(int i=1;i<=mx;i++) fac[i]=fac[i-1]*i%mod;
ifac[mx]=power(fac[mx],mod-2); for(int i=mx-1;i>=1;i--) ifac[i]=ifac[i+1]*(i+1)%mod;
for(int i=0;i<=n;i++)
for(int j=1;j<=lim;j++)
for(int k=1;k<=n-i;k++)
{
int base=0,temp=C(n,i)%mod*g(i,j-1,m-j*k)%mod;
if(!temp) continue;
for(int p=k;p<=n-i;p++)
{
int temp=C(n-i,p); if(!temp) break;
add(base,temp*power(lim-j,n-i-p)%mod);
}
if(base) add(ans,base*temp%mod);
}
printf("%lld",ans*power(power(lim,n),mod-2)%mod); return 0;
}

T4 数树

解题思路

枚举以哪个节点为根以及子树和 T2 的匹配程度来判断。

具体实现可以 Hash+素数 防止重复。

code

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"RP++"<<endl
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int N=3e3+10,M=20,bas=23,mod=998244353;
int n,m,cnt,ans,pri[N],siz[N];
int tot=1,head[N],ver[N<<1],nxt[N<<1];
ull has[N];
unordered_map<ull,int> ys,f[N];
unordered_map<ull,bool> can,mp;
vector<int> v[M];
bitset<N> vis;
void add_edge(int x,int y)
{
ver[++tot]=y;
nxt[tot]=head[x];
head[x]=tot;
}
void Get_Prime()
{
for(int i=2;i<=1000;i++)
{
if(!vis[i]) pri[++cnt]=i;
for(int j=1;j<=cnt&&pri[j]*i<=1000;j++)
{
vis[i*pri[j]]=true;
if(i%pri[j]==0) break;
}
}
}
void pre_dfs(int x,int fa)
{
siz[x]=has[x]=1;
for(auto to:v[x])
{
if(to==fa) continue;
pre_dfs(to,x); siz[x]+=siz[to];
has[x]+=has[to]*pri[siz[to]+bas];
}
mp.insert(make_pair(has[x],true));
ys.insert(make_pair(has[x],siz[x]+bas));
}
void dfs(int x,int fa)
{
f[x].insert(make_pair(1,1));
for(int i=head[x];i;i=nxt[i])
{
int to=ver[i]; if(to==fa) continue;
unordered_map<ull,int> temp=f[x]; dfs(to,x);
for(auto it1:temp) for(auto it2:f[to])
{
ull rec=it1.first+it2.first*pri[ys.find(it2.first)->second];
f[x][rec]=(f[x][rec]+it1.second*it2.second)%mod;
}
}
vector<ull> dela;
for(auto it:f[x]) if(mp.find(it.first)==mp.end()) dela.push_back(it.first);
for(auto it:dela) f[x].erase(it);
}
#undef int
int main()
{
#define int long long
freopen("count.in","r",stdin); freopen("count.out","w",stdout);
n=read(); Get_Prime();
for(int i=1,x,y;i<n;i++)
x=read(),y=read(),
add_edge(x,y),add_edge(y,x);
m=read();
for(int i=1,x,y;i<m;i++)
x=read(),y=read(),
v[x].push_back(y),v[y].push_back(x);
for(int i=1;i<=m;i++) pre_dfs(i,0),can.insert(make_pair(has[i],true));
dfs(1,0);
for(auto it:can)
for(int i=1;i<=n;i++)
if(f[i].find(it.first)!=f[i].end())
ans=(ans+f[i].find(it.first)->second)%mod;
printf("%lld",ans);
return 0;
}

NOIP模拟84(多校17)的更多相关文章

  1. NOIP模拟83(多校16)

    前言 CSP之后第一次模拟赛,感觉考的一般. 不得不吐槽多校联测 OJ 上的评测机是真的慢... T1 树上的数 解题思路 感觉自己思维有些固化了,一看题目就感觉是线段树. 考完之后才想起来这玩意直接 ...

  2. Noip模拟79 2021.10.17(题目名字一样)

    T1 F 缩点缩成个$DAG$,然后根据每个点的度数计算期望值 1 #include<cstdio> 2 #include<cstring> 3 #include<vec ...

  3. Noip模拟18 2021.7.17 (文化课专场)

    T1 导弹袭击(数学) 显然,我们要找到最优的A,B使得一组a,b优于其他组那么可以列出: $\frac{A}{a_i}+\frac{B}{b_i}<\frac{A}{a_j}+\frac{B} ...

  4. Noip模拟55 2021.9.17(打表大胜利)

    T1 skip 普通$dp$很好打: $f[i]=max(f[j]-\sum_{k=1}^{K}k+a_i)$ 就是要注意边界问题很烦人. 1 #include<bits/stdc++.h> ...

  5. Noip模拟42 2021.8.17

    T1 卷 一看跟没有上司的舞会一样,直接敲了然后试个自己造的样例对了就跑了... 然而把它想简单了,乘积取模,还能比大小吗????? 显然不能 所以直接让对数的加和跟着$dp$直接一起跑,比大小的都用 ...

  6. Noip模拟8 2021.6.17

    T1 星际旅行 仔细一看,发现像一个欧拉路(简称一笔画). 满足"可以一笔画"的条件是: 1.所有点都有偶数条连边; 2.有偶数个点连奇数条边; 满足以上两个条件的任意一个即可一笔 ...

  7. NOIP模拟92(多校25)

    前言 所以说这次是 HZOI 多校联测巅峰????(题目,数据过水??) T1 石子合并 解题思路 签到题. 发现我们可以给每个数字附一个正负号,每个数字的贡献就是它本身乘上这个符号. 发现至少应该有 ...

  8. Noip模拟84 2021.10.27

    以后估计都是用\(markdown\)来写了,可能风格会有变化 T1 宝藏 这两天老是会的题打不对,还是要细心... 考场上打的是维护\(set\)的做法,但是是最后才想出来的,没有维护对于是没有交. ...

  9. NOIP模拟85(多校18)

    前言 好像每个题目背景所描述的人都是某部番里的角色,热切好像都挺惨的(情感上的惨). 然后我只知道 T1 的莓,确实挺惨... T1 莓良心 解题思路 首先答案只与 \(w\) 的和有关系,于是问题就 ...

随机推荐

  1. epoll代码框架

    epoll代码实现框架: #define MAX_EVENTS 10 struct epoll_event ev, events[MAX_EVENTS]; int listen_sock, conn_ ...

  2. 用python的pandas读取excel文件中的数据

    一.读取Excel文件   使用pandas的read_excel()方法,可通过文件路径直接读取.注意到,在一个excel文件中有多个sheet,因此,对excel文件的读取实际上是读取指定文件.并 ...

  3. 将dict.define转化成dict.txt

    在使用捷通智能灵云外呼系统V6.1时.需要大量使用到模式码,也就是正则表达式.而老版本365还是使用场景文件. 当要将老版本改编成新版本的时候,需要需要将dict.define文件中的一行行的词条用& ...

  4. windows中ren(rename-重命名)命令的使用方法

    脚本(*.dat文件): cd /d %~dp0 ren * *.mp3

  5. 使用私有gitlab发布自动生成版本号和标签(version和tag)(骚)

    设置 semantic ,自动生成版本号和标签 FROM node:14-buster-slim LABEL maintainer="wangyunpeng" COPY sourc ...

  6. mysql 选取操作日志(mysql-bin.0000恢复数据)

    my.ini 配置log-bin=mysql-bin 启用日志 用  mysql-bin.0000 mysqlbinlog -d keyboard ../data/mysql-bin.000024 – ...

  7. Java面向对象系列(1)- 什么是面向对象

    面向过程 & 面向对象 面向过程思想 步骤清晰清楚,第一步做什么,第二步做什么-- 面对过程适合处理一些较为简单的问题 面向对象思想 物以类聚,分类的思维模式,思考问题首先会解决问题需要哪些分 ...

  8. lightweight openpose 入门实操笔记(pytorch环境)

    最近有个小项目要搞姿态识别,简单调研了一下2D的识别: 基本上是下面几种 (单人)single person 直接关键点回归 heatmap,感觉其实就是把一个点的标签弄成一个高斯分布 (多人)mul ...

  9. 替代jquery中的几个函数

    // https://open.alipay.com/developmentAccess/developmentAccess.htm var $ = window.jQuery; (function( ...

  10. openldap 双主模式部署

    规划两台机器 系统版本centos7.5 master1上部署ldap: 一.安装启动openldap软件 yum -y install openldap compat-openldap openld ...