Noip模拟84 2021.10.27
以后估计都是用\(markdown\)来写了,可能风格会有变化
T1 宝藏
这两天老是会的题打不对,还是要细心。。。
考场上打的是维护\(set\)的做法,但是是最后才想出来的,没有维护对于是没有交。。
然后觉得细节太多于是下午来的就去打了比较好搞得权值线段树
基本思想就是用一个指针\(pos\)指向当前需要更新的答案(\(pos\times 2+1\)是题目输入的选择的数的个数,这样解释应该就好懂了)
那么考虑先将所有宝藏按照价值从小到大排序,发现从最后开始向前扫每一个宝藏,他可以更新的\(pos\)是单调递增的
例如,当前考虑下标为\(x\)的宝藏价值为中位数最大值的时候,
他的可以更新的这个\(pos\)值域取在\([pos,\min(n-x,x-1)]\)一段区间,
那么我们只需要看,从\(x\)的左边和右边分别选择\(pos\)个宝藏,他们的\(\sum t\)是否有可能小于\(T\),
显然每次找到最小的前\(pos\)个\(t\)的\(\sum\)是最优的,所以开两颗权值线段树分别维护当前的\(x\)左边和右边的值即可
treasure
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int NN=3e5+5;
namespace AE86{
auto read=[](){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
};
auto write=[](int x,char opt='\n'){
char ch[20];short len=0;if(x<0)x=~x+1,putchar('-');
do{ch[len++]=x%10+(1<<5)+(1<<4);x/=10;}while(x);
for(short i=len-1;i>=0;--i)putchar(ch[i]);putchar(opt);
};
}using namespace AE86;
int n,T,Q,x,ans[NN],mx,dis[NN];
struct node{
int w,t;
bool operator<(const node&a)const{
return w==a.w?t<a.t:w<a.w;
}
}p[NN];
struct SNOWtree{
#define lid (id<<1)
#define rid (id<<1|1)
int ll[NN<<2],rr[NN<<2],nm[NN<<2],sm[NN<<2];
inline void pushup(int id){if(ll[id]==rr[id])return;nm[id]=nm[lid]+nm[rid];sm[id]=sm[lid]+sm[rid];}
inline void build(int id,int l,int r){ll[id]=l;rr[id]=r;if(l==r)return;int mid=l+r>>1;build(lid,l,mid);build(rid,mid+1,r);}
inline void insert(int id,int pos,int v){
if(ll[id]==rr[id])return nm[id]+=v,sm[id]+=v*dis[pos],void();int mid=ll[id]+rr[id]>>1;
if(pos<=mid)insert(lid,pos,v); else insert(rid,pos,v); pushup(id);
}
inline int query(int id,int lim){
if(!lim) return 0;
if(nm[id]<=lim) return sm[id];
if(ll[id]==rr[id]) return dis[ll[id]]*lim;
int mid=ll[id]+rr[id]>>1;
if(lim<=nm[lid]) return query(lid,lim);
return sm[lid]+query(rid,lim-nm[lid]);
}
}ri,le;
int pos;
auto solve=[](int x){
int r=min(n-x,x-1); if(pos>r) return;
while(pos<=r){
int tot=ri.query(1,pos)+le.query(1,pos)+dis[p[x].t];
if(tot<=T) ans[pos*2+1]=p[x].w,++pos; else break;
}
};
namespace WSN{
inline short main(){
// freopen("in.in","r",stdin);freopen("bl.out","w",stdout);
freopen("treasure.in","r",stdin);freopen("treasure.out","w",stdout);
n=read();T=read();Q=read();
for(int i=1;i<=n;i++) p[i].w=read(),p[i].t=read(),dis[i]=p[i].t;
sort(p+1,p+n+1); memset(ans,-1,sizeof(ans));
sort(dis+1,dis+n+1); mx=unique(dis+1,dis+n+1)-dis-1;
for(int i=1;i<=n;i++) p[i].t=lower_bound(dis+1,dis+mx+1,p[i].t)-dis;
ri.build(1,1,mx);le.build(1,1,mx);
for(int i=1;i<=n;++i) le.insert(1,p[i].t,1);
for(int i=n;i;--i) le.insert(1,p[i].t,-1),solve(i),ri.insert(1,p[i].t,1);
while(Q--) x=read(),write(ans[x]);
return 0;
}
}
signed main(){return WSN::main();}
T2 寻找道路
对不起,坏孩子没有打正解,直接使用\(bitset\)可以淼过去
考场上打了一半的\(bitset\)然后发现使用\(dijkstra\)无法优化其对于堆优化时候的比较函数
然后当时不知道咋想的,也没有试一试\(spfa\),\(spfa\)只有一次比较,写起来非常方便
只要写了\(spfa+bitset\)就可以切掉了,但是只能说是淼过去的
非常好写,就是注意删除前导\(0\)就可以了
path
#include<bits/stdc++.h>
#define int long long
#define si(i,x) for(int i=head[x],y=e[i].to;i;i=e[i].next,y=e[i].to)
using namespace std;
const int NN=2e6+5,mod=1e9+7;
namespace AE86{
auto read=[](){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
};
auto write=[](int x,char opt='\n'){
char ch[20];short len=0;if(x<0)x=~x+1,putchar('-');
do{ch[len++]=x%10+(1<<5)+(1<<4);x/=10;}while(x);
for(short i=len-1;i>=0;--i)putchar(ch[i]);putchar(opt);
};
}using namespace AE86;
int n,m;
struct SNOW{int to,val,next;}e[NN];int head[NN],rp=1;
auto add=[](int x,int y,int z){e[++rp]=SNOW{y,z,head[x]};head[x]=rp;};
int dis[NN],wei[NN];
bitset<1001> bt[NN],tmp;
bool vis[NN];
queue<int>q;
auto spfa=[](){
for(int i=1;i<=n;i++)
dis[i]=1e18,wei[i]=1000,bt[i].flip();
q.push(1); vis[1]=true;
dis[1]=0; wei[1]=1; bt[1].reset(); bt[1]=0;
while(!q.empty()){
int x=q.front(); q.pop(); vis[x]=false;
si(i,x){
bool cmp=0; int wi=wei[x]+1;
if(bt[x][wei[x]]==0) --wi;
tmp=bt[x]<<1; tmp[1]=e[i].val;
if(wei[y]>wi) cmp=1;
else if(wei[y]<wi) cmp=0;
else{
for(int j=wei[y];j;--j){
if(bt[y][j]>tmp[j]) {cmp=1;break;}
else if(bt[y][j]<tmp[j]) {cmp=0;break;}
}
}
if(cmp){
bt[y]=tmp; wei[y]=wi;
dis[y]=(dis[x]*2%mod+e[i].val)%mod;
if(!vis[y]){
vis[y]=true;
q.push(y);
}
}
}
}
};
namespace WSN{
inline short main(){
freopen("path.in","r",stdin);
freopen("path.out","w",stdout);
n=read();m=read();
for(int i=1,u,v,w;i<=m;i++)
u=read(),v=read(),w=read(),add(u,v,w);
spfa();
for(int i=2;i<=n;i++)
if(dis[i]>=1e18) write(-1,' ');
else write(dis[i],' ');
puts("");
return 0;
}
}
signed main(){return WSN::main();}
T3 猪国杀
又搞这一套,又想吓我,已经无法被吓倒了
不过题目还是很神仙的假期望,真计数
设方案数为\(ans\),那么答案就是
\(ans=\sum\limits_{i=0}^{n}\sum\limits_{j=1}^{A}\sum\limits_{k=1}^{n-i}g_{i,j-1,m-j*k}\times C_n^i\sum\limits_{t\geq k}C_{n-i}^{t}\times(A-j)^{n-i-t}\)
其中\(g_{i,j,k}\)表示有多少个长度为\(i\)的序列满足每一个数字都不大于\(j\)且所有数字的总和不大于\(k\)
以上式子可以理解为枚举序列的长度,然后枚举最大值\(j\),然后再枚举最大值的个数\(k\),然后剩下的\(n-i\)个可以在去掉最大值的限制后随便选
你可能会问,这个算方案数的式子为什么不用考虑每次的贡献?
实际上他每个合法的序列计算的次数正好为他做的贡献数,即每个合法序列都会计算\(i+k\)次
那么考虑容斥出\(g_{i,j,k}=\sum\limits_{t=0}^{i}(-1)^t\times C_i^t\times C_{k-j*t}^{i}\)
那么原式的转化我就不写了,随便代一下就出来了,直接照这个式子抄就可以了,把没有贡献的地方直接跳过不计算
legend
#include<bits/stdc++.h>
#define int long long
#define si(i,x) for(int i=head[x],y=e[i].to;i;i=e[i].next,y=e[i].to)
using namespace std;
const int NN=101,MM=3001,mod=998244353;
namespace AE86{
auto read=[](){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
};
auto write=[](int x,char opt='\n'){
char ch[20];short len=0;if(x<0)x=~x+1,putchar('-');
do{ch[len++]=x%10+(1<<5)+(1<<4);x/=10;}while(x);
for(short i=len-1;i>=0;--i)putchar(ch[i]);putchar(opt);
};
}using namespace AE86;
int n,m,A,ans;
namespace Math{
int h[MM],v[MM];
auto qmo=[](int a,int b,int ans=1){
int c=mod;for(;b;b>>=1,a=a*a%c)if(b&1)ans=ans*a%c;
return ans;
};
auto inv=[](int x){return qmo(x,mod-2);};
auto prework=[](){
h[0]=h[1]=1; v[0]=v[1]=1; for(int i=2;i<MM;++i)h[i]=h[i-1]*i%mod;
v[MM-1]=inv(h[MM-1]); for(int i=MM-2;i>=2;--i)v[i]=v[i+1]*(i+1)%mod;
};
auto C=[](int n,int m){return (n<m||n<0||m<0) ? 0 : (h[n]*v[n-m]%mod*v[m]%mod);};
}using namespace Math;
namespace WSN{
inline short main(){
freopen("legend.in","r",stdin);
freopen("legend.out","w",stdout);
n=read();m=read();A=read();prework();
for(int i=0;i<=n;++i){
for(int j=1;j<=A;++j){
for(int k=1;k<=n-i;++k){
if(m-k*j<0)continue;
int tmp=0,res=0,bs=1;
for(int t=0;t<=i&&m-k*j-t*(j-1)>=i;++t,bs=-bs)
tmp=(tmp+bs*C(i,t)%mod*C(m-k*j-t*(j-1),i)%mod+mod)%mod;
tmp=tmp*C(n,i)%mod;
for(int t=k;t<=n-i;++t)
res=(res+C(n-i,t)*qmo(A-j,n-i-t)%mod)%mod;
ans=(ans+tmp*res%mod)%mod;
}
}
}
write(ans*inv(qmo(A,n))%mod);
return 0;
}
}
signed main(){return WSN::main();}
T4 数树
数据范围提示状态压缩,那么考虑如何设状态
设计状态\(f[x][S]\)表示\(x\)子树中的点集为\(S\)的方案数
例如,\(S=0001110\)就表示\(2,3,4\)这三个点是某一个点的子孙节点
那么因为\(T2\)的形状对于不同的节点做根可能会不一样,所以以每一个节点做根都跑一边\(dp\)
然后注意去重,具体操作就是用\(map\)预处理出会重复算多少次,最后的\(dp\)数组再把算重复的除掉就行了
count
#include<bits/stdc++.h>
#define int long long
#define pb push_back
using namespace std;
const int NN=3005,mod=998244353;
namespace AE86{
auto read=[](){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
};
auto write=[](int x,char opt='\n'){
char ch[20];short len=0;if(x<0)x=~x+1,putchar('-');
do{ch[len++]=x%10+(1<<5)+(1<<4);x/=10;}while(x);
for(short i=len-1;i>=0;--i)putchar(ch[i]);putchar(opt);
};
}using namespace AE86;
auto qmo=[](int a,int b,int ans=1){
int c=mod;for(;b;b>>=1,a=a*a%c)if(b&1)ans=ans*a%c;
return ans;
};auto inv=[](int x){return qmo(x,mod-2);};
int n,m,v[11],S[1<<10],tot,root,T[11],dp[NN][1<<10],tmp[11];
vector<int> son[11];
struct Graph{
#define star(e,i,x) for(int i=e.head[x],y=e.to[i];i;i=e.nxt[i],y=e.to[i])
int to[NN<<1],nxt[NN<<1],head[NN],rp;
inline void add(int x,int y){
to[++rp]=y; nxt[rp]=head[x]; head[x]=rp;
to[++rp]=x; nxt[rp]=head[y]; head[y]=rp;
}
}e,g;
unordered_map<int,bool>mp;
unordered_map<int,int>t[11];
inline void dfs(int f,int x,int bs){
S[x]|=(1<<x-1);tot+=bs;
star(g,i,x) if(y!=f){
son[x].pb(y); dfs(x,y,bs*10);
++t[x][T[y]];
S[x]|=S[y];T[x]+=T[y];
}
star(g,i,x) if(y!=f) if(t[x].find(T[y])!=t[x].end())
tmp[x]=tmp[x]*v[t[x][T[y]]]%mod,t[x].erase(T[y]);
T[x]+=bs;
}
int res[1<<10],ans;
inline void DP(int f,int x){
for(int i=0;i<m;i++) dp[x][1<<i]=1;
star(e,i,x) if(y!=f){
DP(x,y);
for(int j=1;j<(1<<m);j++) res[j]=dp[x][j];
for(int j=1;j<(1<<m);j++) if(dp[x][j])
for(int k=1;k<=m;k++) if(!(j&S[k])&&dp[y][S[k]])
dp[x][S[k]|j]=(dp[x][S[k]|j]+dp[y][S[k]]*res[j]%mod)%mod;
}
for(int i=1;i<=m;i++) dp[x][S[i]]=dp[x][S[i]]*tmp[i]%mod;
ans=(ans+dp[x][(1<<m)-1])%mod;
}
namespace WSN{
inline short main(){
freopen("count.in","r",stdin);freopen("count.out","w",stdout);
v[0]=1; for(int i=1;i<=10;i++) v[i]=v[i-1]*inv(i)%mod;
n=read();for(int i=1;i<n;i++)e.add(read(),read());
m=read();for(int i=1;i<m;i++)g.add(read(),read());
for(int i=1;i<=m;i++){
for(int j=1;j<=m;j++)t[j].clear(),son[j].clear(),tmp[j]=1;
memset(S,0,sizeof(S));
memset(T,0,sizeof(T));
memset(dp,0,sizeof(dp));
tot=0; root=i;
dfs(0,root,1);
if(mp.find(tot)==mp.end())
mp[tot]=true,DP(0,1);
}
write(ans);
return 0;
}
}
signed main(){return WSN::main();}
Noip模拟84 2021.10.27的更多相关文章
- Noip模拟70 2021.10.6
T1 暴雨 放在第一道的神仙题,不同的做法,吊人有的都在用线段树维护$set$预处理 我是直接$dp$的,可能代码的复杂度比那种的稍微小一点 设$f[i][j][p][0/1]$表示考虑了前$i$列, ...
- Noip模拟69 2021.10.5
考场拼命$yy$高精度结果没学好$for$循环痛失$50pts$,当场枯死 以后一定打对拍,要不考后会... T1 石子游戏 首先要知道典型的$NIM$博弈,就是说如果所有堆石子个数的异或和为$0$则 ...
- Noip模拟76 2021.10.14
T1 洛希极限 上来一道大数据结构或者单调队列优化$dp$ 真就没分析出来正解复杂度 正解复杂度$O(q+nm)$,但是据说我的复杂度是假的 考虑一个点转移最优情况是从它上面的一个反$L$形转移过来 ...
- Noip模拟63 2021.9.27(考场惊现无限之环)
T1 电压机制 把题目转化为找那些边只被奇数环包含. 这样的话直接$dfs$生成一棵树,给每个点附上一个深度,根据其他的非树边都是返祖边 可以算出环内边的数量$dep[x]-dep[y]+1$,然后判 ...
- Noip模拟81 2021.10.20
T1 语言 比较简单的题,然后就瞎写了,所以考场上就我一个写了线段树的,所以我的常数.... 所以就枚举动词的位置,找前面后面有没有出现$4$即可 1 #include<bits/stdc++. ...
- Noip模拟83 2021.10.26
T1 树上的数 有手就能在衡中$OJ$上过,但是$WaitingCoders$不行,就是这样 必须使用$O(n)$算法加上大力卡常,思路就是找子树内没更新的更新,更新过了直接$return$ 1 #i ...
- Noip模拟80 2021.10.18
预计得分:5 实际得分:140?????????????? T1 邻面合并 我考场上没切掉的大水题....(证明我旁边的cty切掉了,并觉得很水) 然而贪心拿了六十,离谱,成功做到上一篇博客说的有勇气 ...
- Noip模拟79 2021.10.17(题目名字一样)
T1 F 缩点缩成个$DAG$,然后根据每个点的度数计算期望值 1 #include<cstdio> 2 #include<cstring> 3 #include<vec ...
- Noip模拟74 2021.10.11
T1 自然数 考场上当我发现我的做法可能要打线段树的时候,以为自己百分之百是考虑麻烦了 但还是打了,还过掉了所有的样例,于是十分自信的就交了 正解还真是线段树,真就第一题数据结构 但是包括自己造的小样 ...
随机推荐
- TCP头部格式和封装
文章目录 12.3 TCP头部和封装 12.3.1 端口号 12.3.2 序列号 12.3.3 头部长度 12.3.4 相关控制位 12.3.5 窗口大小 12.3.6 校验和 12.3.7 选项字段 ...
- Identity角色管理三(创建角色)
首先创建视图模型 using System.ComponentModel; using System.ComponentModel.DataAnnotations; namespace Shop.Vi ...
- 机器学习——集成学习(Bagging、Boosting、Stacking)
1 前言 集成学习的思想是将若干个学习器(分类器&回归器)组合之后产生一个新学习器.弱分类器(weak learner)指那些分类准确率只稍微好于随机猜测的分类器(errorrate < ...
- UVA 11853 Paintball(几何数学+DFS)
https://vjudge.net/problem/UVA-11853 根据题意描述,相当于在一个正方形中有若干个圆形障碍物,问是否能从左边界走到右边界.判断是否有解需要一点创造性的思维:不妨把正方 ...
- 【OI】WERTYU UVa 10082
题目: A common typing error is to place the hands on the keyboard one row to the right of the correct ...
- PHP中的日期相关函数(一)
日期相关的操作函数是我们在日常的工作开发中最常接触到的功能.当然,大部分同学可能最多用到的就是 date() . time() 这两个函数,我们今天先不讲这两个函数,或许后面的文章也不太会讲它们,毕竟 ...
- 分布式文件系统FastDFS在CentOS7上的安装及与Springboot的整合
1. 概述 FastDFS 是目前比较流行的分布式文件系统,可以很容易的实现横向扩展.动态扩容.灾备.高可用和负载均衡. FastDFS 的服务分为 tracker 服务 和 storage 服务, ...
- 《HelloGitHub》第 66 期
兴趣是最好的老师,HelloGitHub 让你对编程感兴趣! 简介 分享 GitHub 上有趣.入门级的开源项目. 这里有实战项目.入门教程.黑科技.开源书籍.大厂开源项目等,涵盖多种编程语言 Pyt ...
- Redis高可用解决方案:哨兵(Sentinel)
哨兵是Redis的高可用解决方案:由多个哨兵组成的系统监视主从服务器,可以将下线的主服务器属下的某个从服 务器升级为新的主服务器,继续保障运行. 启动并初始化Sentinel redis-sentin ...
- shiro的使用与JWT整合
一.shiro入门 两大框架对比:安全框架Shiro和SpringSecurity的比较 了解shiro 什么是Shiro Apache Shiro是一个Java的安全(权限)框架.| Shiro可以 ...