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. 植入式Web前端开发

    在博客园.凡科建站和其他的一些CMS系统中,提供有允许管理者向网页中插入自定义HTML代码的功能,我将其称之为"植入式"的Web前端代码. 因为CSS和JavaScript可以直接 ...

  2. 使用Java MVC模式设计一个学生管理系统

    最近在做web实验,要求是用jsp+servlet+mysql实现一个学生管理系统,完成对数据库的增删改查. 效果图:   代码: package dao; import java.util.List ...

  3. scrum项目冲刺_day01总结

    摘要:今日完成任务. 1.app基本框架页面正在进行 2.图像识别正在进行 总任务: 一.appUI页面 二.首页功能: 1.图像识别功能 2.语音识别功能 3.垃圾搜索功能 4.相关新闻爬取 三.我 ...

  4. PHP中的输出:echo、print、printf、sprintf、print_r和var_dump

    大家在面试中,经常会被问到的问题: 请简要说明PHP的打印方式都有哪些? 或者直接点问: 请说明echo.print.print_r的区别 看着很简单,一般会出现在初中级的笔试题中.但是要真正说明白这 ...

  5. symfony生成路由

    控制器里生成地址 $this->generateUrl('course_manage_show_test', array('id' => 1)) twig前端文件生成地址: {{ path ...

  6. 深入剖析RocketMQ源码-NameServer

    一.RocketMQ架构简介 1.1 逻辑部署图 (图片来自网络) 1.2 核心组件说明 通过上图可以看到,RocketMQ的核心组件主要包括4个,分别是NameServer.Broker.Produ ...

  7. 牛客挑战赛48E-速度即转发【带修莫队,分块】

    正题 题目链接:https://ac.nowcoder.com/acm/contest/11161/E 题目大意 给出\(n\)个数字的一个序列,\(m\)个操作. 给出\(l,r,k\),求一个最大 ...

  8. Redis对象

    概述 Redis并没有使用基础数据结构去实现键值数据库,而是基于数据结构封装了一个个对象. 类型和编码 由于Redis是键值数据库,所以每次存储数据时,至少包含两个对象,即K.V对应的对象.其数据结构 ...

  9. 深入理解JAVA虚拟机《二》

    对象.内存回收和垃圾收集算法 一.引用计数算法(不可靠) 现在很多比较普遍的判断对象是否存活的算法就是引用计数算法,其大概原理是:给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1:当 ...

  10. 11.4.2 LVS—NAT

    Virtual Server via NAT(VS-NAT) 用地址翻译实现虚拟服务器。地址转换器有能被外界访问到的合法IP地址,它修改来自专有网络的流出包的地址。外界看起来包是来自地址转换器本身,, ...