您的位置:首页 > 其它

2017.10.16离线赛总结

2017-10-16 21:16 169 查看

draw ——3787

思路:这是源自一个岛国的游戏…

思考一下画鬼脚的本质,一条竖线其实就是一次对相邻两个元素的交换操作。

所以,模拟画鬼脚的时候,只需要按照高度从高到低,依次进行所有的交换操作即可。

那么,对于第二问,众所周知,就是求排列的逆序对数——线段树或BIT

#include<bits/stdc++.h>
#define REP(i,f,t) for(int i=(f),i##_end_=(t);i<=i##_end_;i++)
#define DREP(i,f,t) for(int i=(f),i##_end_=(t);i>=i##_end_;i--)
#define INF 0x3f3f3f3f
#define LL long long
#define N 100005
#define M 1000005
#define INF 0x3f3f3f3f
using namespace std;
int n,m,ans;
int pos
;
struct BIT{
int A
;
void add(int x,int v){
while(x<=N){
A[x]+=v;
x+=x&-x;
}
}
int query(int x){
int res=0;
while(x){
res+=A[x];
x-=x&-x;
}
return res;
}
}BIT;
int main(){
scanf("%d%d",&n,&m);
REP(i,1,n)pos[i]=i;
REP(i,1,m){
int x;scanf("%d",&x);
swap(pos[x],pos[x+1]);
}
REP(i,1,n)printf("%d ",pos[i]);
DREP(i,n,1){
ans+=BIT.query(pos[i]);
BIT.add(pos[i],1);
}
printf("\n%d\n",ans);
return 0;
}


run ——3788

思路:50%:直接模拟,链时,显然 他会消灭所有距离他为偶数条的祖先。

100%:先dfs,遇到一个点就把这个点记录到一个数组,即求出了树的欧拉序,显然如果不考虑循环的话,guard是在这个序列上每次往后走一个,起始位置就是第i个点第一次出现的位置。而显然 YOUSIKI 只可能与他的所有祖先相遇,所以我们要最短路把树上的这些祖先的位置标记出来。

假设 YOUSIKI 现在走到了 x 点,过了 t 秒,那么我们在这个序列上遍历 x 出现的所有位置,并查看这个位置往前 t 个是否为 x 的祖先,如果是,把那个祖先标为1,表示已被消灭。

复杂度 O(n)。

#include<bits/stdc++.h>
#define REP(i,f,t) for(int i=(f),i##_end_=(t);i<=i##_end_;i++)
#define DREP(i,f,t) for(int i=(f),i##_end_=(t);i>=i##_end_;i--)
#define INF 0x3f3f3f3f
#define LL long long
#define N 500005
#define INF 0x3f3f3f3f
using namespace std;
int n,m;

void Rd(int &ans){
ans=0;char c;
while((c=getchar())<48);
do ans=(ans<<1)+(ans<<3)+(c^48);
while((c=getchar())>47);
}
struct P100{
int n,T,ans;
int fa
,Lt[N<<1], mark[N<<1];
vector<int>E
,es
;
void Init(){
memset(fa,0,sizeof(fa));
memset( mark,0,sizeof( mark));
memset(Lt,0,sizeof(Lt));
REP(i,1,n)E[i].clear(),es[i].clear();
T=0;ans=0;
}
void dfs(int x,int f){
Lt[x]=++T;
fa[x]=f;
es[x].push_back(T);
REP(i,0,E[x].size()-1){
int y=E[x][i];
dfs(y,x);
es[x].push_back(++T);
}
}
void solve(){
Init();
Rd(n);
REP(i,1,n){
int k,x;
Rd(k);
REP(j,1,k)Rd(x),E[i].push_back(x);
}
int beg;Rd(beg);
dfs(1,0);
for(int i=beg;i;i=fa[i])mark[Lt[i]]=1;
int t=0;
for(int i=beg;i;i=fa[i]){
REP(j,0,es[i].size()-1){
int dt=es[i][j]-t;
if(dt<=0 || ! mark[dt])continue;
ans++,mark[dt]=0;
}
t++;
}
printf("%d\n",ans);

}
}AC;
int main(){
int Case;
Rd(Case);
while(Case--)AC.solve();
return 0;
}


tri ——3789

思路:

5%:直接输出2^n-1;

20%:强模拟;

40%:推出等式,与组合有关——∑C(i,x)(i=0 to n-1) = C(n,x+1)

m=1时:可以推出通项公式——2^(n-1)*n;

m=2时:也可以推出——n*(2^(n-1)+2^(n-2)*(n-1));

