zoukankan      html  css  js  c++  java
  • qbxt 10 月模拟赛 Day3

    A

    (ln),每次只需要判断 (sum_{i=1}^k ln(i) geq sum_{i=k+1}^n ln(i)) 即可。

    #include <bits/stdc++.h>
    
    #define fi first
    #define se second
    #define db double
    #define U unsigned
    #define P std::pair<int,int>
    #define LL long long
    #define pb push_back
    #define MP std::make_pair
    #define all(x) x.begin(),x.end()
    #define CLR(i,a) memset(i,a,sizeof(i))
    #define FOR(i,a,b) for(int i = a;i <= b;++i)
    #define ROF(i,a,b) for(int i = a;i >= b;--i)
    #define DEBUG(x) std::cerr << #x << '=' << x << std::endl
    
    const int MAXN = 5e5 + 5;
    int a[MAXN],n,b[MAXN];
    
    int main(){
        scanf("%d",&n);
        long double a=0,b=0;
        FOR(i,1,n) a += log(i);
        ROF(i,n,1){
            b += log(i);
            a -= log(i);
            if(a < b){
                printf("%d
    ",i);
                break;
            }
        }
        return 0;
    }
    

    B

    难点在于读入方式。。

    我的模拟写法大概是首先按照分号分组,然后每一组按照冒号分组,就可以知道名字了,然后在中括号内根据逗号分组,就可以知道依赖关系了。

    然后根据类似拓扑排序的方法,每次维护正在做的工作,每次取出所有同时完成的工作一起扩展,如果有工作所有限制满足的话就按照题目顺序从当前时间开始做。

    写+调了2h。。还是自己太菜了,直接导致T3没做出

    #include <bits/stdc++.h>
    
    #define fi first
    #define se second
    #define db double
    #define U unsigned
    #define P std::pair<int,int>
    #define LL long long
    #define pb push_back
    #define MP std::make_pair
    #define all(x) x.begin(),x.end()
    #define CLR(i,a) memset(i,a,sizeof(i))
    #define FOR(i,a,b) for(int i = a;i <= b;++i)
    #define ROF(i,a,b) for(int i = a;i >= b;--i)
    #define DEBUG(x) std::cerr << #x << '=' << x << std::endl
    
    const int MAXN = 1e6 + 5;
    int len;
    std::map<std::string,int> S;
    std::string str;
    int n;
    int a[MAXN];
    std::vector<int> G[MAXN];
    int rk[MAXN],deg[MAXN],ts[MAXN];
    
    struct Node{
        int ts,rk,id;
        Node(int ts=0,int rk=0,int id=0) : ts(ts),rk(rk),id(id) {}
    
        inline bool operator < (const Node &t) const {
            return MP(ts,rk) < MP(t.ts,t.rk);
        }
    };
    
    bool vis[MAXN];
    int sa[MAXN];
    
    int main(){
      //  freopen("B.in","r",stdin);
        std::ios::sync_with_stdio(false);
        std::cin >> str;
        int tt = 0;
        for(int l = 0,r = 0;str[l] != '/';l = r+1){
            r = l;
            while(str[r] != ';' && str[r] != '/') ++r;
            int p = l;
            while(str[p] != ':') ++p;
            std::string name = str.substr(l,p-l);
            S[name] = ++n;
            while(str[r] != ':') --r;
            ++r;
            while(str[r] != ';' && str[r] != '/') a[n] = a[n]*10+str[r]-'0',++r;
            if(str[r] == '/') break;
        }
        for(int i=1,l = 0,r = 0;str[l] != '/';l = r+1,++i){
            r = l;
            while(str[r] != ';' && str[r] != '/') ++r;
            int p = l;
            while(str[p] != '[') ++p;
            int q = l;
            while(str[q] != ']') ++q;
            for(int ll = p+1,rr = p+1;ll < q;ll = rr+1){
                rr = ll;
                while(str[rr] != ']' && str[rr] != ',') ++rr;
                int to = S[str.substr(ll,rr-ll)];
    //            if(i == 89) DEBUG(to),DEBUG(str.substr(ll,rr-ll));
                G[to].pb(i);++deg[i];
                if(str[rr] == ']') break;
            }
            if(str[r] == '/') break;
        }
        int p = 0,lim = 0;
        while(str[p] != '/') ++p;++p;
        while(p < str.length()) lim = lim*10+str[p]-'0',++p;
        p = 1;
        for(auto x:S) rk[x.se] = p++;
        std::set<Node> tba;
        std::set<P> inw;
        FOR(i,1,n) sa[rk[i]] = i;
        FOR(i,1,n){
            if(!deg[sa[i]]){
                vis[sa[i]] = 1;
                if(inw.size() < lim) inw.insert(MP(a[sa[i]],sa[i]));
                else tba.insert(Node(0,rk[sa[i]],sa[i]));
            }
        }
        int ans = 0;
    /*    std::vector<P> sfsf;
        FOR(v,1,n){
            for(auto x:G[v]){
                sfsf.pb(MP(rk[v],rk[x]));
            }
        }*/
        while(!inw.empty()){
            int las = -1;
            while(!inw.empty() && (las == -1 || las == inw.begin()->fi)){
                P t = *inw.begin();inw.erase(inw.begin());
                int v = t.se,w = t.fi;ans = std::max(ans,w);
                for(auto x:G[v]){
                    --deg[x];
                    if(!deg[x]){
                        tba.insert(Node(w,rk[x],x));
                        vis[x] = 1;
                    }
                }
                las = w;
            }
            while(!tba.empty() && inw.size() < lim){
                inw.insert(MP(las+a[tba.begin()->id],tba.begin()->id));
                tba.erase(tba.begin());
            }
        }
        std::cout << ans << '
    ';
        return 0;
    }
    

    C

    暴力的想法是找出循环节,然后可以轻松计算答案。但是发现循环节是 ( ext{lcm}),很大。

    我们考虑合并循环节:如果我们在合并两个长度 (a,b) ,如果 ((a,b)=1),那么在到达循环节时,每种可能的位置都会被遍历。举个例子((a=2,b=3)):

    1 2 1 2 1 2
    1 2 3 1 2 3
    1 - (1,2,3)
    2 - (1,2,3)
    

    合并相同长度只需要对应相加即可。所以对于长度全是质数的部分分,我们可以对于每个长度都设一个 (f_i) 表示有 (i)(1) 的位置个数,合并直接背包转移即可。

    我们观察一下 ((a,b) = g,g eq 1) 的情况:举个例子((a=4,b=6)):

    1 2 3 4 1 2 3 4 1 2 3 4
    1 2 3 4 5 6 1 2 3 4 5 6
    1 - (1,3,5)
    2 - (2,4,6)
    3 - (1,3,5)
    4 - (2,4,6)
    

    也就是说,我们可以把所有数字按照 (mod g) 分组,每一组内都是可以直接背包合并的。

    但是如果这样直接做 (gcd) 可能会超级大,这里要用一个神奇的优化:

    我们考虑我们实际上是可以任意重复每个串若干次,不影响答案的,所以我们的思路是用重复这个操作使得所有串两两之间 (gcd) 都不是很大,就可以顺次合并。

    我们把长度分解质因数,如果质因数只包含 (2,3,5,7),我们考虑这样的数的 ( ext{lcm}) 不会超过 (2^5 * 3^3 * 5^2 * 7^2 = 1058400) ,所以可以都扩展到这个长度然后统一合并。

    否则质因数一定包含且仅包含一个 (geq 11) 的质数,发现所有这样的数合并后的长度和 (1058400)(gcd) 不会超过 (2^2 * 3=12)。所以我们可以将这些数都扩展到质数(*12)的长度,这样保证了两两类之间的 (gcd) 都是 (12),就可以背包合并了。

    时间复杂度 (O(n^2 * 12^2)),这个感觉就是类似根号平衡一样找一个比较中间的数字。。

    这种套路就是如果合并复杂度(gcd) 有关,并且可以任意延长,就要想到可以通过延长降低 (gcd) 这样确实是可行的!

    #include <bits/stdc++.h>
    
    #define fi first
    #define se second
    #define db double
    #define U unsigned
    #define P std::pair<int,int>
    #define LL long long
    #define pb push_back
    #define MP std::make_pair
    #define all(x) x.begin(),x.end()
    #define CLR(i,a) memset(i,a,sizeof(i))
    #define FOR(i,a,b) for(int i = a;i <= b;++i)
    #define ROF(i,a,b) for(int i = a;i >= b;--i)
    #define DEBUG(x) std::cerr << #x << '=' << x << std::endl
    
    const int MAXN = 100 + 5;
    const int ha = 1e9 + 7;
    const int prime[] = {0,11,13,17,19,23,29,31,37,41,43,47};
    const int lcm = 32*27*25*49;
    
    char str[MAXN][MAXN];int len[MAXN],n;
    int ans[12][MAXN],t[12][MAXN],b[12][MAXN];
    int cnt[2000000+5];
    int res[MAXN];
    
    inline void add(int &x,int y){
    	x += y;if(x >= ha) x -= ha;
    }
    
    int main(){
    	scanf("%d",&n);
    	FOR(i,1,n) scanf("%s",str[i]+1),len[i] = strlen(str[i]+1);
    	FOR(i,1,n){
    		if(lcm%len[i]) continue;
    		FOR(j,1,lcm) cnt[j] += (str[i][(j-1)%len[i]+1] == '1');
    	}
    	FOR(i,1,lcm) ans[(i-1)%12][cnt[i]]++;
    	FOR(i,1,11){
    		int p = prime[i];CLR(cnt,0);CLR(b,0);
    		FOR(j,1,n){
    			if(len[j]%p) continue;
    			FOR(k,1,p*12){
    				cnt[k] += (str[j][(k-1)%len[j]+1] == '1');
    			}
    		}
    		FOR(j,1,p*12) b[(j-1)%12][cnt[j]]++;
    		FOR(j,0,11) FOR(k,0,n) t[j][k] = ans[j][k],ans[j][k] = 0;
    		// DEBUG(b[0][0]);
    		FOR(j,0,11){
    			FOR(x,0,n){
    				FOR(y,0,n){
    					// if(j == 0 && x == 2 && y == 0){
    						// DEBUG(t[j][x]);DEBUG(b[j][y]);
    						// DEBUG(t[j][x]*b[j][y]);
    					// }
    					if(x+y <= n) add(ans[j][x+y],1ll*t[j][x]*b[j][y]%ha);
    				}
    			}
    		}
    	// DEBUG(ans[0][2]);
    	}
    	FOR(i,0,11) FOR(j,0,n) add(res[j],ans[i][j]);
    	int coe = 1;
    	FOR(i,2,50){
    		if(i == 32 || i == 27 || i == 25 || i == 49) continue;
    		bool flag = 0;
    		FOR(j,1,11) if(i == prime[j]){flag = 1;break;}
    		if(flag) continue;
    		coe = 1ll*coe*i%ha;
    	}
    	FOR(i,0,n) res[i] = 1ll*res[i]*coe%ha;
    	FOR(i,0,n) printf("%d
    ",res[i]);
    	return 0;
    }
    

    D

    这个题就很 naive 了。。暴力的想法是枚举一个 (V) 形,设坐标为 (i,j,k),贡献就是 (i * (n-k)),那么我们考虑枚举 (j),相当于两边做一个卷积,只需要求一下大于它的所有 (i)(n-k) 的和就好了。

    这题我思考了一下可以加强:题目大概是计数这样的合法的子序列,满足输入的大小关系,这个题输入的大小关系就是 > < ,设大小关系长度为 (k),这个题可以做到 (O(nk log n)):只需要先考虑处理处大小关系为 (2) 的答案(对于每个前缀,后缀都处理),就能推到长度为 (3),就能继续做。。。

    #include <bits/stdc++.h>
    
    #define fi first
    #define se second
    #define db double
    #define U unsigned
    #define P std::pair<int,int>
    #define LL long long
    #define pb push_back
    #define MP std::make_pair
    #define all(x) x.begin(),x.end()
    #define CLR(i,a) memset(i,a,sizeof(i))
    #define FOR(i,a,b) for(int i = a;i <= b;++i)
    #define ROF(i,a,b) for(int i = a;i >= b;--i)
    #define DEBUG(x) std::cerr << #x << '=' << x << std::endl
    
    const int MAXN = 1e5 + 5;
    const int ha = 1e9 + 7;
    
    inline void add(int &x,int y){
        x += y;if(x >= ha) x -= ha;
    }
    
    struct BIT{
        int tree[MAXN];
        #define lowbit(x) ((x)&(-(x)))
    
        inline void add(int pos,int x){
            while(pos){
                ::add(tree[pos],x);
                pos -= lowbit(pos);
            }
        }
    
        inline int query(int pos){
            int res = 0;
            while(pos < MAXN){
                ::add(res,tree[pos]);
                pos += lowbit(pos);
            }
            return res;
        }
    }pre,suf;
    
    int n,a[MAXN];
    
    int main(){
        scanf("%d",&n);
        FOR(i,1,n) scanf("%d",a+i);
        FOR(i,2,n) suf.add(a[i],n-i+1);
        int ans = 0;
        FOR(i,2,n-1){
            pre.add(a[i-1],i-1);
            suf.add(a[i],ha-(n-i+1));
            add(ans,1ll*pre.query(a[i]+1)*suf.query(a[i]+1)%ha);
        }
        printf("%d
    ",ans);
        return 0;
    }
    
  • 相关阅读:
    训练1-J
    训练1-K
    训练1-P
    二分查找法详解
    POJ:1094-Sorting It All Out(拓扑排序经典题型)
    POJ:2632-Crashing Robots
    POJ:1086-Parencodings
    POJ:2586-Y2K Accounting Bug
    POJ:2109-Power of Cryptography(关于double的误差)
    POJ:1328-Radar Installation
  • 原文地址:https://www.cnblogs.com/rainair/p/14305842.html
Copyright © 2011-2022 走看看