sdoi2016感觉比2017简单
。。。。
[SDOI2016]排列计数
错排+组合数
// luogu-judger-enable-o2
#include<cstdio>
#include<iostream>
using namespace std;
const int N=1e6+9,mod=1e9+7;
long long f[N],jc[N],jn[N];
int fastpow(int a,int k){
int ans=1;
while(k){
if(k&1) ans=(1ll*ans*a)%mod;
a=(1ll*a*a)%mod;
k>>=1;
}
return ans;
}
long long c(int n,int m){
return 1ll*jc[n]*jn[m]%mod*jn[n-m]%mod;
}
int n,m,T;
int main(){scanf("%d",&T);jc[1]=1;f[2]=1;f[0]=1;jc[0]=jn[0]=1;
for(int i=3;i<=N;i++) f[i]=(f[i]+1ll*(i-1)*(f[i-1]+f[i-2])%mod)%mod;
for(int i=2;i<=N;i++) jc[i]=jc[i-1]*i%mod;
for(int i=1;i<=N;i++) jn[i]=fastpow(jc[i],mod-2);
while(T--){scanf("%d%d",&n,&m);
printf("%lld\n",1ll*c(n,m)*f[n-m]%mod);
}
}
[SDOI2016]数字配对
由质数的性质得没有环。。
把他们分成两部分,网络流二分图匹配,二分一个最大流,求最大费用,能否大于0
// luogu-judger-enable-o2
#include<cstdio>
#include<cstring>
#include<queue>
#define ll long long
using namespace std;
const ll INF=(ll)1<<60,M=210;
ll s,t,n,nex[M*M*2],to[2*M*M],tot=1,head[M],pre[M*2],vis[M],dep[M],f[M][M];
ll cap[M*2*M],cos[M*M*2],dis[M],a[M],b[M],c[M];
bool ip(ll x) {
if(x==0||x==1)return false;
for(ll t=2;t*t<=x;t++) if(x%t==0)return false;
return true;
}
void add(int x,int y,ll z,ll w){nex[++tot]=head[x];to[tot]=y;cos[tot]=w;cap[tot]=z;head[x]=tot;}
struct node{
ll flow,cost;node(){flow=cost=0;}
int spfa(){queue<int>q;
memset(vis,0,sizeof vis);for(int i=s;i<=t;i++) dis[i]=-INF;
memset(pre,-1,sizeof pre);
q.push(s);vis[s]=1;dis[s]=0;
while(!q.empty()){int x=q.front();q.pop();vis[x]=0;
for(int i=head[x],tmp;i;i=nex[i])
if(cap[i]&&dis[tmp=to[i]]<dis[x]+cos[i]){
dis[tmp]=dis[x]+cos[i];pre[tmp]=i;
if(!vis[tmp]) q.push(tmp),vis[tmp]=1;
}
}
return dis[t]>-INF;
}
bool max_flow(ll tt){ll x=tt;
while(spfa()&&x){flow=x;
for(int i=t;i!=s;i=to[pre[i]^1]) flow=min(flow,cap[pre[i]]);
for(int i=t;i!=s;i=to[pre[i]^1]) cap[pre[i]]-=flow,cap[pre[i]^1]+=flow;
cost+=flow*dis[t];x-=flow;
}
return cost>=0&&x<=0;
}
}T;
void dfs(ll x){
for(int i=1;i<=n;i++)
if(!dep[i]&&f[x][i]) dep[i]=dep[x]+1,dfs(i);
}
int main(){
scanf("%d",&n);s=0,t=n+1;
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
for(int i=1;i<=n;i++) scanf("%lld",&b[i]);
for(int i=1;i<=n;i++) scanf("%lld",&c[i]);
for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)
if(i!=j) if(a[i]%a[j]==0&&ip(a[i]/a[j])) f[i][j]=1,f[j][i]=1;
for(int i=1;i<=n;i++) if(!dep[i]) dep[i]=1,dfs(i);
for(int i=1;i<=n;i++){
if(dep[i]&1) add(s,i,b[i],0),add(i,s,0,0);
else {add(i,t,b[i],0),add(t,i,0,0);continue;}
for(int j=1;j<=n;j++) if(f[i][j]&&!(dep[j]&1)) add(i,j,INF,c[i]*c[j]),add(j,i,0,-c[i]*c[j]);
}
ll l=1,r=10000000000ll;
while(l<=r){ll mid=(l+r)/2;
for(int i=2;i<=tot;i+=2) cap[i]=cap[i]+cap[i^1],cap[i^1]=0; T.cost=0;
if(T.max_flow(mid)) l=mid+1;
else r=mid-1;
}
printf("%lld",l-1);
}
[SDOI2016]征途
暴力dp[i][j]=min dp[q][j-1]+cost[q][i]
然后就可以斜率优化了,维护一个单调递增队列
但是精度有问题。。。。我也很无奈啊
#include<cstdio>
#include<iostream>
int n,m,t[120000],head=0,tail=0,j,l;long long dp[4000][4000],sum[120000],c[120000];
double k(int x,int y){
return (double)(dp[x][j-1]+sum[x]*sum[x]-dp[y][j-1]-sum[y]*sum[y])/(double)(sum[x]-sum[y]);
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%lld",&c[i]),sum[i]=c[i]+sum[i-1];
for(int i=1;i<=n;i++) dp[i][1]=(sum[i])*(sum[i]);
for(j=2;j<=m;j++){head=tail=0;
for(int i=1;i<=n;i++){long long w=sum[i];
while(k(t[head],t[head+1])<2*w&&head<tail) head++;int q=t[head];
dp[i][j]=dp[q][j-1]+(w-sum[q])*(w-sum[q]);
while(head<tail&&k(t[tail-1],t[tail])>k(t[tail-1],i)) tail--;
t[++tail]=i;
}
}
printf("%lld",(long long)dp[n][m]*m-sum[n]*sum[n]);
}
[SDOI2016]齿轮
把比例连边,暴力找环,实数判断能不能转
// luogu-judger-enable-o2
#include<cstdio>
#include<iostream>
#include<cstring>
const double esp=0.000001;const int M=40000;
using namespace std;
int T,n,m,flag,head[M],nex[M*2],to[M*2],cnt,tot;double x,y,vis[M],cos[2*M];
void add(int x,int y,double z){
nex[++tot]=head[x];to[tot]=y;
cos[tot]=z;head[x]=tot;
}
double fabs(double x){return x>0?x:-x;}
int dfs(int x){
if(!flag) return 0;
for(int i=head[x],tmp;i;i=nex[i]){
if(fabs(vis[tmp=to[i]])<esp) vis[tmp]=vis[x]*cos[i],dfs(tmp);
else flag&=(fabs(vis[tmp]-vis[x]*cos[i])<esp);
}
}
int main(){
scanf("%d",&T);
while(T--){cnt++;
scanf("%d%d",&n,&m);flag=1;tot=0;memset(head,0,sizeof head);memset(vis,0,sizeof vis);
for(int i=1,u,v;i<=m;i++) scanf("%d%d%lf%lf",&u,&v,&x,&y),add(u,v,(double)y/x),add(v,u,(double)x/y);
for(int i=1;i<=n&&flag;i++)
if(fabs(vis[i])<esp) vis[i]=1.0,dfs(i);
printf("Case #%d: ",cnt);
cout<<(flag?"Yes":"No")<<endl;
}
}