【题解】毒瘤 OI 刷题汇总 [SCOI2010]

【题解】毒瘤 OI 刷题汇总 [SCOI2010]

由于不清楚题目顺序,就按照 ( ext{BZOJ}) 上面的排列好了。


【Day1 T1】幸运数字

传送门:幸运数字 ( ext{[P2567]}) ( ext{[Bzoj1853]})

【题目描述】

只由 (6,8) 组成的十进制数为幸运数字,幸运数字的倍数均为近似幸运数字(包括自己),求 ([L,R]) ((1 leqslant L leqslant R leqslant 10^{10})) 中近似幸运数字的个数。

【分析】

直接大力暴搜+剪枝搞容斥就可以水过去(貌似正解就是这样)。

先搜索求出 (R) 以内所有的幸运数字,并且把存在幸运数字因子的数全部筛掉,剩下的从大到小排序(后面暴搜时剪枝更强),存到数组 ({A}) 中。

(cnt(x)=lfloor frac{R}{x} floor-lfloor frac{L-1}{x} floor)([L,R])(x) 倍数的个数),求解答案时上一个小学生容斥就可以了:(ans=sum_{iin {A}}cnt(i)-sum_{i,jin{A},i eq j}cnt(operatorname{lcm}(i,j))+sum_{i,j,kin{A},i eq j eq k}cnt(operatorname{lcm}(i,j,k))...)

时间复杂度:(O( ext{玄学}))

【Code】

#include<algorithm>
#include<cstring>
#include<cstdio>
#define LL unsigned long long
#define Re register LL
using namespace std;
const int N=1e5+3;
LL l,r,t,n,Ans,A[N];bool vis[N];
inline void in(Re &x){
    int f=0;x=0;char c=getchar();
    while(c<'0'||c>'9')f|=c=='-',c=getchar();
    while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
    x=f?-x:x;
}
inline void dfs(Re x){
    if(x>r)return;
    A[++t]=x,dfs(x*10+6),dfs(x*10+8);
}
inline LL gcd(Re a,Re b){return !b?a:gcd(b,a%b);}
inline void dfs_(Re w,Re cnt,Re lcm){
    if(lcm>r)return;
    if(w>n){
        if(cnt)Ans+=((cnt&1)?1:-1)*(r/lcm-(l-1)/lcm);
        return;
    }
    dfs_(w+1,cnt,lcm),lcm/=gcd(lcm,A[w]);
    if(lcm*A[w]<=r)dfs_(w+1,cnt+1,lcm*A[w]);
}
int main(){
//    freopen("123.txt","r",stdin);
    in(l),in(r),dfs(6),dfs(8);
    sort(A+1,A+t+1);
    for(Re i=1;i<=t;++i)if(!vis[i]){
        A[++n]=A[i];
        for(Re j=i+1;j<=t;++j)vis[j]|=(A[j]%A[i]==0);
    }
    for(Re i=1;i<=n/2;++i)swap(A[i],A[n-i+1]);
    dfs_(1,0,1),printf("%llu
",Ans);
}

【Day1 T2】游戏

传送门:游戏 ( ext{[P1640]}) ( ext{[Bzoj1854]})

【题目描述】

给出 (n) ((nleqslant 10^6)) 个装备,每个装备带有两种攻击力 (a_i,b_i) ((1 leqslant a_i,b_i leqslant 10000)),先需要先使用攻击力为 (1) 的装备,再使用攻击力为 (2) 的,再使用攻击力为 (3)(...) 依次递推,每个装备只可选择一种攻击力并且只能使用一次,求最多能攻击多少次。

【分析】

匈牙利暴力匹配判断。

发现装备和攻击力可以被划分为两个集合,且同集合内的点互不相干,即二分图。

从小到大枚举攻击力,当无法找到合法匹配点时就输出。

时间复杂度:(O(k* ext{玄学})),其中 (k) 为最大攻击次数(即答案),( ext{玄学}) 为每次匹配跑的边数(最大为 (m))。

快得飞起

注意不要用 (memset)

【Code】

#include<cstring>
#include<cstdio>
#define Re register int
const int N=1e6+3;
int n,x,y,o,ans,pan[N],tmp[N],head[N],match[N];
struct QAQ{int to,next;}a[N<<1];
inline void in(Re &x){
    int f=0;x=0;char c=getchar();
    while(c<'0'||c>'9')f|=c=='-',c=getchar();
    while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
    x=f?-x:x;
}
inline void add(Re x,Re y){a[++o].to=y,a[o].next=head[x],head[x]=o;}
inline int dfs(Re x){
    for(Re i=head[x],to;i;i=a[i].next)
        if(!pan[to=a[i].to]){
            pan[to]=1,tmp[++tmp[0]]=to;
            if(!match[to]||dfs(match[to])){
                match[to]=x;return 1;
            }
        }
    return 0;
}
int main(){
    in(n);
    for(Re i=1;i<=n;++i)in(x),in(y),add(x,i),add(y,i);
    for(Re i=1;i<=10000;++i){
        for(Re j=1;j<=tmp[0];++j)pan[tmp[j]]=0;
        tmp[0]=0;
        if(dfs(i))++ans;
        else break;
    }
    printf("%d",ans);
}

【Day1 T3】股票交易

传送门:股票交易 ( ext{[P2569]}) ( ext{[Bzoj1855]})

【题目描述】

初始时有用不完的钱(拿来烧掉造永动机?),并且每天持有的股票数不能超过 (P) ((Pleqslant 2000)) 。一共有 (n) ((n leqslant 2000)) 天,第 (i) 天的股票买入价格为 (AP_i),卖出价格为 (BP_i) ((AP_i,BP_i leqslant 1000)), 买入股票的数量限制为 (AS_i),卖出数量限制为 (BS_i) ((AS_i,BS_i leqslant P)) 。相邻两次卖卖交易之间必须间隔 (W) 天。求第 (n) 天结束后最大收益是多少。

【分析】

暴力 (dp) + 单调队列优化。

(dp[i][j]) 为第 (i) 天持有 (j) 个股票的最大收益,(I=(i-W-1,0)),分三种情况转移

  • 不买也不卖:(dp[i][j]=dp[i-1][j])

  • 买入:(dp[i][j]=max{dp[I][k]-(j-k)*AP_i)}) ((kin[j-AS_i,j]))

  • 卖出:(dp[i][j]=max{dp[I][k]+(k-j)*BP_i}) ((kin[j+1,j+BS_i,P]))

