noip模拟8

A 图书管理

之前考过。。。

但是我忘了咋写了,然后随便胡了个动态开点权值数上去,O(n2logn) 拿了 80。。。

维护一个桶,检测到进来的两个数在中位数同侧,则中位数移动,否则不移动,然后就好了?。。。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
int n;
const int N=1e4+4;
int mx=1e4;
int a[N];
bitset<N>vis;
signed main()
{
freopen("book.in","r",stdin);
freopen("book.out","w",stdout);
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>n;long long ans=0;
for(int i=1;i<=n;i++)cin>>a[i],ans+=(1ll*i*i*a[i]);
for(int i=1;i<=n;i++)
{
int now=a[i];
vis.reset();
vis[a[i]]=1;
for(int j=i+2;j<=n;j+=2)
{
int a1=a[j-1],a2=a[j];
if(a1>a2)swap(a1,a2);
vis[a1]=1,vis[a2]=1;
if(a1<now&&a2>now);
else if(a1<now&&a2<now)
{
--now;
while(!vis[now])now--;
}
else if(a1>now&&a2>now)
{
++now;
while(!vis[now])now++;
}
ans+=(1ll*i*j*now);
}
}
cout<<ans;
return 0;
}

哎呀我是真唐,这都忘了。。

B 两棵树

连通块数 = 剩余的点数 − 剩余的边数

贡献被拆成四个部分:点 × 点 − 边 × 点 − 点 × 边 + 边 × 边

