大体状况
195/300
爆炸= =
T1 draw
分析
Q1直接交换。
Q2半天想不出来。
浪费大量时间到最后就是一个求逆序对= =
代码
(敲了40分的暴搜BFS。
#include<bits/stdc++.h>
using namespace std;
#define Komachi is retarded
#define REP(i,a,b) for(int i=(a),i##_end_=(b);i<i##_end_;i++)
#define DREP(i,a,b) for(int i=(a),i##_end_=(b);i>i##_end_;i--)
#define chkmax(a,b) a=max(a,b)
#define chkmin(a,b) a=min(a,b)
#define LL long long
void Rd(int &res){
char c;res=0;
while((c=getchar())<48);
do res=(res<<3)+(res<<1)+(c^48);
while((c=getchar())>47);
}
#define M 1000004
int n,m,A[M];
struct P40{
int P[14];
int Dis[44444],id,s,t;
map<int,int>ID;
int Re[44444];
int Hash(int *P){
int h=0;
REP(i,1,n+1)h=(h<<3)+(h<<1)+P[i];
return h;
}
int Array(int x,int *P){
DREP(i,n,0)P[i]=x%10,x/=10;
}
int Q[44444],l,r;
int BFS(){
Dis[s]=1;
l=r=0;
Q[r++]=s;
while(l<r){
int u=Q[l++];
if(u==t)return Dis[u]-1;
int x=Re[u];
Array(x,P);
// cerr<<Dis[u]<<":"<<endl;
// REP(i,1,n+1)cerr<<P[i]<<' ';cerr<<endl;
REP(i,1,n){
swap(P[i],P[i+1]);
int v=ID[Hash(P)];
if(!Dis[v]) Dis[v]=Dis[u]+1,Q[r++]=v;
swap(P[i],P[i+1]);
}
}
}
void Solve(){
REP(i,1,n+1)P[i]=i;
t=Hash(P);
do{
int h=Hash(P);
ID[h]=++id,Re[id]=h;
}while(next_permutation(P+1,P+n+1));
t=ID[t];
s=ID[Hash(A)];
// cerr<<s<<","<<t<<endl;
printf("%d\n",BFS());
}
}P40;
struct P100{
int Ans,B[M];
void Merge(int l,int r){
if(l>=r)return;
int mid=l+r>>1;
Merge(l,mid);
Merge(mid+1,r);
int i=l,j=mid+1,k=l;
while(i<=mid && j<=r){
if(A[i]<=A[j])B[k++]=A[i++];
else Ans+=mid-i+1,B[k++]=A[j++];
}
while(i<=mid)B[k++]=A[i++];
while(j<=r)B[k++]=A[j++];
REP(v,l,r+1)A[v]=B[v];
}
void Solve(){
Ans=0;
Merge(1,n);
printf("%d\n",Ans);
}
}P100;
int main(){
freopen("draw.in","r",stdin);
freopen("draw.out","w",stdout);
Rd(n),Rd(m);
REP(i,1,n+1)A[i]=i;
REP(i,0,m){
int x;
Rd(x);
swap(A[x],A[x+1]);
}
REP(i,1,n+1)printf("%d%c",A[i]," \n"[i==n]);
// if(n<=8)P40.Solve();
P100.Solve();
return 0;
}
T2 run
分析
先对整棵树求一个欧拉序。
那么每个节点就对应一段区间内的不断循环。
然后经过思考发现:
如果在第一次循环内未遇上当前点,那么以后都不会遇上。
那么每次记录到达当前的点的时间,
以及该点是否可用(为起始节点的祖先。
在欧拉序上标记,查询每个被访问到节点的若干时间前是否为可用节点即可。
然后Ans++。
代码
比赛中dfn没有清空导致数组访问越界,变成了40分= =
#include<bits/stdc++.h>
using namespace std;
#define Komachi is retarded
#define REP(i,a,b) for(int i=(a),i##_end_=(b);i<i##_end_;i++)
#define DREP(i,a,b) for(int i=(a),i##_end_=(b);i>i##_end_;i--)
#define chkmax(a,b) a=max(a,b)
#define chkmin(a,b) a=min(a,b)
void Rd(int &res){
char c;res=0;
while((c=getchar())<48);
do res=(res<<3)+(res<<1)+(c^48);
while((c=getchar())>47);
}
int T,n;
#define M 500004
int Next[M],V[M],Head[M],tot,Tmp[M];
void Add_Edge(int u,int v){
Next[++tot]=Head[u],V[Head[u]=tot]=v;
}
#define LREP(i,A) for(int i=Head[A];i;i=Next[i])
int DFN[M],P[M<<1],dfn;
int Fa[M];
void DFS(int A){
DFN[A]=++dfn;
P[dfn]=A;
LREP(i,A){
Fa[V[i]]=A;
DFS(V[i]);
P[++dfn]=A;
}
}
struct P100{
int x,Tim[M];
bool Mark[M<<1];
void Solve(){
memset(Tim,-1,sizeof(Tim));
memset(Mark,0,sizeof(Mark));
Rd(x);
int tim=0,Ans=0;
while(x){
Tim[x]=tim;Mark[DFN[x]]=1;
x=Fa[x];tim++;
}
REP(i,1,dfn+1){
if(Tim[P[i]]==-1)continue;
int pos=i-Tim[P[i]];
if(pos<=0)continue;
if(Mark[pos])
Ans++,Mark[pos]=0;
}
printf("%d\n",Ans);
}
}P100;
int main(){
Rd(T);
while(T--){
memset(Head,tot=dfn=0,sizeof(Head));
Rd(n);
REP(i,1,n+1){
int p;
Rd(p);
REP(j,0,p)Rd(Tmp[j]);
DREP(j,p-1,-1)Add_Edge(i,Tmp[j]);
}
DFS(1);
P100.Solve();
}
return 0;
}
T3 tri
分析
P20
直接模拟
P40
然后发现Sum值是
Cn+1,1
到
Cn+1,n+1
于是处理出阶乘和逆元可在
O(nlogm)
内直接计算答案解决。
P50
m=1 时可找规律得到答案为 n∗2n−1
P60
m=2 时可找规律得到答案为 n∗(n+1)∗2n−2
P95
看起来上面很有规律。
然而
m=3
时答案为
n∗n∗(n+3)∗2n−3
。
???
所以没有规律
然后要考虑其组合上的意义。
在n个颜色中取出i个颜色,然后作一个长度为m的序列,颜色可重。
可以发现序列的长度较小,
所以枚举长度为m的序列中颜色的个数。
然后容斥算答案。
Fi=∑i−1j=0Ci−ji∗(i−j)m∗(j%2?−1:1)
Ans=∑min(n,m)i=1Fi∗Cin∗2−i
复杂度为
O(m2)
P100
将上面这个改写为第二类斯特林数的形式。
即
Ans=∑min(n,m)i=1S(m,i)∗i!
NTT优化求出
S(m,1)−S(m,m)
然后我不会。
代码
只有95分。
#include<bits/stdc++.h>
using namespace std;
#define Komachi is retarded
#define REP(i,a,b) for(int i=(a),i##_end_=(b);i<i##_end_;i++)
#define DREP(i,a,b) for(int i=(a),i##_end_=(b);i>i##_end_;i--)
#define chkmax(a,b) a=max(a,b)
#define chkmin(a,b) a=min(a,b)
void Rd(int &res){
char c;res=0;
while((c=getchar())<48);
do res=(res<<3)+(res<<1)+(c^48);
while((c=getchar())>47);
}
#define Mod 998244353
int n,m;
int Pow(int x,int p){
int Res=1,k=x;
while(p){
if(p&1)Res=1ll*Res*k%Mod;
k=1ll*k*k%Mod;p>>=1;
}
return Res;
}
int C(int n,int i,int Inv){
int Res=1;
REP(a,n-i+1,n+1)
Res=1ll*Res*a%Mod;
return 1ll*Res*Inv%Mod;
}
struct P40{
static const int M=1000004;
int Fac[M],Inv[M];
void Solve(){
Fac[0]=Inv[0]=1;
REP(i,1,n+1)Fac[i]=1ll*Fac[i-1]*i%Mod,Inv[i]=Pow(Fac[i],Mod-2);
int Ans=0;
REP(i,1,n+1) (Ans+=1ll*Inv[i]*Inv[n-i]%Mod*Pow(i,m)%Mod)%=Mod;
printf("%d\n",1ll*Ans*Fac[n]%Mod);
}
}P40;
struct P95{
static const int M=3004;
int Fac[M],Inv[M],Pw[M],Ans;
void Solve(){
Fac[0]=Inv[0]=1;
REP(i,1,m+1){
Fac[i]=1ll*Fac[i-1]*i%Mod;
Inv[i]=Pow(Fac[i],Mod-2);
Pw[i]=Pow(i,m);
}
Ans=0;
REP(i,1,min(n,m)+1){
int f=1,Res=0;
DREP(j,i,0){
(Res+=1ll*f*Fac[i]*Inv[j]%Mod*Inv[i-j]%Mod*Pw[j]%Mod)%=Mod;
f=-f;
}
Res=1ll*Res*C(n,i,Inv[i])%Mod*Pow(2,n-i)%Mod;
(Ans+=Res)%=Mod;
}
(Ans+=Mod)%=Mod;
printf("%d\n",Ans);
}
}P95;
int main(){
Rd(n),Rd(m);
if(m==0) printf("%d\n",Pow(2,n)-1);
else if(n<=1000000)P40.Solve();
else P95.Solve();
return 0;
}