zoukankan      html  css  js  c++  java
  • 【题解】毒瘤 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));
        }
    }
    

  • 相关阅读:
    2021-06-22 总结
    【每日一题】13. 罗马数字转整数
    【每日一题】12. 整数转罗马数字
    【每日一题】1269. 停在原地的方案数
    【每日一题】1310. 子数组异或查询
    【每日一题】1734. 解码异或后的排列
    【每日一题】872. 叶子相似的树
    【每日一题】1482. 制作 m 束花所需的最少天数
    【每日一题】1723. 完成所有工作的最短时间
    【每日一题】1486. 数组异或操作
  • 原文地址:https://www.cnblogs.com/Xing-Ling/p/12707719.html
Copyright © 2011-2022 走看看