所以可以把问题转换到第 n+1 行上,相当于求 ∑C(n,i)*i^m (i=1 to n) ,O(n lg m);

95%:

考虑这个式子的组合意义

相当于先枚举 i,然后在 n 个颜色里选出 i 个颜色,再用这 i 个颜色可重复排出一个长度为m 的排列的方案数。

换个思路。

枚举最后排列中实际用了几种颜色(*C(n,i) ),其他的颜色可以选或不选( *2^(n-i)),然后再乘上 i 种颜色能排出的排列数。

现在问题在于排列数怎么求,这里可以用 dp 或容斥,说一下容斥。

首先 i 个颜色随便放的方案数是 i^m,但现在可能最终只出现了 i-1 种颜色,所以需要减去C(i,i − 1) ∗ (i − 1)^m,同理再加上 C(i,i − 2) ∗ (i − 2)m…O(m^2)。

可能有人已经发现这个东西就是 S(m,i) ∗ i!,S为第二类斯特林数,表示把 m 个物品放入 i个相同盒子且盒子非空的方案数,因为本题是一个排列,所以需要乘上i! 。

第二类斯特林数可以用 或容斥求,O(n^2) 。

将容斥的式子化简一下,发现是一个卷积的形式,用 NTT 可以求出 S(m, 1) 到S(m, m) , O(mlogm + mlogn),然而这5分太恶心了,有 AK 梦想的选手

可以写一写。

#include<bits/stdc++.h>
#define REP(i,f,t) for(int i=(f),i##_end_=(t);i<=i##_end_;i++)
#define DREP(i,f,t) for(int i=(f),i##_end_=(t);i>=i##_end_;i--)
#define INF 0x3f3f3f3f
#define LL long long
#define N 1000005
#define Mod 998244353
#define INF 0x3f3f3f3f
// LL mod
using namespace std;
LL n,m;
LL dp[3005][3005];
void Init(){
dp[0][0]=1;
REP(i,1,n)
REP(j,1,i)
dp[i][j]=(dp[i-1][j]+dp[i-1][j-1])%Mod;
}
LL Pow(LL a,LL b){
LL res=1;
while(b>0){
if(b&1)res*=a,res%=Mod;
b>>=1,a*=a,a%=Mod;
}
return res;
}
struct p20{
LL ans;
void solve(){
Init();
REP(j,1,n){
LL p=Pow(j,m);
REP(i,j,n)dp[i][j]*=p,dp[i][j]%=Mod,ans+=dp[i][j],ans%=Mod;
}
printf("%lld\n",ans);
}
}p20;
struct p40{
void solve(){
LL C=n;
LL ans=n;
REP(i,1,n){
C=(C*(n-i)%Mod)*Pow(i+1,Mod-2)%Mod;
ans+=C*Pow(i+1,m)%Mod,ans%=Mod;
}
printf("%lld\n",ans);
}
}p40;
struct p95{
LL fac[3005];
void Init(){
dp[0][0]=1;
REP(i,1,3005){
dp[i][0]=1;
REP(j,1,i)dp[i][j]=(dp[i-1][j-1]+dp[i-1][j])%Mod;
}
fac[0]=fac[1]=1;// i!
REP(i,2,3005)fac[i]=(i*fac[i-1])%Mod;
}
void solve(){
Init();
LL ans=0,p=n;
REP(i,1,m){
LL f=-1,sum=Pow(i,m);
DREP(j,i-1,0)sum+=f*dp[i][j]*Pow(j,m)%Mod,sum%=Mod,f=-f;//rong chi
ans+=p*Pow(2,n-i)%Mod*sum%Mod,ans%=Mod;
p=p*(n-i)%Mod*Pow(i+1,Mod-2)%Mod;
}
printf("%lld\n",ans%Mod);
}
}p95;
int main(){
//  freopen("tri.in","r",stdin);
//  freopen("tri.out","w",stdout);
scanf("%lld%lld",&n,&m);
if(!m)printf("%lld\n",Pow(2,n)-1);
else if(m==1)printf("%lld\n",((Pow(2,n-1)%Mod)*n)%Mod);
else if(m==2)printf("%lld\n",((Pow(2,n-1)%Mod+((Pow(2,n-2)%Mod)*(n-1))%Mod)*n)%Mod);
else if(n<=2000)p20.solve();
else if(n<=1000000)p40.solve();
else if(m<=3000)p95.solve();
return 0;
}


小结:今天的题着实有点骚,但有新意,还是可以,只是今天第3题的暴力都没打…第2题写着写着就内心崩溃了…看到第3题(woc,第3题数论题,数论是什么…)。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: