noip模拟36
A. Dove打扑克
一道很水的题,然而我考场上再次没对拍然后打挂..
忘记去重了..
此题开个桶计数就行了,树状数组优化后会飞快,不优化也能过,最大的测试点只有\(300ms+\)..
A_code
#include<bits/stdc++.h>
using namespace std;
namespace BSS
{
#define ll long long int
#define re register ll
#define lf double
#define lb lower_bound
#define ub upper_bound
#define mp make_pair
#define File(x,y) freopen(#x,"r",stdin),freopen(#y,"w",stdout)
#define Fill(x,y) memset(x,y,sizeof x);
#define Copy(x,y) memset(y,x,sizeof x);
inline ll read()
{
ll ss=0; bool cit=1; char ch;
while(!isdigit(ch=getchar())) if(ch=='-') cit=0;
while(isdigit(ch)) ss=(ss<<3)+(ss<<1)+(ch^48),ch=getchar();
return cit?ss:-ss;
}
} using namespace BSS;
const ll N=1e5+51;
ll m,n,cnt,tot;
ll vis[N],with[N],w[N],rk[N],siz[N],fa[N],v[N],v2[N];
queue<ll> que;
ll find(ll x){
return (x==fa[x]) ? x : (fa[x]=find(fa[x])) ;
}
signed main(){
n=read(); m=read();
ll opt,x,y,wx,wy,temp;
vis[1]=n; with[++cnt]=1,rk[1]=cnt;
for(re i=1;i<=n;i++) w[i]=1,fa[i]=i;
for(re i=1;i<=m;i++){
opt=read(); x=read();
if(opt&1){
y=read(); if(find(x)==find(y)) continue;
wx=w[find(x)],wy=w[find(y)];
vis[wx]--,vis[wy]--;
w[find(x)]+=wy,w[find(y)]=0;
fa[find(y)]=find(x);
if((!vis[wx]) and (!v[rk[wx]])){
que.push(rk[wx]); v[rk[wx]]=1;
with[rk[wx]]=0;
}
if((!vis[wy]) and (!v[rk[wy]])){
que.push(rk[wy]); v[rk[wy]]=1;
with[rk[wy]]=0;
}
if(vis[w[find(x)]]){
vis[w[find(x)]]++;
}
else{
vis[w[find(x)]]++;
if(que.size()){
temp=que.front();
que.pop(); v[temp]=0;
}
else temp=++cnt;
with[temp]=w[find(x)];
rk[w[find(x)]]=temp;
}
}
else{
ll now,ans=0;
for(re i=1;i<=cnt;i++){
now=with[i]; if(!vis[now]) continue;
for(re j=i;j<=cnt;j++){
if(!vis[with[j]]) continue;
if(abs(now-with[j])>=x){
if(with[j]==now) ans+=vis[now]*(vis[now]-1)/2;
else ans+=vis[now]*vis[with[j]];
}
}
}
printf("%lld\n",ans);
}
}
return 0;
}
B. Cicada 与排序
一个大模拟上的Dp..
可以将归并排序看成异化的线段树..
\(a\)的值域大于\(n\),所以可以考虑离散化..
但是我们又发现离散化没有什么用,但是对于正解我们却发现了值域的突破口.
我们可以考虑每个单值的答案..
\(f[i][j][k]\)表示深度为\(i\),原来的位置在\(j\)后来移动到了\(k\)位置的概率..
但是发现复杂度过高,需要预先处理一些值,于是选择考虑辅助数组.
\(g[i][j]\)表示当一个指针在\(i\),另一个指针在\(j\)的概率.
关于如何转移,按照归并排序的正常流程即可..
在左区间指针和右区间指针值相同的时候,选择概率转移.
最后答案累加起来就可以了..
B_code
#include<bits/stdc++.h>
using namespace std;
namespace BSS {
#define ll long long int
#define ull unsigend ll
#define re register ll
#define lf double
#define mp(x,y) make_pair(x,y)
#define lb lower_bound
#define ub upper_bound
#define File(x,y) freopen(#x,"r",stdin),freopen(#y,"w",stdout)
#define Fill(x,y) memset(x,y,sizeof x)
#define Copy(x,y) memset(x,y,sizeof x)
inline ll read() {
ll ss=0; bool cit=1; char ch;
while(!isdigit(ch=getchar())) if(ch=='-') cit=0;
while(isdigit(ch)) ss=(ss<<3)+(ss<<1)+(ch^48),ch=getchar();
return cit?ss:-ss;
}
} using namespace BSS;
const ll N=505;
const ll mod=998244353;
ll m,n,cnt,tot,inv;
ll val[N];
ll g[N][N],dp[N][N][N];
inline ll ksm(ll a,ll b,ll c){
ll temp=1; a%=c;
while(b){
if(b&1) temp=(temp*a)%c;
a=(a*a)%c; b>>=1;
}
return temp%c;
}
void merge(ll dep,ll l,ll r){
if(l==r){
dp[dep][l][l]=1;
return ;
}
ll mid=(l+r)>>1;
merge(dep+1,l,mid); merge(dep+1,mid+1,r);
Fill(g,0); ll len1=mid-l+1,len2=r-mid;
g[0][0]=1;
for(re i=0;i<=len1;i++){
for(re j=0;j<=len2;j++){
if(i==len1) (g[i][j+1]+=g[i][j]%mod)%=mod;
else if(j==len2) (g[i+1][j]+=g[i][j]%mod)%=mod;
else if(val[l+i]>val[mid+1+j]) (g[i][j+1]+=g[i][j]%mod)%=mod;
else if(val[l+i]<val[mid+1+j]) (g[i+1][j]+=g[i][j]%mod)%=mod;
else{
(g[i+1][j]+=g[i][j]*inv%mod)%=mod;
(g[i][j+1]+=g[i][j]*inv%mod)%=mod;
}
}
}
for(re i=l;i<=r;i++){
for(re j=0;j<=len1;j++){
for(re k=0;k<=len2;k++){
if(j==len1 and k==len2) continue;
if(j==len1) (dp[dep][i][j+k+l]+=dp[dep+1][i][k+mid+1]*g[j][k]%mod)%=mod;
else if(k==len2) (dp[dep][i][j+k+l]+=dp[dep+1][i][j+l]*g[j][k]%mod)%=mod;
else if(val[l+j]<val[mid+1+k]) (dp[dep][i][j+k+l]+=dp[dep+1][i][j+l]*g[j][k]%mod)%=mod;
else if(val[l+j]>val[mid+1+k]) (dp[dep][i][j+k+l]+=dp[dep+1][i][k+mid+1]*g[j][k]%mod)%=mod;
else (dp[dep][i][j+k+l]+=(dp[dep+1][i][j+l]+dp[dep+1][i][k+mid+1])%mod*(g[j][k]*inv%mod)%mod)%=mod;
}
}
}
sort(val+l,val+r+1);
}
signed main(){
n=read(); inv=ksm(2,mod-2,mod);
for(re i=1;i<=n;i++) val[i]=read();
merge(1,1,n);
for(re i=1;i<=n;i++){
ll ans=0;
for(re j=1;j<=n;j++){
(ans+=dp[1][i][j]*j)%=mod;
}
printf("%lld ",ans);
}
return 0;
}
C. Cicada拿衣服
要透析位运算的性质,总共有\(log_2(n)\)位,所以本质不同的区间也只有\(log_2\)级别个.
然后链表维护每个区间的\(or\)和\(and\),
对于\(min-max\),发现一定单调,于是在最远的合法区间二分即可.
C_code
#include<bits/stdc++.h>
using namespace std;
namespace BSS
{
#define ll int
#define ull unsigned ll
#define re register ll
#define lf double
#define lb lower_bound
#define ub upper_bound
#define mp make_pair
#define File(x,y) freopen(#x,"r",stdin),freopen(#y,"w",stdout)
#define Fill(x,y) memset(x,y,sizeof x)
#define Copy(x,y) memcpy(x,y,sizeof x)
inline ll read()
{
ll ss=0; bool cit=1; char ch;
while(!isdigit(ch=getchar())) if(ch=='-') cit=0;
while(isdigit(ch)) ss=(ss<<3)+(ss<<1)+(ch^48),ch=getchar();
return cit?ss:-ss;
}
} using namespace BSS;
const ll N=1e6+21;
ll m,n,cnt,head,tail;
ll val[N],lg2[23];
ll stmin[N][23],stmax[N][23],stor[N][23],stand[N][23];
struct I { ll l,r,pre,nxt; } e[N];
vector<ll> d[N]; multiset<ll> s;
inline void Pre(){
n=read(),m=read();
for(re i=1;i<=n;i++)
stmin[i][0]=stmax[i][0]=stor[i][0]=stand[i][0]=val[i]=read();
for(re i=2;i<=n;i++) lg2[i]=lg2[i>>1]+1;
for(re j=1;j<=20;j++)
for(re i=1;i+(1<<j)-1<=n;i++){
stmin[i][j]=min(stmin[i][j-1],stmin[i+(1<<j-1)][j-1]),
stmax[i][j]=max(stmax[i][j-1],stmax[i+(1<<j-1)][j-1]),
stor[i][j]=stor[i][j-1] | stor[i+(1<<j-1)][j-1],
stand[i][j]=stand[i][j-1] & stand[i+(1<<j-1)][j-1];
}
}
inline ll qmin(ll l,ll r){ ll Log=lg2[r-l+1]; return min(stmin[l][Log],stmin[r-(1<<Log)+1][Log]); }
inline ll qmax(ll l,ll r){ ll Log=lg2[r-l+1]; return max(stmax[l][Log],stmax[r-(1<<Log)+1][Log]); }
inline ll qor(ll l,ll r){ ll Log=lg2[r-l+1]; return stor[l][Log] | stor[r-(1<<Log)+1][Log]; }
inline ll qand(ll l,ll r){ ll Log=lg2[r-l+1]; return stand[l][Log] & stand[r-(1<<Log)+1][Log]; }
inline void update(ll i){
e[++cnt].l=i,e[cnt].r=i,e[cnt].pre=tail,e[tail].nxt=cnt,tail=cnt;
if(cnt==1) head=1; ll now=cnt,pre;
while(pre=e[now].pre){
if(qor(e[now].l,i)==qor(e[pre].l,i) and qand(e[now].l,i)==qand(e[pre].l,i)){
e[now].l=e[pre].l,e[now].pre=e[pre].pre,e[e[pre].pre].nxt=now;
if(!e[now].pre) head=now;
}
else now=e[now].pre;
}
}
inline bool check(ll l,ll r){
return qmin(l,r)+qor(l,r)-qmax(l,r)-qand(l,r)>=m ;
}
inline void Work(){
ll le,ri,res,mid;
for(re i=1;i<=n;i++){
update(i); res=-1;
for(re j=head;j;j=e[j].nxt){
if(check(e[j].r,i)){
le=e[j].l,ri=e[j].r;
while(le<=ri){
mid=(le+ri)>>1;
if(check(mid,i)) res=mid,ri=mid-1;
else le=mid+1;
}
break;
}
}
if(res!=-1) d[res].push_back(i-res+1),d[i+1].push_back(res-i-1);
}
for(re i=1;i<=n;i++){
for(auto j : d[i]){
if(j>0) s.insert(j);
else s.erase(s.find(-j));
}
if(s.size()) printf("%d ",*(--s.end()));
else printf("-1 ");
}
}
signed main(){
Pre(),Work();
exit(0);
}