6.10 考试修改+总结+颓废记

昨天晚上得到了非常不爽的消息,zcg要去给高一讲课,而我并不能去

虽然什么事情并不能都顺着我的心意来吧,但是这件事情真是让人越想越不痛快

要知道,我从去年就一直期待着给高一讲课呢

所以今天考试非常不开心,一般这个时候我会选择爆零的

但是想了想觉得爆零太难看,就看了看好像第一题可做

在教学楼里颓废了好久然后吃了点东西,用最后的时间码完了第一题

(反正二、三题我没看出来怎么做,所以暴力也不想写了

然后惊讶的是,只有第一题程序的窝rank1了QAQ

 

先放题解吧

第一题:

首先我们注意到转置的实质是某个数循环右移a位

问题就转化成了给定你一个置换,求循环节个数

但是这个置换的元素个数很多,所以我们可以对他们进行分类统计

即按循环节长度分类,问题就转化成了计数问题

那么我们枚举循环节长度,很容易发现循环节长度一定是a+b的约数

就会有LCM(a+b,k*a)个等价类,每个等价类有两种选择

即2^LCM(a+b,k*a),但是我们会发现这部分方案还包含k的约数

容斥一下就可以了

我的程序实际上有更快的方法,即考虑每部分对答案的贡献系数实际上是约数个数函数

线性筛出约数个数函数可以变快了QAQ

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;

typedef long long LL;
const int mod=1000000007;
const int maxn=2000010;
int xp[maxn],st[maxn],top=0;
int vis[maxn],pos[maxn],tot=0,tim=0;
int Ans[maxn];
int T,n,m,N,lim,ans;

LL gcd(LL a,LL b){return b==0?a:gcd(b,a%b);}
int pow_mod(int v,int p){
	int tmp=1;
	while(p){
		if(p&1)tmp=1LL*tmp*v%mod;
		v=1LL*v*v%mod;p>>=1;
	}return tmp;
}

int main(){
	freopen("matrix.in","r",stdin);
	freopen("matrix.out","w",stdout);
	scanf("%d",&T);
	xp[0]=1;
	for(int i=1;i<=2000000;++i){
		xp[i]=(xp[i-1]<<1);
		if(xp[i]>=mod)xp[i]-=mod;
	}
	while(T--){
		scanf("%d%d",&n,&m);
		if(n==0&&m==0){printf("0\n");continue;}
		N=n+m;N/=gcd(n,N);
		lim=(int)(sqrt(N));top=0;
		tim++;tot=0;ans=0;
		for(int i=1;i<=lim;++i){
			if(N%i==0){
				st[++top]=i;
				vis[i]=tim;
				if(i*i!=N){
					int cur=N/i;
					st[++top]=cur;
					vis[cur]=tim;
				}
			}
		}
		sort(st+1,st+top+1);
		for(int i=1;i<=top;++i)Ans[i]=0;
		for(int i=1;i<=top;++i){
			Ans[i]=Ans[i]+xp[gcd(n+m,1LL*st[i]*n)];
			if(Ans[i]>=mod)Ans[i]-=mod;
			for(int j=i+1;j<=top;++j){
				if(st[j]%st[i]==0){
					Ans[j]-=Ans[i];
					if(Ans[j]<0)Ans[j]+=mod;
				}
			}
			Ans[i]=1LL*Ans[i]*pow_mod(st[i],mod-2)%mod;
			ans+=Ans[i];if(ans>=mod)ans-=mod;
		}ans=1LL*xp[n]*xp[m]%mod-ans;
		if(ans<0)ans+=mod;
		printf("%d\n",ans);
	}return 0;
}

第二题:

上午完全没有读懂第二题在说啥

题解很神

首先很重要的一点是实际上答案是个定值,所以最小化什么的不用管QAQ

我们注意到我们每一次移动之后所有的数的下标和不变

而下标的平方和+2,那么我们就可以通过下标和来逐步确定最终状态

而且可以利用下标的平方和来统计答案

首先考虑单个添加,不难发现如果原位置没有,则直接添加即可

否则会使得左右区间分裂,且左区间左端点-1,右区间右端点+1

那么很容易统计出现在的下标和,用现在的下标和减去之前的下标和就是分裂点

由于p是递增的,所以用单调栈模拟就可以了

还有一种做法是考虑一次性添加一个位置的点

由上面可以推论,如果这个位置有奇数个点,则区间最终不会分裂

如果有偶数个点,则区间会分裂,且分裂点为当前点

之后我们用单调栈维护区间合并,每次合并区间的时候很容易统计出下标和S以及下标平方和

还可以统计区间中数的个数K

那么设区间的左端点为L

很容易知道(L+L+K-1)*K/2<=S<(L+L+K+1)*K/2

又因为L是正整数,可以通过这个式子直接解出L

但是注意在程序中的除法是向下取整,所以正负数要分类讨论

然后继续可以解出分裂点和右端点R

吐槽一句:感觉考试的时候完全想不到维护平方和来计算答案啊,可是想不到这个就只能QAQ了

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cstdlib>
using namespace std;

typedef long long LL;
int T,n,p,q,top;
LL ans;
LL S(int x){return 1LL*x*(x+1)*((x<<1)|1)/6;}
struct Node{
	int L,R,x;
	Node(int L=0,int R=0,int x=0):L(L),R(R),x(x){}
	LL val(){
		if(L>0)return S(R)-S(L-1)-1LL*x*x;
		else if(R>=0)return S(R)+S(abs(L))-1LL*x*x;
		else return S(abs(L))-S(abs(R)-1)-1LL*x*x;
	}
}st[2000010],now;
Node operator +(Node A,Node B){
	Node tmp;
	int N=A.R-A.L+B.R-B.L;
	LL S=1LL*(A.R+A.L)*(A.R-A.L+1)/2+1LL*(B.R+B.L)*(B.R-B.L+1)/2-A.x-B.x;
	if(2*S-1LL*N*N+N>0)tmp.L=(2*S-1LL*N*N+N)/(2*N);
	else tmp.L=(2*S-1LL*N*N-N)/(2*N);
	tmp.R=tmp.L+N;
	tmp.x=1LL*(tmp.L+tmp.R)*(tmp.R-tmp.L+1)/2-S;
	ans=ans+(tmp.val()-A.val()-B.val())/2;
	return tmp;
}

int main(){
	freopen("game.in","r",stdin);
	freopen("game.out","w",stdout);
	scanf("%d",&T);
	while(T--){
		scanf("%d",&n);top=0;ans=0;
		for(int i=1;i<=n;++i){
			scanf("%d%d",&p,&q);
			if(q&1)now=Node(p-q/2,p+q/2+1,p+q/2+1);
			else now=Node(p-q/2,p+q/2,p);
			ans=ans+(now.val()-1LL*p*p*q)/2;
			while(top&&st[top].R>=now.L){
				now=st[top]+now;
				top--;
			}st[++top]=now;
		}printf("%lld\n",ans);
	}return 0;
}

第三题:

第三题至今还在卡常数ing

但是没有关系,用了zcg的卡常大法水过去了

最后还是羞耻的去掉了

自己的概率DP太弱,考试的时候虽然懒得写暴力,但是想的时候也只能想出暴力

完全没有想到概率DP的做法,不过貌似想到了我就能看出可以用FFT优化了QAQ

(请叫我熟练的FFT工人,我一定是被zcg带坏了)

设f(i,j)表示i这个点还剩j时间的时候的最小费用

这个状态都是老生常谈,但是因为概率在边上非常难转移

关键是下面,设g(i,j)表示到了i这条边还剩j时间的时候的最小费用

这样的话,不难发现当j<=0的时候f(i,j)=dis(i->n)+fine

否则f(i,j)=min(g(k,j)) (i不等于n)

而对于g(k,j)的转移我们发现实际上把所以这条边用的时间的情况讨论完求sigma就可以了

时间瓶颈在于求g(k,j)

g(k,j)=val(k)+sigma(p(k,t)*f(u,j-t))

不难发现后面是个卷积形式,分治FFT优化一下即可

看完题解就直接麻麻麻,结果一直WA,后来发现有个地方要特判一下是不是等于n QAQ

感觉考场上如果真写这道题目自己药丸啊

#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
using namespace std;

const int oo=0x7fffffff/3;
const int maxn=102;
const double pi=acos(-1.0);
int n,m,T,F,u,v,N,len;
int dis[maxn][maxn];
int w[maxn];
int h[maxn],cnt=0;
double p[52][20010];
double S[52][20010];
double f[52][20010];
double g[52][20010];
struct edge{
	int to,next,w;
}G[102];
struct cpx{
	double r,i;
	cpx(double r=0,double i=0):r(r),i(i){}
}A[100010],B[100010],C[100010];
int rev[100010];
vector<int>V[maxn];
cpx operator +(const cpx &A,const cpx &B){return cpx(A.r+B.r,A.i+B.i);}
cpx operator -(const cpx &A,const cpx &B){return cpx(A.r-B.r,A.i-B.i);}
cpx operator *(const cpx &A,const cpx &B){return cpx(A.r*B.r-A.i*B.i,A.r*B.i+A.i*B.r);}

void add(int x,int y,int z){
	++cnt;G[cnt].to=y;G[cnt].next=h[x];G[cnt].w=z;h[x]=cnt;
}
void read(int &num){
	num=0;char ch=getchar();
	while(ch<'!')ch=getchar();
	while(ch>='0'&&ch<='9')num=num*10+ch-'0',ch=getchar();
}
void floyd(){
	for(int i=1;i<=n;++i)dis[i][i]=0;
	for(int k=1;k<=n;++k){
		for(int i=1;i<=n;++i){
			for(int j=1;j<=n;++j){
				dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
			}
		}
	}return;
}
void Get_S(){
	for(int i=1;i<=m;++i){
		int v=G[i].to;
		for(int j=T;j>=1;--j){
			S[i][j]=p[i][j]*(dis[v][n]+F)+S[i][j+1];
		}
	}return;
}
void FFT(cpx *A,int n,int type){
    for(int i=0;i<n;++i)C[i]=A[rev[i]];
    for(int i=0;i<n;++i)A[i]=C[i];
    for(int i=2;i<=n;i<<=1){
        cpx wn(cos(2*pi/i),sin(2*pi/i)*type);
        int mi=(i>>1);
        for(int j=0;j<n;j+=i){
            cpx w(1,0);
            for(int k=0;k<mi;++k){
                cpx x=A[k+j],y=A[k+j+mi]*w;
                A[k+j]=x+y;A[k+j+mi]=x-y;
                w=w*wn;
            }
        }
    }
    if(type==-1){for(int i=0;i<n;++i)A[i].r/=n;}
    return;
}
void Solve(int L,int R){
	if(L==R){
		for(int i=1;i<=m;++i){
			g[i][L]+=w[i];
			if(G[i].to!=n)g[i][L]+=S[i][L];
			else g[i][L]+=S[i][L+1];
		}
		for(int i=1;i<n;++i){
			f[i][L]=1e18;
			for(int j=h[i];j;j=G[j].next){
				f[i][L]=min(f[i][L],g[j][L]);
			}
		}return;
	}
	int mid=(L+R)>>1;
	Solve(L,mid);
	for(N=1,len=0;N<(R-L+1);N<<=1,len++);N<<=1,len++;
	for(int i=0;i<N;++i)rev[i]=rev[i>>1]>>1|((i&1)<<(len-1));
	for(int u=1;u<n;++u){
		for(int j=0;j<V[u].size();++j){
			int v=V[u][j];
			for(int i=0;i<N;++i)A[i]=B[i]=cpx();
			for(int i=0;i<(N>>1);++i)A[i].r=p[v][i+1];
			for(int i=L;i<=mid;++i)B[i-L].r=f[u][i];
			FFT(A,N,1);FFT(B,N,1);
			for(int i=0;i<N;++i)A[i]=A[i]*B[i];
			FFT(A,N,-1);
			for(int i=mid+1;i<=R;++i)g[v][i]+=A[i-L-1].r;
		}
	}
	Solve(mid+1,R);
}

int main(){
	freopen("girls.in","r",stdin);
	freopen("girls.out","w",stdout);
	read(n);read(m);read(T);read(F);
	for(int i=1;i<=n;++i)for(int j=1;j<=n;++j)dis[i][j]=oo;
	for(int i=1;i<=m;++i){
		read(u);read(v);read(w[i]);
		add(u,v,w[i]);
		dis[u][v]=w[i];
		V[v].push_back(cnt);
		for(int j=1;j<=T;++j){
			read(u);
			p[i][j]=u*0.00001;
		}
	}floyd();Get_S();Solve(1,T);
	printf("%.10lf\n",f[1][T]);
	return 0;
}

今天考试没什么可以说的

自己心情不太好,没有认真的去对待

不过至今还是对自己rank1的事情表示惊奇

Em 发泄一下心情就好了,以后的考试还是要好好考的QAQ

不过大致想一想,就算自己第二题和第三题写了也不过是30的暴力分

自己还是太弱了QAQ

没有想出正解的原因是第二题没有仔细模拟过程并探究性质

第三题没有想到可以对边做DP

 

一些坑:概率DP,FFT专项

posted @ 2016-06-10 19:36  _Vertical  阅读(185)  评论(1编辑  收藏  举报