传送门:CF1129
手速变慢了…代码熟练度不够
A1&2. Toy Train
g [ x ] g[x] g[x]存起点为 a i a_i ai的所有 b i b_i bi(按 d i s ( a i , b i ) dis(a_i,b_i) dis(ai,bi)排序)。
设 m x = m a x ( g [ x ] . s i z e ( ) ) mx=max(g[x].size()) mx=max(g[x].size()),则对于任意起点,前 m x − 1 mx-1 mx−1圈一定是要走的,只是最后一圈不一样。,枚举起点 O ( n 2 ) O(n^2) O(n2)跑一遍最后一圈即可。(有一些细节)
#include<bits/stdc++.h>
#define mkp make_pair
#define fi first
#define sc second
#define pii pair<int,int>
#define pb push_back
using namespace std;
typedef long long ll;
typedef double db;
const int N=20005;
int n,m,cnt,mx,cot,pos[5010];ll ans,res;
vector<int>g[5010],rep[5010];
int del[5010];
inline int dis(int x,int y)
{
if(y>=x) return y-x;
return y-x+n;
}
inline int add(int x,int y)
{
x+=y;return x>n?x-n:x;
}
inline void cal(int st)
{
int i,nw=0;cot=0;ll bs=0;
memset(del,0,sizeof(del));
for(i=1;i<=n;++i){
rep[i].clear();pos[i]=0;
if(g[i].size()+1<mx) continue;
if(g[i].size()==mx){
if(mx>1 && dis(st,i)>dis(st,add(i,g[i][1])) && add(i,g[i][1])!=st){
nw++;del[add(i,g[i][1])]++;
}
rep[i].pb(g[i][0]),cot++;
}else{
if(mx>1 && dis(st,i)>dis(st,add(i,g[i][0])) && add(i,g[i][0])!=st)
nw++,del[add(i,g[i][0])]++;
}
}
for(i=st;;i= i==n?1:(i+1)){
if(pos[i]<rep[i].size()){
nw++;del[add(i,rep[i][pos[i]])]++;
pos[i]++;cot--;
}
nw-=del[i];del[i]=0;
if((!nw)&&(!cot)){
printf("%I64d ",bs+ans);return;
}
bs++;
}
}
int main(){
int i,j,a,b;
scanf("%d%d",&n,&m);
for(i=1;i<=m;++i){
scanf("%d%d",&a,&b);
g[a].pb(dis(a,b));
}
for(i=1;i<=n;++i) sort(g[i].begin(),g[i].end());
mx=0;
for(i=1;i<=n;++i) mx=max(mx,(int)g[i].size());
ans+=(ll)(mx-1)*n;
for(int x=1;x<=n;++x) cal(x);
return 0;
}
B.Wrong Answer
这题想了很久,因为没法证明构造是对的,然而AC了…
考虑构造一个长度为
2000
2000
2000的答案,分成三部分:
一段前缀0+一个’-1’+全为正的后缀(设和为
S
S
S,长度为
l
l
l)。
则Alice的答案是 S ⋅ l S·l S⋅l,实际答案为 ( S − 1 ) n (S-1)n (S−1)n。
S n − n − S l = k → S ( n − l ) = k + n Sn-n-Sl=k\to S(n-l)=k+n Sn−n−Sl=k→S(n−l)=k+n
枚举 l l l判断即可。
正确性玄学?
C.Morse Code
构造 S A M SAM SAM,对于每个结点维护 t i m i tim_i timi表示该结点right集合中最前的位置, d p i dp_i dpi表示从根走到当前结点的所有字符串能构成的摩斯密码的方案数。
拓扑排序 d p dp dp,每次 O ( 2 4 ) O(2^4) O(24)暴力转移。
#include<bits/stdc++.h>
#define mkp make_pair
#define fi first
#define sc second
#define pii pair<int,int>
#define pb push_back
using namespace std;
typedef long long ll;
typedef double db;
const int N=3005,mod=1e9+7;
int n,cnt=1,last,cur=1,dis[N<<1];
int dp[N<<1],tim[N<<1],ans[N];
ll sum;
inline void ad(int &x,int y){x+=y;if(x>=mod) x-=mod;}
inline void dc(int &x,int y){x-=y;if(x<0) x+=mod;}
int ch[N<<1][2];
struct bk{
int fa,size;
}t[N<<1];
inline void insert(int a,int d)
{
last=cur;cur=++cnt;dis[cur]=d;tim[cur]=d;
int p=last;
for(;p && !ch[p][a];p=t[p].fa) ch[p][a]=cur;
if(!p) t[cur].fa=1;else{
int q=ch[p][a];
if(dis[q]==dis[p]+1) t[cur].fa=q;else{
int nt=++cnt;tim[cnt]=tim[q];
dis[nt]=dis[p]+1;
memcpy(ch[nt],ch[q],sizeof(ch[q]));
t[nt].fa=t[q].fa;t[q].fa=nt;t[cur].fa=nt;
for(;ch[p][a]==q;p=t[p].fa) ch[p][a]=nt;
}
}
t[cur].size=1;
}
int dd[N<<1];
queue<int>que;
int main(){
int i,j,k,a,b,c,i1,i2,i3,i4,j1,j2,j3,j4;
scanf("%d",&n);
for(i=1;i<=n;++i){
scanf("%d",&a);
insert(a,i);
}
for(i=1;i<=cnt;++i)
for(j=0;j<2;++j)
if(ch[i][j]) dd[ch[i][j]]++;
que.push(1);dp[1]=1;
for(;!que.empty();){
a=que.front();que.pop();b=dp[a];
ad(ans[tim[a]],b);
for(i1=0;i1<2;++i1) if(ch[a][i1]){
ad(dp[(j1=ch[a][i1])],b);
for(i2=0;i2<2;++i2) if(ch[j1][i2]){
ad(dp[(j2=ch[j1][i2])],b);
for(i3=0;i3<2;++i3) if(ch[j2][i3]){
ad(dp[(j3=ch[j2][i3])],b);
for(i4=0;i4<2;++i4) if(ch[j3][i4]){
ad(dp[ch[j3][i4]],b);
}
}
}
}
if(ch[ch[ch[ch[a][0]][0]][1]][1])
dc(dp[ch[ch[ch[ch[a][0]][0]][1]][1]],b);
if(ch[ch[ch[ch[a][0]][1]][0]][1])
dc(dp[ch[ch[ch[ch[a][0]][1]][0]][1]],b);
if(ch[ch[ch[ch[a][1]][1]][1]][0])
dc(dp[ch[ch[ch[ch[a][1]][1]][1]][0]],b);
if(ch[ch[ch[ch[a][1]][1]][1]][1])
dc(dp[ch[ch[ch[ch[a][1]][1]][1]][1]],b);
for(j=0;j<2;++j) if(ch[a][j]){
dd[ch[a][j]]--;if(!dd[ch[a][j]]) que.push(ch[a][j]);
}
}
for(i=1;i<n;++i) ad(ans[i+1],ans[i]);
for(i=1;i<=n;++i) printf("%d\n",ans[i]);
return 0;
}
D.Isolation
按 r = 1 − n r=1-n r=1−n扫过去,动态维护 t i t_i ti表示区间 [ i , r ] [i,r] [i,r]中只出现一次的数的个数,每次处理一下 a r a_r ar和前驱之间的值再 d p dp dp一下即可。
复杂度也比较玄学,题解没看懂,似乎是 O ( n n ) O(n\sqrt n) O(nn)的。
E.Legendary Tree
构造题一向比较巧妙:
设根为1。
首先可以 O ( n ) O(n) O(n)求出每个点的子树大小 s z [ i ] sz[i] sz[i](询问1到其他所有点的路径经过点 i i i的个数)。
将点按 s z sz sz升序排序,逐个找到每个点的儿子结点。
设集合 S S S,存储所有未找到父节点的结点,扫到结点 i i i时,暴力二分找到最小的 k k k,满足 1 1 1到 s 1 , s 2 , . . . , s k s_1,s_2,...,s_k s1,s2,...,sk的路径经过点 i i i的个数非0, k k k就是 i i i的儿子结点。
询问次数在 n + n log n n+n\log n n+nlogn级别。