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专项

浙公网安备 33010602011771号