zoukankan      html  css  js  c++  java
  • CodeCraft-20 (Div. 2)

    $$CodeCraft-20 (Div. 2)$$

    A.Grade Allocation

    签到,取总和和(m)中的小的那个

    //#pragma GCC optimize("O3")
    //#pragma comment(linker, "/STACK:1024000000,1024000000")
    #include<bits/stdc++.h>
    using namespace std;
    function<void(void)> ____ = [](){ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);};
    const int MAXN = 1111;
    int n, m, A[MAXN];
    void solve(){
        cin >> n >> m;
        for(int i = 1; i <= n; i++) cin >> A[i];
        int tot = accumulate(A+1,A+1+n,0);
        A[1] = min(tot,m);
        cout << A[1] << endl;
    }
    int main(){
        ____();
        int T;
        for(cin >> T; T ; T--) solve();
        return 0;
    }
    

    B.String Modification

    暴力枚举翻转长度,找出翻转之后的串是什么样的,和当前答案串进行暴力比较即可

    //#pragma GCC optimize("O3")
    //#pragma comment(linker, "/STACK:1024000000,1024000000")
    #include<bits/stdc++.h>
    using namespace std;
    function<void(void)>  = [](){ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);};
    const int MAXN = 5555;
    int n,res;
    string s;
    string check(int cur){
        string tp = s.substr(cur-1);
        tp.append(s.substr(0,cur-1));
        if((n-cur+1)&1) reverse(tp.begin()+n-cur+1,tp.end());
        return tp;
    }
    void solve(){
        res = 1;
        cin >> n >> s;
        for(int i = 2; i <= n; i++){
            if(check(i)<check(res)) res = i;
        }
        cout << check(res) << endl;
        cout << res << endl;
    }
    int main(){
        ();
        int T;
        for(cin >> T; T; T--) solve();
        return 0;
    }
    

    C.Primitive Primes

    本原多项式
    (f(x)=a_0+a_1x^1+a_2x^2+cdots+a_{n-1}x^{n-1})
    (g(x)=b_0+b_1x^1+b_2x^2+cdots+b_{m-1}x^{m-1})
    (h(x)=f(x)cdot g(x)=sum_{s=0}^{n+m-1}sum_{p=0}^{s}a_pcdot b_{s-p}=c_0+c_1x^1+c_2x^2+cdots+c_{n+m-1}x^{n+m-1})
    由于(gcd(a_0,a_1,a_2,cdots,a_{n-1})=0且gcd(b_0,b_1,b_2,cdots,b_{m-1})=0)
    现在给出素数(p),假设(a_l,b_k)分别为(f(x),g(x))系数中从(0)开始第一个不能被(p)整除的,也即:

    (a_0\%p=0,a_1\%p=0,cdots,a_{l-1}\%p=0,a_l\%p e 0)
    (b_0\%p=0,b_1\%p=0,cdots,b_{k-1}\%p=0,a_k\%p e 0)
    则可以证明:(c_{l+k}\%p e 0)

    (ecause c_{l+k}=sum_{s=0}^{l+k}a_0b_{l+k}+a_1b_{l+k-1}+cdots+a_{l-1}b_{k+1}+a_lb_k+a_{l+1}b_{k-1}+cdots+a_{l+k}b_0)

    其中除了(a_lb_k)不能被(p)整除以外,其他项都能被(p)整除

    ( herefore c_{l+k}\%p e 0)

    //#pragma GCC optimize("O3")
    //#pragma comment(linker, "/STACK:1024000000,1024000000")
    #include<bits/stdc++.h>
    using namespace std;
    function<void(void)> ____ = [](){ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);};
    const int MAXN = 1e6+7;
    int A[MAXN],B[MAXN],n,m,p;
    using LL = int_fast64_t;
    int main(){
        ____();
        cin >> n >> m >> p;
        for(int i = 0; i < n; i++) cin >> A[i];
        for(int i = 0; i < m; i++) cin >> B[i];
        int pa = 0, pb = 0;
        for(int i = 0; i < n; i++) if(A[i]%p!=0){
            pa = i;
            break;
        }
        for(int i = 0; i < m; i++) if(B[i]%p!=0){
            pb = i;
            break;
        }
        cout << pa + pb << endl;
        return 0;
    }
    

    D.Primitive Primes

    首先每个最终停止点是自己的点必然可以标上'x',对于其他最终停止点相同的点,只要从停止点开始BFS找出扩展出去的路径即可,如果所有路径都扩展完了还有不在循环路径上的点没有标方向,则INVALID

    对于循环路径上的点,只要存在两个相邻的点,就可以相互构成循环路径。如果其中一个点已经在循环路径中,只要拿另一个点的点指向在循环路径中的点即可。

    //#pragma GCC optimize("O3")
    //#pragma comment(linker, "/STACK:1024000000,1024000000")
    #include<bits/stdc++.h>
    using namespace std;
    function<void(void)> ____ = [](){ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);};
    const int MAXN = 1111;
    const int dir[4][2] = {{1,0},{-1,0},{0,1},{0,-1}};
    const char D[4] = {'U','D','L','R'};
    char mat[MAXN][MAXN];
    pair<int,int> sta[MAXN][MAXN];
    int n;
    int main(){
        scanf("%d",&n);
        queue<pair<int,int>> que;
        for(int i = 1; i <= n; i++) for(int j = 1; j <= n; j++){
            scanf("%d %d",&sta[i][j].first,&sta[i][j].second);
            if(sta[i][j].first==i&&sta[i][j].second==j){
                que.push(sta[i][j]);
                mat[i][j] = 'X';
            }
        }
        while(!que.empty()){
            auto p = que.front();
            que.pop();
            for(int d = 0; d < 4; d++){
                int nx = p.first + dir[d][0];
                int ny = p.second + dir[d][1];
                if(nx<1||ny<1||nx>n||ny>n||mat[nx][ny]||sta[nx][ny]!=sta[p.first][p.second]) continue;
                mat[nx][ny] = D[d];
                que.push(make_pair(nx,ny));
            }
        }
        for(int i = 1; i <= n; i++) for(int j = 1; j <= n; j++){
            if(mat[i][j]) continue;
            if(sta[i][j]!=make_pair(-1,-1)) return puts("INVALID"), 0;
            for(int d = 0; d < 4; d++){
                int nx = i + dir[d][0];
                int ny = j + dir[d][1];
                if(nx<1||ny<1||nx>n||ny>n||sta[nx][ny]!=make_pair(-1,-1)) continue;
                if(!mat[nx][ny]) mat[nx][ny] = D[d];
                mat[i][j] = D[d^1];
                break;
            }
            if(!mat[i][j]) return puts("INVALID"), 0;
        }
        puts("VALID");
        for(int i = 1; i <= n; i++) puts(mat[i]+1);
        return 0;
    }
    

    E.Team Building

    看到数据就应该是状压DP了,如果直接按乱序来做是不行的,所以我们先对所有人按数组(A)(作为观众的(strength))从大到小排序

    这时候对于前面的一部分人,如果不选做运动员,那么就必然选做观众了(除非观众已经足够),这样做起来就很方便了

    定义(DP[i][msk])表示当前到第(i)个人,运动员的分布为(msk)的情况下,最大的(strength)是多少,我们从上一次枚举到的人向当前转移,初始化(DP[0][0]=0,DP[0][orthers]=-1(表示没有方案)),并设(ord[i])为排完序之后的第(i)个人的编号,状态转移方程如下

    (DP[i][msk|(1<<bit)]=max(DP[i][msk|(1<<bit)],DP[i-1][msk]+s[ord[i][bit]), msk&(1<<bit) e 0 and DP[i-1][msk] e -1)

    (DP[i][msk]=egin{cases}max(DP[i][msk],DP[i-1][msk])&i-1-bit\_count(msk)=k \max(DP[i][msk],DP[i-1][msk]+A[ord[i]])&orthers end{cases})

    • (bit\_count(msk))为二进制数(msk)(1)的的数量
    //#pragma GCC optimize("O3")
    //#pragma comment(linker, "/STACK:1024000000,1024000000")
    #include<bits/stdc++.h>
    using namespace std;
    function<void(void)> ____ = [](){ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);};
    const int MAXN = 1e5+7;
    using LL =int_fast64_t;
    int n,p,k,A[MAXN];
    LL f[2][1<<7];
    vector<int> player[MAXN];
    int main(){
        ____();
        cin >> n >> p >> k;
        for(int i = 0; i < n; i++) cin >> A[i];
        for(int i = 0; i < n; i++){
            player[i].resize(p);
            for(int j = 0; j < p; j++) cin >> player[i][j];
        }
        vector<int> order(n);
        for(int i = 0; i < n; i++) order[i] = i;
        sort(order.begin(),order.end(),[](const int &x, const int &rhs){ return A[x] > A[rhs]; });
        int tag = 0;
        memset(f[tag],255,sizeof(f[tag]));
        f[tag][0] = 0;
        for(int i = 0; i < n; i++){
            int x = order[i];
            tag^=1;
            memset(f[tag],255,sizeof(f[tag]));
            for(int msk = 0; msk < (1<<p); msk++){
                if(f[tag^1][msk]==-1) continue;
                int num = __builtin_popcount(msk);
                for(int bit = 0; bit < p; bit++){
                    if(msk&(1<<bit)) continue;
                    f[tag][msk|(1<<bit)] = max(f[tag][msk|(1<<bit)],f[tag^1][msk]+player[x][bit]);
                }
                if(i-num<k) f[tag][msk] = max(f[tag][msk],f[tag^1][msk]+A[x]);
                f[tag][msk] = max(f[tag][msk],f[tag^1][msk]);
            }
        }
        cout << f[tag][(1<<p)-1] << endl;
        return 0;
    }
    

    F.Battalion Strength

    给出长为(n)的数组(A),每次选取数组(A)中的若干个元素,选取方案一共有(2^n)种且每种情况的概率相同,假设选取的数组为(a=[a_0,a_1,a_2,cdots,a_k]),则这次选取的分数为将序列(a)排序完之后的数组(b=[b_0,b_1,b_2,cdots,b_k](其中b_0le b_1le b_2le cdotsle b_k))中相邻两项相乘的总和,即(sum_{i=1}^kb_icdot b_{i-1}),而总的分数为所有选取方式的分数的平均值,给出(q)次修改,每次修改数组中某个元素之后都要输出改完之后的总分

    考虑先把数组(A)排完序,可以发现(A_i和A_j)对答案的贡献为(A_icdot A_jcdotfrac{1}{2^{j-i+1}},(j>i))(i)(j)之间的数都不选的情况下(A_icdot A_j)才能对答案做出贡献,也就是贡献的次数为(2^{n-j+i-1}),除上总次数即为(frac{1}{2^{j-i+1}})

    所以总的分数可以表示为(S=sum_{i=1}^nsum_{j=i+1}^nA_icdot A_jcdot frac{1}{2^{j-i+1}}),如果没有修改操作的话,可以用前缀和处理出来之后(O(n))做,但是带修改就不行了

    考虑线段树,先把一开始的数组中的数和后面询问中改的数放在一起进行离散化处理,这样做了之后所有数都能在线段树中有一个确定的位置,放进去之后就可以有相对的大小关系了。

    线段树的每个节点包含的区间为([L_x,R_x]),且其中包含的节点为([A_1,A_2,A_3,cdots,A_k])

    在线段树中记录四个值:

    • (V[x]=sum_{i=1}^{k}sum_{j=i+1}^{k}A_icdot A_jcdot frac{1}{2^{j-i+1}})

    • (LV[x]=sum_{i=1}^{k}A_icdot 2^{i-1})

    • (RV[x]=sum_{j=1}^{k}A_jcdotfrac1{2^j})

    • (tot[x]=k)

    其实的第一个节点的(V[x])对应的就是要求的答案,考虑(pushup)时的合并操作

    定义(ls(x))为左儿子,(rs(x))为右儿子

    • (V[x]=V[ls(x)]+V[rs(x)]+frac{LV[ls(x)]cdot RV[rs(x)]}{2^{cnt[ls(x)]}})

    • (LV[x]=LV[ls(x)]+LV[rs(x)]cdot 2^{tot[ls(x)]})

    • (RV[x]=RV[ls(x)]+RV[rs(x)]cdot frac{1}{2^{tot[ls(x)]}})

    • (tot[x]=tot[ls(x)]+tot[rs(x)])

    对于每次修改先在线段树中删除值,在加入值就完成了,每次操作完输出(V[1])即是答案

    //#pragma GCC optimize("O3")
    //#pragma comment(linker, "/STACK:1024000000,1024000000")
    #include<bits/stdc++.h>
    using namespace std;
    function<void(void)> ____ = [](){ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);};
    using LL = int_fast64_t;
    const int MAXN = 6e5+7;
    const LL MOD = 1e9+7;
    int n,m;
    LL inv2[MAXN],pow2[MAXN];
    vector<pair<int,int> > vec;
    pair<int,int> A[MAXN];
    pair<int,pair<int,int> > Q[MAXN];
    int ID(pair<int,int> &X){ return lower_bound(vec.begin(),vec.end(),X) - vec.begin() + 1; }
    LL qpow(LL a, int b){
        LL res = 1;
        while(b){
            if(b&1) res = res * a % MOD;
            b >>= 1;
            a = a * a % MOD;
        }
        return res;
    }
    LL inv(int x){ return qpow(x,MOD-2); }
    struct SegmentTree{
        LL v[MAXN<<2],lv[MAXN<<2],rv[MAXN<<2],tot[MAXN<<2];
        int l[MAXN<<2], r[MAXN<<2];
        #define ls(rt) rt << 1
        #define rs(rt) rt << 1 | 1
        void build(int L, int R, int rt){
            l[rt] = L; r[rt] = R;
            if(L+1==R) return;
            int mid = (L+R) >> 1;
            build(L,mid,ls(rt)); build(mid,R,rs(rt));
        }
        void pushup(int rt){
            v[rt] = (v[ls(rt)] + v[rs(rt)] + lv[ls(rt)] * rv[rs(rt)] % MOD * inv2[tot[ls(rt)]] % MOD) % MOD;
            lv[rt] = (lv[ls(rt)]+pow2[tot[ls(rt)]]*lv[rs(rt)])%MOD;
            rv[rt] = (rv[ls(rt)]+inv2[tot[ls(rt)]]*rv[rs(rt)])%MOD;
            tot[rt] = tot[ls(rt)] + tot[rs(rt)];
        }
        void update(int pos, int rt, int p, int tag){
            if(l[rt]+1==r[rt]){
                if(tag){
                    v[rt] = 0;
                    lv[rt] = p; rv[rt] = p * inv2[1] % MOD;
                    tot[rt] = 1;
                }
                else v[rt] = lv[rt] = rv[rt] = tot[rt] = 0;
                return;
            }
            int mid = (l[rt] + r[rt]) >> 1;
            if(pos<mid) update(pos,ls(rt),p,tag);
            else update(pos,rs(rt),p,tag);
            pushup(rt);
        }
    }ST;
    int main(){
        ____();
        cin >> n;
        inv2[0] = 1ll; inv2[1] = inv(2);
        pow2[0] = 1ll; pow2[1] = 2ll;
        for(int i = 2; i < MAXN; i++){
            inv2[i] = inv2[i-1]*inv2[1]%MOD;
            pow2[i] = pow2[i-1]*pow2[1]%MOD;
        }
        for(int i = 1; i <= n; i++){
            cin >> A[i].first;
            A[i].second = i;
            vec.emplace_back(A[i]);
        }
        cin >> m;
        for(int i = 1; i <= m; i++){
            cin >> Q[i].first >> Q[i].second.first;
            Q[i].second.second = i + n;
            vec.emplace_back(Q[i].second);
        }
        sort(vec.begin(),vec.end());
        ST.build(1,MAXN,1);
        for(int i = 1; i <= n; i++) ST.update(ID(A[i]),1,A[i].first,1);
        cout << ST.v[1] << endl;
        for(int i = 1; i <= m; i++){
            ST.update(ID(A[Q[i].first]),1,0,0);
            A[Q[i].first] = Q[i].second;
            ST.update(ID(A[Q[i].first]),1,Q[i].second.first,1);
            cout << ST.v[1] << endl;
        }
        return 0;
    }
    
  • 相关阅读:
    SQL Server:创建索引视图
    Asp.Net常用函数
    SQL Server联机丛书:删除存储过程
    音乐知识全接触
    深入透析样式表滤镜
    有一天,爸妈会变老
    今天终于买到票啦~~
    今天,回到上海啦~~(附工作生涯回顾)
    十八问:怎么才是喜欢编程
    把旧光驱改CD播放机的方法
  • 原文地址:https://www.cnblogs.com/kikokiko/p/12422631.html
Copyright © 2011-2022 走看看