传送门:CF1129
手速变慢了…代码熟练度不够
A1&2. Toy Train
存起点为 的所有 (按 排序)。
设 ,则对于任意起点,前 圈一定是要走的,只是最后一圈不一样。,枚举起点 跑一遍最后一圈即可。(有一些细节)
#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了…
考虑构造一个长度为
的答案,分成三部分:
一段前缀0+一个’-1’+全为正的后缀(设和为
,长度为
)。
则Alice的答案是 ,实际答案为 。
枚举 判断即可。
正确性玄学?
C.Morse Code
构造 ,对于每个结点维护 表示该结点right集合中最前的位置, 表示从根走到当前结点的所有字符串能构成的摩斯密码的方案数。
拓扑排序 ,每次 暴力转移。
#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
按 扫过去,动态维护 表示区间 中只出现一次的数的个数,每次处理一下 和前驱之间的值再 一下即可。
复杂度也比较玄学,题解没看懂,似乎是 的。
E.Legendary Tree
构造题一向比较巧妙:
设根为1。
首先可以 求出每个点的子树大小 (询问1到其他所有点的路径经过点 的个数)。
将点按 升序排序,逐个找到每个点的儿子结点。
设集合 ,存储所有未找到父节点的结点,扫到结点 时,暴力二分找到最小的 ,满足 到 的路径经过点 的个数非0, 就是 的儿子结点。
询问次数在 级别。