zoukankan      html  css  js  c++  java
  • The Preliminary Contest for ICPC Asia Shanghai 2019

    传送门

    B. Light bulbs

    题意:
    起初(n)个位置状态为(0)(m)次操作,每次操作更换区间状态:(0)(1)(1)(0)
    共有(T,Tleq 1000)组数据,(nleq 10^6,mleq 1000)
    最后输出状态为(1)的个数和。

    思路:
    一开始冲了一发维护差分,最后求出前缀和,成功(TLE)
    其实只要发现有用的点只有(2m)个,之后从左到右扫一遍,类似于扫描线的思想,累加贡献就行了。

    Code
    #include <bits/stdc++.h>
    #define MP make_pair
    #define fi first
    #define se second
    using namespace std;
    typedef long long ll;
    typedef pair<int, int> pii;
    const int N = 1e3 + 5;
    
    int T;
    int n, m;
    
    struct Istream {
    	template <class T>
    	Istream &operator >>(T &x) {
    		static char ch;static bool neg;
    		for(ch=neg=0;ch<'0' || '9'<ch;neg|=ch=='-',ch=getchar());
    		for(x=0;'0'<=ch && ch<='9';(x*=10)+=ch-'0',ch=getchar());
    		x=neg?-x:x;
    		return *this;
    	}
    }fin;
    
    pii x[N << 1];
    
    int main() {
        fin >> T;
        int tot = 0;
        while(T--) {
            fin >> n >> m;
            for(int i = 1; i <= m; i++) {
                int l, r; fin >> l >> r;
                x[2 * i - 1] = MP(l, 0), x[2 * i] = MP(r + 1, 1);
            }
            sort(x + 1, x + 2 * m + 1, [&](pii A, pii B) {
                if(A.fi == B.fi) return A.se < B.se;
                return A.fi < B.fi;
            });
            int cnt = 0, ans = 0, last = 0;
            for(int i = 1; i <= 2 * m; i++) {
                if(x[i].se == 0) {
                    if(cnt & 1) {
                        ans += x[i].fi - last;
                    }
                    ++cnt;
                } else{
                    if(cnt & 1) {
                        ans += x[i].fi - last;
                    }
                    --cnt;
                }
                last = x[i].fi;
            }
            ++tot;
            cout << "Case #" << tot << ": ";
            printf("%d
    ", ans);
        }
        return 0;
    }
    

    C. Triple

    题意:
    给出三个数组(A,B,C),要求满足条件的三元组((i,j,k)),满足:

    [left{ egin{aligned} |A_i-B_j|leq C_k\ |B_j-C_k|leq A_i\ |A_i-C_k|leq B_j end{aligned} ight. ]

    限制条件:(nleq 10^5,1leq A_i,B_i,C_ileq 10^5)

    思路:

    • 数据范围较小时,可以考虑枚举一个(i)作为最大值,然后其余两个数组双指针来扫,时间复杂度(O(3*n^2))
    • 但这对于本题数据来说,显然会(T)的,考虑如何优化。
    • 注意到在多项式乘法当中,两项相乘其指数相加,那么我们可以将数组对应挂在指数上面,系数则为对应指数出现次数,那么将两个多项式乘起来最终得到一项(x^n),其前面的系数则为两数相加和为(n)的方案数。
    • 此过程(FFT)优化即可,时间复杂度(O(nlogn))

    因为本题数据比较特殊,所以就小范围暴力,大范围(FFT)优化即可。
    另外还注意一些细节,去重的问题:

    • 如果出现多个都为最大值的情况,会重复计算。
    • 小范围暴力时,可以参考我的代码,只是有些地方限制一下即可。
    • (FFT)处理时,将三个数组丢到同一个数组当中进行排序,然后扫一遍,对于第(i)个位置,假设目前其为最大值。那么我们减掉一下情况就行(以(a_i)为最大值为例):
      • (b_jgeq a_i,c_kgeq a_i)
      • (b_jgeq a_i,c_k<a_i)
      • (b_j<a_i,c_kgeq a_i)
    • 所以用个桶维护每种数出现次数即可。

    详见代码(有点复杂...)

    Code
    #include <bits/stdc++.h>
    #define MP make_pair
    #define fi first
    #define se second
    #define sz(x) (int)(x).size()
    //#define Local
    using namespace std;
    typedef long long ll;
    typedef pair<int, int> pii;
    const int N = 3e5 + 5;
    int UP = 200000;
    const double pi = acos(-1.0);
    struct Com{
        double x, y;
        Com(double xx = 0, double yy = 0) {x = xx, y = yy;}
    }a[N], b[N], c[N];
    Com operator + (const Com &a, const Com &b) {return Com(a.x + b.x, a.y + b.y);}
    Com operator - (const Com &a, const Com &b) {return Com(a.x - b.x, a.y - b.y);}
    Com operator * (const Com &a, const Com &b) {return Com(a.x * b.x - a.y * b.y, a.x * b.y + a.y * b.x);}
    int l, r[N];
    int lim = 1;
    void solve(Com *A, int type) {
        for(int i = 0; i < lim; i++) if(i < r[i]) swap(A[i], A[r[i]]);
        for(int mid = 1; mid < lim; mid <<= 1) {
            Com Wn(cos(pi / mid), type * sin(pi / mid)) ;
            for(int R = mid << 1, j = 0; j < lim; j += R) {
                Com w(1, 0);
                for(int k = 0; k < mid; k++, w = w * Wn) {
                    Com x = A[j + k], y = w * A[j + mid + k];
                    A[j + k] = x + y;
                    A[j + mid + k] = x - y;
                }
            }
        }
    }
    int n;
    void FFT(Com *a, Com *b) {
        solve(a, 1); solve(b, 1);
        for(int i = 0; i <= lim; i++) c[i] = a[i] * b[i];
        solve(c, -1);
        for(int i = 0; i <= UP; i++) c[i].x = (c[i].x / lim + 0.5);
    }
    
    int arr[3][N];
    ll sum[N];
    
    ll gao(int p, int q, int r) {
        vector <int> num1(lim + 1), num2(lim + 1);
        for(int i = 1; i <= n; i++) {
            ++num1[arr[p][i]];
            ++num2[arr[q][i]];
        }
        for(int i = 0; i <= lim; i++) {
            a[i] = Com(num1[i]);
            b[i] = Com(num2[i]);
        }
        FFT(a, b);
        for(int i = UP; i >= 0; i--)
            sum[i] = sum[i + 1] + (ll)c[i].x;
        ll ans = 0;
        for(int i = 1; i <= n; i++) {
            ans += sum[arr[r][i]];
        }
        return ans;
    }
    pii tmp[N];
    int cnt[3];
    ll calc(int id){
        ll ans = 0;
        int x = (id + 1) % 3;
        int y = (x + 1) % 3;
        ans += 1ll * cnt[x] * (n - cnt[y]);
        ans += 1ll * cnt[y] * (n - cnt[x]);
        ans += 1ll * (n - cnt[x]) * (n - cnt[y]);
        return ans;
    }
    ll work2() {
        int tot = 0;
        for(int i = 0; i < 3; i++) {
            for(int j = 1; j <= n; j++) {
                cin >> arr[i][j];
                tmp[++tot] = MP(arr[i][j], i);
            }
        }
        ll ans = 0;
        ans += gao(0, 1, 2);
        ans += gao(1, 2, 0);
        ans += gao(0, 2, 1);
        cnt[0] = cnt[1] = cnt[2] = 0;
        sort(tmp + 1, tmp + tot + 1);
        for(int i = 1; i <= tot; i++) {
            ans -= calc(tmp[i].se);
            ++cnt[tmp[i].se];
        }
        return ans;
    }
    namespace bf {
        int getans(int *a, int *b, int *c, int d, int e) {
            int ans = 0;
            for(int i = 1; i <= n; i++) {
                int k = upper_bound(c + 1, c + n + 1, a[i] - e) - c - 1;
                int p = k;
                for(int j = 1; j <= n && b[j] <= a[i] - d; j++) {
                    while(k && b[j] + c[k] >= a[i]) --k;
                    ans += p - k;
                }
            }
            return ans;
        }
        int A[N], B[N], C[N];
        int work() {
            for(int i = 1; i <= n; i++) cin >> A[i];
            for(int i = 1; i <= n; i++) cin >> B[i];
            for(int i = 1; i <= n; i++) cin >> C[i];
            sort(A + 1, A + n + 1);
            sort(B + 1, B + n + 1);
            sort(C + 1, C + n + 1);
            int ans = 0;
            ans += getans(A, B, C, 0, 0);
            ans += getans(B, A, C, 1, 0);
            ans += getans(C, A, B, 1, 1);
            return ans;
        }
    }
    
    int Case;
    void run() {
        ++Case;
        cin >> n;
        ll ans;
        if(n <= 2000) {
            ans = bf :: work();
        } else {
            ans = work2();
        }
        cout << "Case #" << Case << ": ";
        cout << ans << '
    ';
    }
    
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(0); cout.tie(0);
        cout << fixed << setprecision(20);
    #ifdef Local
        freopen("../input.in", "r", stdin);
        freopen("../output.out", "w", stdout);
    #endif
        while(lim <= UP) lim <<= 1, l++;
        for(int i = 0; i < lim; i++) {
            r[i] = (r[i >> 1] >> 1) | ((i & 1) << (l - 1));
        }
        int t; cin >> t;
        while(t--) run();
        return 0;
    }
    
    

    D. Counting Sequences I

    题意:
    定义一个好的序列(a_1,a_2,cdots,a_n)满足:(ngeq 2)(a_1+a_2+cdots+a_n=a_1cdot a_2cdot cdots a_n)
    给定一个长度(n),问有多少长度为(n)的序列满足是好的序列。

    思路:

    • 注意到(1)在等式里面比较特殊,因为不断增加(1)的个数,那么等式左边会不断增加,而等式右边则会不变。
    • 因为题目给出的(n)较小,所以直接爆搜即可,但要注意剪枝:两边的差已经不能用(1)弥补时就及时中止。

    代码中爆搜的时候并没有考虑(1),而是默认用(1)来补差值。同时还要维护一些分母上的东西用来统计答案。
    详见代码吧:

    Code
    #include<bits/stdc++.h>
    typedef long long ll;
    typedef unsigned long long ull;
    typedef double db;
    const int MAXN = 3e3+5,MAXM = 2e6+5,MOD = 1e9+7,INF = 0x3f3f3f3f;
    const ll INFL = 0x3f3f3f3f3f3f3f3f;
    const db eps = 1e-9;
    #define lson o<<1,l,m
    #define rson o<<1|1,m+1,r
    #define mid l + ((r-l)>>1)
    #define rep(i,a,b) for(register int i=(a);i<=(b);i++)
    #define pii pair<int,int>
    #define vii vector<pii>
    #define vi vector<int>
    using namespace std;
    
    int t,n,ans,a;
    ll fact[MAXN],inv[MAXN];
    ll qpow(ll a,ll b){
        ll ans=1;
        for(;b;b>>=1,a=a*a%MOD)if(b&1)ans=ans*a%MOD;
        return ans;
    }
    inline void add(int &x,int y){
        x+=y;
        if(x>=MOD)x-=MOD;
    }
    void dfs(int p,ll A,ll B,int pre,ll sum,int cnt){
        if(p>n){
            if(A==B){
                sum=sum*inv[cnt]%MOD;
                ans += fact[p-1]*sum%MOD;
                ans%=MOD;
            }
            return ;
        }else if(p>1 && A-B == n-p+1){
            sum=sum*inv[cnt]%MOD*inv[n-p+1]%MOD;
            ans += fact[n]*sum%MOD;
            ans%=MOD;
        }
        for(int i=pre;i<=3000;i++){
            if((ll)A*i - (B+i)>n)break;
            if(i==pre) dfs(p+1,A*i,B+i,i,sum,cnt+1);
            else dfs(p+1,A*i,B+i,i,sum*inv[cnt]%MOD,1);
        }
    }
    int main(){
        ios::sync_with_stdio(false);
    
        fact[0]=1;
        for(int i=1;i<=3000;i++)fact[i]=fact[i-1]*i%MOD;
        inv[3000] = qpow(fact[3000],MOD-2);
        for(int i=3000-1;i>=0;i--)inv[i]=inv[i+1]*(i+1)%MOD;
    
        cin>>t;
        while(t--){
            cin>>n;
            ans=0;
            dfs(1,1,0,2,1,0);
            cout<<ans<<'
    ';
        }
        return 0;
    }
    

    E. Counting Sequences II

    题意:
    给出限制条件(n,m),构造序列(a_i),满足(1leq a_ileq m)对于(1leq ileq n),并满足偶数的个数为偶数。

    思路:

    • 对于一个偶数,其出现次数可能为(0,2,4,cdots);对于一个奇数,其出现次数可能为(0,1,2,3,cdots)
    • 因为所求情况数为排列,考虑指数生成函数,我们将每个数可能出现的次数挂到指数上,系数则为方案数。
    • 易知对于一个偶数,其生成函数为:(1+frac{x^2}{2!}+frac{x^4}{4!}+cdots);对于一个奇数,其生成函数为(1+x+frac{x^2}{2!}+frac{x^3}{3!}+frac{x^4}{4!}+cdots)
    • 因为小于等于(m)的数有(lfloorfrac{m}{2} floor),记其为(t),那么答案就为((frac{e^x+e^{-x}}{2})^te^{x(m-t)})的第(n)项的系数乘以(n!)
    • 最后随便化一下,把((frac{e^x+e^{-x}}{2})^t)展开,就得到:(frac{1}{2^t}sum_{i=0}^t {tchoose i}e^{(m-2i)x}),其第(n)项系数为(frac{1}{2^t}sum_{i=0}^t (m-2i)^n)

    预处理一下阶乘和逆元就行了。
    总结一下,关键还是第一步构造生成函数的思想,对每个数独立考虑所有的情况,其余的都很好推。
    生成函数nb!多项式nb!

    Code
    #include <bits/stdc++.h>
    #define MP make_pair
    #define fi first
    #define se second
    #define sz(x) (int)(x).size()
    //#define Local
    using namespace std;
    typedef long long ll;
    typedef pair<int, int> pii;
    const int N = 2e5 + 5, MOD = 1e9 + 7;
    ll qpow(ll a, ll b) {
        ll ans = 1;
        while(b) {
            if(b & 1) ans = ans * a % MOD;
            a = a * a % MOD;
            b >>= 1;
        }
        return ans;
    }
    ll n, m;
    int fac[N], inv[N];
    
    int C(ll n, ll m) {
        return 1ll * fac[n] * inv[m] % MOD * inv[n - m] % MOD;
    }
    
    void init() {
        fac[0] = 1;
        for(int i = 1; i < N; i++) fac[i] = 1ll * fac[i - 1] * i % MOD;
        inv[N - 1] = qpow(fac[N - 1], MOD - 2);
        for(int i = N - 2; i >= 0; i--) inv[i] = 1ll * inv[i + 1] * (i + 1) % MOD;
    }
    
    void run() {
        cin >> n >> m;
        int t = m >> 1;
        ll ans = 0;
        int fm = qpow(qpow(2, t), MOD - 2);
        for(int i = 0; i <= t; i++) {
            ans = (ans + 1ll * C(t, i) * qpow((m - 2 * i), n) % MOD) % MOD;
        }
        if(ans < 0) ans += MOD;
        ans = ans * fm % MOD;
        cout << ans << '
    ';
    }
    
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(0); cout.tie(0);
        cout << fixed << setprecision(20);
    #ifdef Local
        freopen("../input.in", "r", stdin);
        freopen("../output.out", "w", stdout);
    #endif
        init();
        int t; cin >> t;
        while(t--) run();
        return 0;
    }
    
    

    F. Rhyme scheme

    题意:
    求字典序第(k)大的序列,该序列满足每一位不会大于前面的最大位加一。

    思路:

    • 因为是字典序第(k)小,所以考虑逐位确定,但是每当我们考虑某一位时我们需要知道后缀个数信息。
    • 考虑(dp)(递推)(我也不知道是什么)来计算这个信息。
    • 显然每一位与之相关的有当前位数和前面最大的位数,同时由于与后缀相关,我们考虑从后往前依次考虑不同长度的后缀。
    • (dp[i,j])表示长度为(n)的后缀,当前从后往前第(i)位,前面最大值为(j)时的方案数,那么就有转移方程:

    [dp[i][j]=dp[i+1][j]*j+dp[i+1][j+1] ]

    之后逐位确定就行。
    (思路与代码不符,详细可以参见传送门)

    Code
    #include<cstdio>
    #include<cmath>
    #include<cctype>
    #include<algorithm>
    #include<vector>
    #include<cstring>
    #include<cassert>
    using namespace std;
    #define REP(r,x,y) for(register int r=(x); r<y; r++)
    #define REPE(r,x,y) for(register int r=(x); r<=y; r++)
    #ifdef sahdsg
    #define DBG(...) printf(__VA_ARGS__)
    #else
    #define DBG(...) (void)0
    #endif
    template <class T>
    inline void read(T&x) {
    	static int si=1; static char ch; x=0;
    	do ch=getchar(); while(!isdigit(ch) && ch!='-');
    	if(ch=='-') si=-1, ch=getchar();
    	while(isdigit(ch)) {x=x*10+ch-'0'; ch=getchar();} x*=si;
    }
    template <class T, class... A> inline void read(T&t, A&...a){read(t); read(a...);}
    struct Istream {
    	template <class T>
    		Istream &operator >>(T &x) {
    			static char ch;static bool neg;
    			for(ch=neg=0;ch<'0' || '9'<ch;neg|=ch=='-',ch=getchar());
    			for(x=0;'0'<=ch && ch<='9';(x*=10)+=ch-'0',ch=getchar());
    			x=neg?-x:x;
    			return *this;
    		}
    }fin;
    
    struct Ostream {
    	template <class T>
    		Ostream &operator <<(T x) {
    			x<0 && (putchar('-'),x=-x);
    			static char stack[233];static int top;
    			for(top=0;x;stack[++top]=x%10+'0',x/=10);
    			for(top==0 && (stack[top=1]='0');top;putchar(stack[top--]));
    			return *this;
    		}
    
    	Ostream &operator <<(char ch) {
    		putchar(ch);
    		return *this;
    	}
    }fout; 
    #define MAXN 1000007
    #define LL __int128
    int n;
    LL f[30][30][30];
    LL k;
    inline void db() {
    	memset(f,0,sizeof f);
    	for(int l=26;l>=1;l--) {
    		REPE(i,1,26) f[i][1][l]=1;
    		REPE(j,2,26) {
    			REPE(i,1,26) {
    				REPE(k,1,max(i+1,l)) {
    					f[i][j][l]+=f[k][j-1][max(i+1,l)];
    				}
    			}
    		}
    	}
    }
    char ans[30];
    inline void calc(int x, int d, LL sum) {
    	if(d==0) return;
    	REPE(i,1,x+1) {
    		sum+=f[max(i,x)][d][x];
    		if(sum>=k) {
    			ans[d]=i+'A'-1;
    			putchar(ans[d]);
    			calc(max(i,x),d-1,sum-f[max(i,x)][d][x]);
    			return;
    		}
    	}
    }
    int main() {
    	db(); DBG("%d
    ", f[1][2][9]);
    	int kase=0;
    	int T; read(T);
    	while(0<T--) {
    		read(n); fin>>k;
    		printf("Case #%d: ", ++kase);
    		calc(1,n,0);
    		putchar('
    ');
    	}
    	return 0;
    }
    

    G. Substring

    题意:
    现定义两个字符串相等:

    • (S_1=T_1,S_n=T_n)
    • 每个字母出现次数相等。

    现在给出串(S),然后(n)个串,对于每个串,回答它与多少(S)的子串相等。串的长度之和不超过(10^5)

    思路:

    • 首先思考如何判断两个串相等,因为与顺序无关,所以我们可以换一下哈希的方法,使得哈希值只与字母以及出现次数有关。
    • 具体地说就是(hash[i]=hash[i-1]+p[s[i]-'a'])(p[k]=p^k)
    • 因为(n)可能很大,对于每个串直接回答会超时。如果只有一个询问的话,因为长度固定,所以可以考虑类似于滑动窗口那样,那么对于一种长度(O(n))就可以求解。
    • 注意到虽然有多组询问,但是询问串的长度总和不超过(10^5),也就是说长度不同的串为(sqrt{10^5})个,考虑时限比较宽松,那么直接枚举长度搞就行。

    我实现是用了两个unordered_map,最后两个相减得到答案,详细见代码吧:

    Code
    #include <bits/stdc++.h>
    #define MP make_pair
    #define fi first
    #define se second
    #define sz(x) (int)(x).size()
    //#define Local
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    typedef pair<int, int> pii;
    const int N = 1e5 + 5;
    
    unordered_map <ull, int> mp[26][26], mp2[26][26];
    
    char s[N];
    int n;
    ull p[26];
    int ans[N], tmp[N];
    
    void init() {
        p[0] = 131;
        for(int i = 1; i < 26; i++) p[i] = p[i - 1] * (ull)131;
    }
    
    ull Hash(const string &s) {
        int len = s.length();
        ull ans = 0;
        for(int i = 0; i < len; i++) {
            ans += p[s[i] - 'a'];
        }
        return ans;
    }
    
    void gao(int x) {
        int len = strlen(s + 1);
        ull val = 0;
        if(len < x) return;
        for(int i = 1; i <= x; i++) {
            val += p[s[i] - 'a'];
        }
        if(mp2[s[1] - 'a'][s[x] - 'a'].count(val)) ++mp[s[1] - 'a'][s[x] - 'a'][val];
        for(int i = x + 1; i <= len; i++) {
            val += p[s[i] - 'a'];
            val -= p[s[i - x] - 'a'];
            if(mp2[s[i - x + 1] - 'a'][s[i] - 'a'].count(val)) ++mp[s[i - x + 1] - 'a'][s[i] - 'a'][val];
        }
    }
    
    void run() {
        cin >> s + 1;
        cin >> n;
        string t;
        vector <pair<string, int> > q;
        for(int i = 0; i < 26; i++) {
            for(int j = 0; j < 26; j++) {
                mp[i][j].clear();
                mp2[i][j].clear();
            }
        }
        for(int i = 1; i <= n; i++) {
            cin >> t;
            int lent = t.length();
            int fir = t[0] - 'a', last = t[lent - 1] - 'a';
            ++mp[fir][last][Hash(t)];
            ++mp2[fir][last][Hash(t)];
            tmp[i] = lent;
            q.push_back(MP(t, i));
        }
        sort(tmp + 1, tmp + n + 1);
        tmp[n + 1] = 0;
        for(int i = 1, j = 1; i <= n; i = j) {
            while(tmp[i] == tmp[j]) ++j;
            gao(tmp[i]);
        }
        for(auto &it : q) {
            string t = it.fi;
            int id = it.se;
            int lent = t.length();
            ull val = Hash(t);
            ans[id] = mp[t[0] - 'a'][t[lent - 1] - 'a'][val] - mp2[t[0] - 'a'][t[lent - 1] - 'a'][val];
        }
        for(int i = 1; i <= n; i++) cout << ans[i] << '
    ';
    }
    
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(0); cout.tie(0);
        cout << fixed << setprecision(20);
    #ifdef Local
        freopen("../input.in", "r", stdin);
        freopen("../output.out", "w", stdout);
    #endif
        init();
        int t; cin >> t;
        while(t--) run();
        return 0;
    }
    
    

    J. Stone game

    题意:
    现在有(n)个石头,每个石头都有权值(a_i)
    现在要拿石子,直到满足下面的条件:目前手中石子的权值和大于等于剩下石子的权值和,并且拿掉任意一块石子,手中石子的权值和就小于剩下石子的权值和了。
    问这样拿石子的方案有多少种。

    思路:

    • 注意到最终去掉的石子一定为权值最小的一块,那么我们可以考虑枚举最小权值的石子,然后再来(dp)
    • 定义(dp[i][j])表示前(i)个石子,权值和为(j)的方案数,那么考虑当前石子拿或者不拿直接(dp)就行了,很简单的计数(dp)
    • 但是这样搞复杂度是(O(n^3))的,显然不能接受。
    • 发现我们直接这样搞会计算很多重复的部分,比如前面的很多东西都会重复计算,并且我们每次计算的其实是一个后缀。那么直接将(dp)改为从后往前(dp)就行了:最小权值每减少一次,(dp)就会多计算一个,这样复杂度就为(O(n^2))了。

    注意最后要特判一下最后一个位置。

    Code
    #include <bits/stdc++.h>
    #define MP make_pair
    #define fi first
    #define se second
    using namespace std;
    typedef long long ll;
    typedef pair<int, int> pii;
    const int N = 305, MOD = 1e9 + 7, MAX = 150005;
    
    int T;
    int n, m;
    int a[N], sum[N];
    int dp[2][MAX];
    
    void add(int &x, int y) {
        x += y;
        if(x >= MOD) x -= MOD;
    }
    
    int main() {
    //    freopen("input.in", "r", stdin);
        ios::sync_with_stdio(false); cin.tie(0);
        scanf("%d", &T);
        while(T--) {
            sum[0] = 0;
            scanf("%d", &n);
            for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
            sort(a + 1, a + n + 1);
            for(int i = 1; i <= n; i++) sum[i] = sum[i - 1] + a[i];
            int ans = 0;
            for(int i = 0; i <= 1; i++) {
                for(int j = 0; j <= sum[n]; j++)
                    dp[i][j] = 0;
            }
            dp[0][0] = 1;
            for(int i = n, cur = 1; i >= 2; i--, cur ^= 1) {
                for(int k = 0; k <= sum[n]; k++) dp[cur][k] = dp[cur ^ 1][k];
                for(int k = 0; k <= sum[n] - sum[i - 1]; k++) {
                    if(k >= a[i]) add(dp[cur][k], dp[cur ^ 1][k - a[i]]);
                }
                for(int k = 0; k <= sum[n] - sum[i - 1]; k++) {
    //                cout << i << ' ' << k << ' ' << dp[cur][k] << '
    ';
                    if(2 * (k + a[i - 1]) >= sum[n] && 2 * k + a[i - 1] <= sum[n])
                        add(ans, dp[cur][k]);
                }
    //            for(int k = 0; k <= sum[n] - sum[i - 1]; k++) dp[cur ^ 1][k] = dp[cur][k];
            }
            if(2 * a[n] >= sum[n] && a[n] <= sum[n]) ++ans;
            printf("%d
    ", ans);
        }
        return 0;
    }
    

    L. Digit sum

    数位(dp)板子,或者直接暴力==

    Code
    #include<bits/stdc++.h>
    typedef long long ll;
    typedef unsigned long long ull;
    typedef double db;
    const int MAXN = 1e6+5,MAXM = 2e6+5,MOD = 998244353,INF = 0x3f3f3f3f;
    const ll INFL = 0x3f3f3f3f3f3f3f3f;
    const db eps = 1e-9;
    #define lson o<<1,l,m
    #define rson o<<1|1,m+1,r
    #define mid l + ((r-l)>>1)
    #define rep(i,a,b) for(register int i=(a);i<=(b);i++)
    #define pii pair<int,int>
    #define vii vector<pii>
    #define vi vector<int>
    using namespace std;
    
    int t,n,b;
    ll f[11][33][11],base[11][22];
    void init(){
        for(int b=2;b<=10;b++){
            base[b][0]=1;
            for(int i=1;i<=21;i++)base[b][i]=INF;
            for(int i=1;i<22;i++){
                base[b][i] = base[b][i-1]*b;
                if(base[b][i]>=1000000)break;
            }
        }
        for(int b=2;b<=10;b++){
            for(int i=1;base[b][i-1]<=1000000;i++){
                for(int j=1;j<b;j++){
                    f[b][i][j] = f[b][i][0] + j*base[b][i-1];
                }
                for(int j=0;j<b;j++)f[b][i+1][0] += f[b][i][j];
            }
        }
    }
    int a[22];
    ll solve(int n,int b){
        int cnt=0,tmp=n;
        while(tmp){
            a[++cnt] = tmp%b;
            tmp/=b;
        }
        ll ans=0;
        for(int i=cnt;i>=1;i--){
            for(int j=0;j<a[i];j++){
                ans += f[b][i][j];
            }
            n -= a[i]*base[b][i-1];
            ans += a[i]*(n+1);
        }
        return ans;
    }
    int main(){
        ios::sync_with_stdio(false);
        init();
        cin>>t;
        for(int kase=1;kase<=t;kase++){
            cin>>n>>b;
            cout<<"Case #"<<kase<<": "<<solve(n,b)<<'
    ';
        }
        return 0;
    }
    
  • 相关阅读:
    c# WF 第11节 RichTextBox
    c# WF 第10节 textbox 控件
    c# WF 第9节 button控件
    c# WF 第8节 label控件
    c# WF 第7节 对控件的基本操作
    c# WF 第6节 MDI窗体
    c# WF 第5节 窗体的控件
    Python接口自动化之动态数据处理
    Saturn分布式调度之系统架构简介
    Jmeter系列之接口依赖
  • 原文地址:https://www.cnblogs.com/heyuhhh/p/11545443.html
Copyright © 2011-2022 走看看