Noip模拟95 2021.11.11
T1 嗑瓜子
\(\color{red}{\huge{以后能线性求逆元就别用快速幂!!!}}\)
可得要长个教训,千万不要相信本机跑\(0.7\)能在\(TLEcoders\)上面\(A\)题!!!
一定要优化,不要浪!!!!
剩下的就没啥了,应该都会切
算了还是说一下吧,设一个\(dp[i][j]\)表示堆里面有\(i\)个瓜子\(j\)个瓜子皮时候的概率
那么最后答案就是吃剩下了\(n\)个瓜子皮,不剩瓜子时候的概率乘上到这种状态时候的需要的轮数
答案就是这样统计的
for(int i=n*2;~i;i--)ans=(ans+f[0][i]*(n++)%mod)%mod;
那么转移的话直接就可以简单的按照概率的计算方案计算出多少概率抽出瓜子多少概率抽出皮分别转移到\(dp[i-1][j+2]\)和\(dp[i][j-1]\)上面了
eat
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int NN=2005,mod=998244353;
namespace AE86{
FILE *wsn;
#define gc() p1==p2 and (p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?EOF:*p1++
char buf[1<<20],*p1=buf,*p2=buf;
// #define gc getchar
auto read=[](){
int x=0,f=1;char ch=gc();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=gc();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=gc();} 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);
};
auto qmo=[](int a,int b,int ans=1){
for(;b;b>>=1,a=a*a%mod)if(b&1)ans=ans*a%mod;
return ans;
};
}using namespace AE86;
int n,f[NN][NN<<1],ans,inv[NN<<1];
inline int frac(int x,int y){return x*inv[y]%mod;}
namespace WSN{
inline short main(){
wsn=freopen("eat.in","r",stdin);
wsn=freopen("eat.out","w",stdout);
n=read(); f[n][0]=1; inv[0]=inv[1]=1;
for(int i=2;i<=2*n;i++) inv[i]=(mod-mod/i)*inv[mod%i]%mod;
for(int i=n-1;~i;i--){
for(int j=(n-i)*2;~j;j--){
f[i][j]+=f[i+1][j-2]*frac(i+1,i+j-1)%mod;
if(i!=0) f[i][j]+=f[i][j+1]*frac(j+1,i+j+1)%mod;
f[i][j]%=mod;
}
}
for(int i=n*2;~i;i--)ans=(ans+f[0][i]*(n++)%mod)%mod;
write(ans);
return 0;
}
}
signed main(){return WSN::main();}
T2 第k大查询
应该背过主席树板子的都能拿到\(60\),然后我呢,没有用主席树,用了个\(set\),边界没有卡对,只有\(20pts\)
改一下边界就可以拿到\(60\),比较无奈的挂了\(40\)分。。。
TLE60
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int NN=5e5+5;
namespace AE86{
FILE *wsn;
#define gc() p1==p2 and (p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?EOF:*p1++
char buf[1<<20],*p1=buf,*p2=buf;
auto read=[](){
int x=0,f=1;char ch=gc();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=gc();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=gc();} 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);
};
auto checkmin=[](int &a,int b){a=a<b?a:b;};
auto checkmax=[](int &a,int b){a=a>b?a:b;};
}using namespace AE86;
int n,a[NN],k;
namespace carisian_tree{
int stk[NN],top,son[NN][2];
inline void build(int *a){
for(int i=1;i<=n;i++){
while(top&&a[stk[top]]<a[i])son[i][0]=stk[top--];
if(top) son[stk[top]][1]=i;
stk[++top]=i;
}
}
int ll[NN],rr[NN];
inline void dfs(int x=stk[1]){
ll[x]=rr[x]=x;
if(son[x][0]) dfs(son[x][0]),checkmin(ll[x],ll[son[x][0]]);
if(son[x][1]) dfs(son[x][1]),checkmax(rr[x],rr[son[x][1]]);
}
}using namespace carisian_tree;
namespace Task{
auto task=[](){
build(a); dfs(); int ans=0;
for(int i=1;i<=n;i++)ans+=a[i]*(i-ll[i]+1)*(rr[i]-i+1);
write(ans); return;
};
int srt[NN];
auto pian=[](int ans=0){
for(int i=1;i<=n-k+1;i++){
set<int> s;
for(int j=i;j<=i+k-1;j++) s.insert(a[j]);
ans+=*s.begin();
for(int j=k+i;j<=n;j++){
s.insert(a[j]); s.erase(s.begin());
ans+=*s.begin();
}
}
write(ans); return;
};
}
namespace WSN{
inline short main(){
wsn=freopen("kth.in","r",stdin);
wsn=freopen("kth.out","w",stdout);
n=read();k=read();
for(int i=1;i<=n;i++)a[i]=read();
if(k==1) return Task::task(),0;
Task::pian();
return 0;
}
}
signed main(){return WSN::main();}
正解比想\(set\)简单吧,但是比较考验码力。。。依旧是调了很长时间
他们说这叫双向链表,不理解,我觉得和链表没有区别
我拿的\(set\)维护,这样复杂度是\(O(nklogn)\)的,但是\(TLEcoders\)可以跑过就没有什么问题了
把排列的位置记录下来,按照从大到小的顺序依次插入\(set\),这样保证了当前\(set\)中所有数都大于等于这个新插入的元素
找到离他最近的他前面的元素和他后面的元素,连上指针
然后每次只统计在他左边和在他右边的比他大的\(k-1\)个元素能够框出多少合法区间即可
kth
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int NN=5e5+5;
namespace AE86{
FILE *wsn;
#define gc() p1==p2 and (p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?EOF:*p1++
char buf[1<<20],*p1=buf,*p2=buf;
auto read=[](){
int x=0,f=1;char ch=gc();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=gc();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=gc();} 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);
};
auto checkmin=[](int &a,int b){a=a<b?a:b;};
auto checkmax=[](int &a,int b){a=a>b?a:b;};
}using namespace AE86;
int n,a[NN],k;
namespace carisian_tree{
int stk[NN],top,son[NN][2];
inline void build(int *a){
for(int i=1;i<=n;i++){
while(top&&a[stk[top]]<a[i])son[i][0]=stk[top--];
if(top) son[stk[top]][1]=i;
stk[++top]=i;
}
}
int ll[NN],rr[NN];
inline void dfs(int x=stk[1]){ ll[x]=rr[x]=x;
if(son[x][0]) dfs(son[x][0]),checkmin(ll[x],ll[son[x][0]]);
if(son[x][1]) dfs(son[x][1]),checkmax(rr[x],rr[son[x][1]]);
}
}using namespace carisian_tree;
namespace Task{
auto task=[](){
build(a); dfs(); int ans=0;
for(int i=1;i<=n;i++)ans+=a[i]*(i-ll[i]+1)*(rr[i]-i+1);
write(ans); return;
};
}
set<int> s;
int pos[NN],ans,nxt[NN][2];
int len[51][2],l,r;
bool tl,tr;
auto insert=[](int x){
s.insert(x);
if(s.find(x)!=s.begin()){l=*(--s.find(x));nxt[x][0]=l;nxt[l][1]=x;}
if(s.find(x)!=--s.end()){r=*(++s.find(x));nxt[x][1]=r;nxt[r][0]=x;}
};
auto solve=[](int x){
insert(pos[x]); l=r=pos[x]; tl=tr=0;
memset(len,0,sizeof(len));
for(int i=0;i<=k;i++){
if(l!=0){
len[i][0]=l-nxt[l][0];
l=nxt[l][0];
}
if(r!=n+1){
len[i][1]=nxt[r][1]-r;
r=nxt[r][1];
}
if(!l&&r==n+1) break;
}
for(int i=0,j=k;i<=k;++i,--j) ans+=x*len[i][0]*len[j][1];
};
namespace WSN{
inline short main(){
wsn=freopen("kth.in","r",stdin);
wsn=freopen("kth.out","w",stdout);
n=read();k=read()-1;
for(int i=1;i<=n;i++)a[i]=read(),pos[a[i]]=i,nxt[i][0]=0,nxt[i][1]=n+1;
if(k==0) return Task::task(),0;
for(int i=n;i;i--)solve(i);
write(ans);
return 0;
}
}
signed main(){return WSN::main();}
T3 树上路径
其实是一道好题,各种数组的定义在这里再明确一下,感觉那个上面说的不是特别清楚
定义现在考虑的是断开\((x,y)\)后的两颗树的直径如何求
我们现在删掉的是\(y\)的子树,剩下的树算出一个直径记作\(up[y]\),删掉的那个\(y\)里面的直径为\(down[y]\)
\(dp[x][0/1/2]\)表示以\(x\)为根的子树中的最长、次长、次次长链的长度
\(dp[y][3]\)和前面的不是一个东西,可以理解为另一个数组,表示过\(y\)的最长的链的长度
\(len[x][0/1]\)表示不经过\(x\)的最长、次长链长度
tree
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int NN=500005;
namespace AE86{
FILE *wsn;
#define gc() p1==p2 and (p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?EOF:*p1++
char buf[1<<20],*p1=buf,*p2=buf;
auto read=[](){
int x=0,f=1;char ch=gc();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=gc();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=gc();} 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);
};
auto swap_=[](int&a,int&b){a^=b^=a^=b;};
auto checkmax=[](int&a,int b){a=a>b?a:b;};
}using namespace AE86;
int n,ans,dep[NN];
struct node{int u,v;}p[NN];
#define star(e,i,x) for(int i=e.head[x],y=e.to[i];i;i=e.next[i],y=e.to[i])
struct SNOW{
int to[NN<<1],next[NN<<1],head[NN],rp;
inline void add(int x,int y){
to[++rp]=y; next[rp]=head[x]; head[x]=rp;
to[++rp]=x; next[rp]=head[y]; head[y]=rp;
}
}e;
int dp[NN][4],dw[NN],up[NN],mx[NN],len[NN][2];
inline void dfs1(int f,int x,int deep){
dep[x]=deep;
star(e,i,x) if(y!=f){
dfs1(x,y,deep+1);
int tmp=dp[y][0]+1;
if(tmp>dp[x][0]) swap_(dp[x][0],tmp);
if(tmp>dp[x][1]) swap_(dp[x][1],tmp);
if(tmp>dp[x][2]) swap_(dp[x][2],tmp);
checkmax(dw[x],dw[y]);
} checkmax(dw[x],dp[x][0]+dp[x][1]);
}
inline void dfs2(int f,int x){
star(e,i,x) if(y!=f){
int tmp=dw[y];
if(tmp>len[x][0]) swap_(tmp,len[x][0]);
if(tmp>len[x][1]) swap_(tmp,len[x][1]);
}
star(e,i,x) if(y!=f){
if(dp[x][0]==dp[y][0]+1){
dp[y][3]=max(dp[x][3],dp[x][1])+1;
up[y]=max(dp[x][2],dp[x][3])+dp[x][1];
}else if(dp[x][1]==dp[y][0]+1){
dp[y][3]=max(dp[x][3],dp[x][0])+1;
up[y]=max(dp[x][2],dp[x][3])+dp[x][0];
}else{
dp[y][3]=max(dp[x][3],dp[x][0])+1;
up[y]=max(dp[x][1],dp[x][3])+dp[x][0];
}
if(len[x][0]==dw[y]) up[y]=max(up[y],len[x][1]);
else up[y]=max(up[y],len[x][0]);
dfs2(x,y);
}
}
namespace WSN{
inline short main(){
wsn=freopen("tree.in","r",stdin);
wsn=freopen("tree.out","w",stdout);
n=read();
for(int i=1,u,v;i<n;i++)
p[i].u=read(),p[i].v=read(),e.add(p[i].u,p[i].v);
dfs1(0,1,0); dfs2(0,1);
for(int i=1;i<n;i++){
int x=p[i].u,y=p[i].v;
if(dep[x]<dep[y])swap_(x,y);
mx[up[x]+1]=max(mx[up[x]+1],dw[x]+1);
mx[dw[x]+1]=max(mx[dw[x]+1],up[x]+1);
}
for(int i=n;i;i--)mx[i]=max(mx[i],mx[i+1]),ans+=mx[i];
write(ans);
return 0;
}
}
signed main(){return WSN::main();}
T4 糖果
还没改出来,正在改
\(\color{white}{还是不理想,加上挂的分数仅仅能够排到rk44,因为很多人都是切了两道题的,180确实不算高,发现考场上觉得切掉一道题后效率就变得不是那么高了,然而觉得最稳的一道题还被卡时限了。。。。。名次就更低了,确实有一些慌,但是并没有对平日生活造成影响,只是觉得CSP过于之低,又加上之后的这些比赛没有特别优秀的表现,仅仅是有几次题目顺手分还可以看。。。。比原来颓废的时间少了,~~但是并没有什么提升,成绩反而变低了(大雾)~~,就很不理解,不过记得当时CSP前有同学状态不是很好,但是CSP上表现很好,可能这就是在沉默中爆发??不过我还是相信只要不颓废,做题,思考,按步骤来就是可以的,考场上不能做出一道题效率就降低一定要努力思考到考试最后}\)