这里以 边 × 边 为例,对于树 T 的边 (u,v) 假设它被保留(概率 14

则树 Uu,v 必定被删除

计算树 u 中有多少边 (x,y) 不以 uv 为端点

每条边 (x,y) 都有 14 概率被保留

用 set 维护每个点的边,时间复杂度 O(nlogn)

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod=998244353;
int Mod(int x)
{
if(x<0)
{
if(x<=-mod) x%=mod;
if(x==0) return 0;
return x+mod;
}
return x>=mod?x%mod:x;
}
const int inv2=499122177,inv4=748683265,inv8=873463809,inv16=935854081;
const int maxn=2e5;
set<pair<int,int> >se;
int deg[maxn+5];
signed main()
{
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
int n;cin>>n;
int ans=Mod(Mod(n*Mod((n-1)*inv4))-Mod((n-2)*Mod((n-1)*inv4)));
for(int i=1;i<n;i++)
{
int u,v;cin>>u>>v;
if(u>v)swap(u,v);
se.insert({u,v});
deg[u]++,deg[v]++;
}
for(int i=1;i<n;i++)
{
int u,v;cin>>u>>v;
if(u>v)swap(u,v);
ans=Mod(ans+Mod((n-1-deg[u]-deg[v]+se.count({u,v}))*inv16));
}
cout<<ans;
return 0;
}

C 函数(fun)

实际上 O(n2) 可以过百万。。。

然后有一个优化可以直接干过标算,就是我们知道异或有一个性质是 A xor B xor A=B,那我们可以开 map 存每个 xi,然后枚举 j=a xor x,找到 j[0,B] 的所有,去反过来找 j xor a=xmap 里面有没有,如果有,那看它的上一位或者下一位有没有大于 B 的,因为这样找到的 j 一定小于 B,那两个函数就是异号,乘起来小于等于 0

否则如果 B>n,直接跑暴力得了,因为枚举次数小于 B 的。

然后就过了,如果手写哈希表能更快。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,q;
const int N=1e6+5;
int x[N];
int a,b;
inline int f(int i)
{
return (i^a)-b;
}
unordered_map<int,int>mp;
signed main()
{
// freopen("C.in","r",stdin);
// freopen("C1.out","w",stdout);
freopen("fun.in","r",stdin);
freopen("fun.out","w",stdout);
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>n>>q;
for(int i=1;i<=n;i++) cin>>x[i],mp[x[i]]=i;
while(q--)
{
cin>>a>>b;
int ans=0;
if(b>n)
{
for(int i=n-1;i>=1;i--)
{
if(f(x[i])*f(x[i+1])<=0){ans=i;break;}
}
}
else
{
for(int i=b;i>=0;i--)
{
int now=mp[i^a];
if(!now)continue;
if(now<n&&(x[now+1]^a)>=b)
{
ans=now;break;
}
if(now>1&&(x[now-1]^a)>=b)
{
ans=now-1;break;
}
}
}
ans=ans>0?ans:-1;
cout<<ans<<"\n";
}
return 0;
}

正解是这样的:

30pts

O(n2) 枚举所有情况。

60pts

通过整体二分,判定 [1,mid] 内是否有解的方式找到第一组解,或利用离线数据结构技巧进行求解,复杂度为 O(nlognlogC),与正解关联性不大。

100pts

求解 xia 最小和最大的两个位置 c1,c2,如果 f(c1)×f(c2)>0 显然无解。

否则每次取 c1,c2 中点 mid,因为 f(c1),f(c2) 异号,所以 f(c1),f(mid)f(mid),f(c2) 必然有一对异号,每次区间长度减半,因此重复 log 次即可。

求解 xia 最小和最大的两个位置 c1,c2 可以利用 trie 快速求解,时间复杂度为 ((n+q)(logn+logV))

D 编辑(edit.cpp)

全输出零有 60,望周知。

30pts

枚举 T 的某一子串进行编辑距离求解的DP,具体状态为让 A 变成 B,现在只考虑 A[1:i] 变成 B[1:j] 的编辑距离为 f[i][j],转移时考虑删除,添加,修改第 i+1 个位置即可,时间复杂度为 O(n4)​。

100pts

枚举每个后缀,fi,j 表示最大的 x,使得 S[1:x]T[1:x+j] 可以在 i 次编辑内得到,显然若 x 可以,所有x0xS[1:x0]T[1:x0+j] 都可以在 i 次编辑内得到。

考虑如何转移,先考虑做完第 j 次编辑操作后往外延申,可以延申的即为 S 的一个后缀和 T 的一个后缀的最长公共前缀,即fi,j=fi,j+LCP(S[fi,j+1:|S|],T[fi,j+j+1..:|T|]),随后我们可以通过对fi+1,j1,fi+1,j,fi+1,j+1 三项进行转移,即考虑下一次的编辑的具体操作是删除添加还是修改。

每次要算的两个后缀的前缀下标不会差超过 k,因此一共至多求 O(nk) 次 LCP,可以利用二分+ hash 的方式解决。

记录每个后缀中 fi,j=|S| 的那些状态,即可计算出最终答案,时间复杂度为 O(nk2+nklogn)

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define ull unsigned long long
const int N=5e4+5,M=35;
const ull p=13331;
int k,s1,t1,ans[M],f[M][M<<1];
ull pp[N],h1[N],h2[N];
char s[N],t[N];
inline ull gt1(int l,int r)
{
return h1[r]-h1[l-1]*pp[r-l+1];
}
inline ull gt2(int l,int r)
{
return h2[r]-h2[l-1]*pp[r-l+1];
}
bool ck(int lx,int rx,int ly,int ry)
{
return gt1(lx,rx)==gt2(ly,ry);
}
int getlcp(int x,int y)
{
int l=1,r=min(s1-x+1,t1-y+1),mid;
while(l<=r)
{
mid=(l+r)>>1;
if(ck(x,x+mid-1,y,y+mid-1)) l=mid+1;
else r=mid-1;
}
return l-1;
}
void solve(int l)
{
for(int i=0;i<=k;i++)
for(int j=0;j<=2*30;j++) f[i][j]=-1;
f[0][30]=0;
for(int i=0;i<=k;i++)
{
for(int j=-i;j<=i;j++)
{
if(f[i][30+j]==-1)continue;
f[i][30+j]=f[i][30+j]+getlcp(f[i][30+j]+1,l-1+f[i][30+j]+j+1);
f[i+1][30+j-1]=max(f[i+1][30+j-1],f[i][30+j]+1);
f[i+1][30+j]=max(f[i+1][30+j],f[i][30+j]+1);
f[i+1][30+j+1]=max(f[i+1][30+j+1],f[i][30+j]);
}
}
for(int j=-k;j<=k;j++)
{
for(int i=0;i<=k;i++)
{
if(s1+j>=1&&s1+j<=t1-l+1&&f[i][30+j]>=s1)
{
++ans[i];break;
}
}
}
}
signed main()
{
freopen("edit.in","r",stdin);
freopen("edit.out","w",stdout);
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>k;
cin>>(s+1)>>(t+1);
s1=strlen(s+1),t1=strlen(t+1);
pp[0]=1;
for(int i=1;i<=s1;i++)
{
h1[i]=h1[i-1]*p+s[i]-'a';
pp[i]=pp[i-1]*p;
}
for(int i=1;i<=t1;i++)
{
h2[i]=h2[i-1]*p+t[i]-'a';
pp[i]=pp[i-1]*p;
}
for(int i=1;i<=t1;i++) solve(i);
for(int i=0;i<=k;i++) cout<<ans[i]<<"\n";
}
posted @   ccjjxx  阅读(43)  评论(7编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
点击右上角即可分享
微信分享提示