QOJ.ac
QOJ
ID | 题目 | 提交者 | 结果 | 用时 | 内存 | 语言 | 文件大小 | 提交时间 | 测评时间 |
---|---|---|---|---|---|---|---|---|---|
#488865 | #8819. CNOI Knowledge | XiaoShanYunPan | WA | 1ms | 3604kb | C++14 | 1.8kb | 2024-07-24 15:58:07 | 2024-07-24 15:58:07 |
Judging History
answer
/*
QOJ8819 CNOI Knowledge
神金交互题。
一眼丁真鉴定询问次数为 $\mathcal{O}(n\log n)$,那么每个点都有 $\log$ 的次数。
发现如果一个字符从未出现过,那么会和长度为 $i$ 的前缀组成 $i+1$ 个新串。
于是二分即可找到最左侧使得当前这个字符第一次出现的位置就做完了。
为了削减常数,需要自己算已知区间的答案,这个东西唐完了。
考虑枚举目前的后缀长度,找到前面这个东西最后一次出现的位置,这个后缀就会给中间一段都贡献上。
找的话用 Hash 能够解决,但是就是很唐……
不想那么唐其实还有个神奇 KMP,我们实际上只需要后缀的答案。
只需要后缀的答案的话我们就暴力记录每个后缀的 next 数组跟着走,找到 LCP 即可。
*/
#include<bits/stdc++.h>
using namespace std;
using ull=unsigned long long;
constexpr int N=1145;
int ans[N],tmp[N];//tmp是后缀答案
int nxt[N][N];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
int n,res,cnt=0;
cin>>n;
ans[1]=cnt=tmp[1]=1;
for(int i=2;i<=n;i++)
{
cout<<"? 1 "<<i<<endl;
cin>>res;
int l=1,r=i-1,pos=-1;
while(l<=r)
{
int mid=(l+r)>>1;
cout<<"? "<<mid<<" "<<i<<endl;
cin>>res;
if(res==tmp[mid]+i-mid+1)r=mid-1;
else pos=mid,l=mid+1;
}
if(pos==-1)ans[i]=++cnt;
else ans[i]=ans[pos];
tmp[i]=1;
int mx=0;//长度小于mx的都没用
for(int j=i-1;j;j--)
{
int now=nxt[j][i-1];
while(now&&ans[i]!=ans[j+now])now=nxt[j][j+now-1];
if(now||ans[i]==ans[j])nxt[j][j+now]=now+1;
//next 数组咋求来着?
mx=max(mx,nxt[j][i]);
tmp[j]+=(i-j+1-mx);
}
}
cout<<"! ";
for(int i=1;i<=n;i++)cout<<ans[i]<<" ";
cout<<endl;
return 0;
}
详细
Test #1:
score: 0
Wrong Answer
time: 1ms
memory: 3604kb
input:
12 3 3 6 6 10 6 10 15 10 15 21 10 21 27 15 27 20 34 14 6 9 43 20 10 14 52 19 9 5 2 62 25 8 5 3 72 25 9 6
output:
? 1 2 ? 1 2 ? 1 3 ? 1 3 ? 1 4 ? 2 4 ? 1 4 ? 1 5 ? 2 5 ? 1 5 ? 1 6 ? 3 6 ? 1 6 ? 1 7 ? 3 7 ? 1 7 ? 2 7 ? 1 8 ? 4 8 ? 6 8 ? 5 8 ? 1 9 ? 4 9 ? 6 9 ? 5 9 ? 1 10 ? 5 10 ? 7 10 ? 8 10 ? 9 10 ? 1 11 ? 5 11 ? 8 11 ? 9 11 ? 10 11 ? 1 12 ? 6 12 ? 9 12 ? 10 12 ! 1 2 3 4 5 6 2 5 5 5 5 5
result:
wrong answer Wrong Answer.