答案为 (max{dp[n][j]}) ((jin[0,P]))

把式子展开发现这个东西可以用单调队列优化,随便瞎搞搞就好了。

时间复杂度:(O(nP))

【Code】

#include<algorithm>
#include<cstring>
#include<cstdio>
#define LL long long
#define Re register int
using namespace std;
const int N=2005;
int n,h,t,P,W,ans,Q[N],AP[N],BP[N],AS[N],BS[N],dp[N][N];
inline void in(Re &x){
    int f=0;x=0;char c=getchar();
    while(c<'0'||c>'9')f|=c=='-',c=getchar();
    while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
    x=f?-x:x;
}
int main(){
//    freopen("123.txt","r",stdin);
    in(n),in(P),in(W);
    for(Re i=1;i<=n;++i)in(AP[i]),in(BP[i]),in(AS[i]),in(BS[i]);
    memset(dp,-60,sizeof(dp));
    dp[0][0]=0;
    for(Re i=1;i<=n;++i){
        Re I=max(i-W-1,0);
        for(Re j=0,h=1,t=0;j<=P;++j){
            dp[i][j]=dp[i-1][j];
            while(h<=t&&Q[h]<j-AS[i])++h;
            if(h<=t)dp[i][j]=max(dp[i][j],dp[I][Q[h]]+Q[h]*AP[i]-j*AP[i]);
            while(h<=t&&dp[I][Q[t]]+Q[t]*AP[i]<=dp[I][j]+j*AP[i])--t;
            Q[++t]=j;
//          for(Re k=max(j-AS[i],0);k<=j;++k)
//              dp[i][j]=max(dp[i][j],dp[I][k]+k*AP[i]-j*AP[i]);
        }
        for(Re j=P,h=1,t=0;j>=0;--j){
            while(h<=t&&Q[h]>j+BS[i])++h;
            if(h<=t)dp[i][j]=max(dp[i][j],dp[I][Q[h]]+Q[h]*BP[i]-j*BP[i]);
            while(h<=t&&dp[I][Q[t]]+Q[t]*BP[i]<=dp[I][j]+j*BP[i])--t;
            Q[++t]=j;
//          for(Re k=j+1;k<=j+BS[i]&&k<=P;++k)
//              dp[i][j]=max(dp[i][j],dp[I][k]+k*BP[i]-j*BP[i]);
        }
    }
    for(Re j=0;j<=P;++j)ans=max(ans,dp[n][j]);
    printf("%d
",ans);
}

【Day2 T1】字符串

传送门:字符串 ( ext{[P1641]}) ( ext{[Bzoj1856]})

【题目描述】

现要用 (n)(1)(m)(0) 组成字符串 ((1leqslant m leqslant nleqslant 10^6)),并要求对于任意前 (k) ((kin[1,n+m])) 个字符均满足 (1) 的个数不少于 (0) 的个数,求合法字符串个数,答案对 (20100403) 取膜。

【分析】

(20100403) 是个质数,直接暴力组合数计算答案即可。

类似卡特兰的思路,画一个坐标系出来瞎推推就好了,答案为 (C^{m}_{n+m}-C_{n+m}^{m-1})

时间复杂度:(O(n))

【Code】

#include<cstdio>
#define LL long long
#define Re register int
const int N=2e6+3,P=20100403;
int n,m,jc[N],inv[N],invjc[N];
inline int C(Re m,Re n){return (LL)jc[n]*invjc[n-m]%P*invjc[m]%P;}
int main(){
//    freopen("123.txt","r",stdin);
    scanf("%d%d",&n,&m),jc[0]=inv[1]=invjc[0]=invjc[1]=1;
    for(Re i=2;i<=(n+m);++i)inv[i]=(LL)inv[P%i]*(P-P/i)%P;
    for(Re i=1;i<=(n+m);++i)jc[i]=(LL)jc[i-1]*i%P,invjc[i]=(LL)invjc[i-1]*inv[i]%P;
    printf("%d
",(C(m,n+m)-C(m-1,n+m)+P)%P);
}

【Day2 T2】传送门

传送门:传送门 ( ext{[P2571]}) ( ext{[Bzoj1857]})

【题目描述】

略。

【分析】

毒瘤计算几何,貌似可以用模拟退火,不想做。

【Code】

不知道这儿能放啥,干脆买个萌吧(⊙ω⊙)

【Day2 T3】序列操作

传送门:序列操作 ( ext{[P2572]}) ( ext{[Bzoj1858]})

【题目描述】

给出一个长为 (n) ((nleqslant 10^5))(01) 序列,共有 (5) 种操作:

  • (0 l r) 把区间 ([l,r]) 内的所有数全变成 (0)

  • (1 l r) 把区间 ([l,r]) 内的所有数全变成 (1)

  • (2 l r) 把区间 ([l,r]) 内的所有数全部取反,也就是把所有的 (0) 变成 (1),把所有的 (1) 变成 (0)

  • (3 l r) 询问区间 ([l,r]) 内总共有多少个 (1)

  • (4 l r) 询问区间 ([l,r]) 内最多有多少个连续的 (1)

【分析】

珂朵莉大法好

不想码线段树,敲颗珂朵莉树水了过去。

一道板子题,没啥好说的。

时间复杂度:(O( ext{玄学}))

【Code】

#include<algorithm>
#include<cstring>
#include<cstdio>
#include<set>
#define LL long long
#define Re register int
using namespace std;
const int N=1e5+3;
int n,x,y,T,op,A[N];
inline void in(Re &x){
    int f=0;x=0;char c=getchar();
    while(c<'0'||c>'9')f|=c=='-',c=getchar();
    while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
    x=f?-x:x;
}
struct ODT{
    #define IT set<QAQ>::iterator
    struct QAQ{
        int l,r;mutable int v;
        QAQ(int L,int R=-1,int V=0):l(L),r(R),v(V){}
        inline bool operator<(const QAQ &O)const{return l<O.l;}
    };
    set<QAQ>s;
    inline void build(){
        Re w=1;
        for(Re i=2;i<=n;++i)if(A[i]!=A[i-1])s.insert(QAQ(w,i-1,A[i-1])),w=i;
        s.insert(QAQ(w,n,A[n]));
        s.insert(QAQ(n+1,n+1,0));
    }
    inline IT split(Re w){
        IT it=s.lower_bound(QAQ(w));
        if(it!=s.end()&&it->l==w)return it;
        --it;
        Re L=it->l,R=it->r,V=it->v;
        s.erase(it),s.insert(QAQ(L,w-1,V));
        return s.insert(QAQ(w,R,V)).first;
    }
    inline void assign(Re l,Re r,Re v){
        IT itr=split(r+1),itl=split(l);
        s.erase(itl,itr),s.insert(QAQ(l,r,v));
    }
    inline void fan(Re l,Re r){
        IT itr=split(r+1),itl=split(l);
        while(itl!=itr)itl->v^=1,++itl;
    }
    inline int cnt1(Re l,Re r){
        IT itr=split(r+1),itl=split(l);Re ans=0;
        while(itl!=itr)ans+=itl->v*(itl->r-itl->l+1),++itl;
        return ans;
    }
    inline int ask(Re l,Re r){
        IT itr=split(r+1),itl=split(l);Re ans=0,now=0;
        while(itl!=itr){
            if(itl->v)now+=itl->r-itl->l+1,ans=max(ans,now);
            else now=0;
            ++itl;
        }
        return ans;
    }
}T1;
int main(){
//    freopen("123.txt","r",stdin);
    in(n),in(T);
    for(Re i=1;i<=n;++i)in(A[i]);
    T1.build();
    while(T--){
        in(op),in(x),in(y),++x,++y;
        if(op<2)T1.assign(x,y,op);
        else if(op<3)T1.fan(x,y);
        else if(op<4)printf("%d
",T1.cnt1(x,y));
        else printf("%d
",T1.ask(x,y));
    }
}

原文地址:https://www.cnblogs.com/Xing-Ling/p/12707719.html