sdoi2016

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;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值