3.07 test

从北京八十中那儿拿的题,学校自己考了一次,感觉画风十分鬼畜QAQ

P.S.由于本人码力太弱所以代码都是copy题解的QAQ

T1:

sol  :先考虑往有a个A的序列中插入b个B,枚举B的段数n,记得特判最左边和最右边是否插入B

   

   于是我们发现,在相同的两个字符之间,必须插入C、D,否则可以插入

   而插入C、D只有以下4种情况

     ①C......DC

     ②D......CD

     ③CD......CD

     ④DC......DC

   我们发现,①会多用一个C,②会多用一个D,这样的话枚举①的次数即可得到①、②、③+④的次数

   考虑枚举i,表示非必须插入的空隙插入了i个C,D,即共有c+d个C,D分配到i+n个空隙内,之后枚举①的个数j即可

   所以答案即为在①中插入C,在②中插入D,在③或④中插入CD或DC,剩下的将若干对CD随意插入的方案数

   但我们发现这样做的复杂度是O(n^3)的,显然会超时,如何优化呢?我们考虑最后一重对j的枚举是否必要

   我们发现,对于每个j,他的答案仅与n+i有关,只需预处理对于每个n+i,每个j对其贡献的方案数即可

   所以最终复杂度O(n^2)

#include <iostream>
#include <cstdio>
#include <map>
#include <set>
#include <queue>
#include <vector>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#define rep(i,a,b) for(int i = a, _b = b; i <= _b; i++)
#define dep(i,a,b) for(int i = a; i >= b; i--) 
#define Rep(i,a) for(int i = 0; i < a; i++)
#define pb(a) push_back(a)
#define mp(a,b) make_pair(a,b)
#define ab(x) ((x) < 0 ? -(x) : (x))
using namespace std;
typedef long long LL;
typedef map<int, int>::iterator mit;
typedef set<int>::iterator sit;
const int N = 2010, mod = 1e9 + 7;
int f[N], g[N], C[N][N];
 
int pw(int a, int b) { int w = 1; for(;b;b >>= 1, a = 1LL * a * a % mod) if (b & 1) w = 1LL * w * a % mod; return w; }
 
void init(int a, int b, int f[]) {
    if (a < b) swap(a, b);
    if (!a) { f[0] = 1; return; }
    else if (!b) { f[a] = 1; return; }
    Rep(i,4) {
        int c = (i & 1) + (i >> 1);
        rep(k1,0,min(b - c, a - 1)) {
            int k = (a - 1 - k1) + (b - c - k1);
            int F = 1LL * C[a - 1][k1] * C[b - 1][k1 + c - 1] % mod;
            rep(t,k + 1,a + b) 
                f[t] = (f[t] + 1LL * C[a + b - 1 - k][t - k - 1] * F) % mod;
        }
    }
}
 
int main() {
    int a, b, c, d; cin >>a>>b>>c>>d;
    rep(i,0,N - 10) {
        C[i][0] = C[i][i] = 1;
        rep(j,1,i - 1) C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % mod;
    }
    init(a, b, f); init(c, d, g);
    int ans = 0;
    rep(i,0,a + b) {
        ans = (ans + 2LL * f[i] * g[i] + 1LL * f[i] * g[i + 1]) % mod;
        if (i) ans = (ans + 1LL * f[i] * g[i - 1]) % mod;
    }
    if (ans < 0) ans += mod;
    cout <<ans<<endl;
    return 0;
}

 

T2:

  

sol  :感觉本题和Noi2008志愿者招募的一种做法有点像欸

   题意可以化为,给定以下若干个约束条件,求最大收益

   将吃饭赋值成1,睡觉为0,处理成一个序列,则约束条件如下:

     

   设0≤yi≤k-ms-me,则上述不等式可化为若干等式如下:

     

   将这些等式两两相减并添加第一个&最后一个式子,可得

     

   我们发现左边的方程和为0而且每个变量仅+-各一次

   我们把左边的方程看做一个顶点,x、y代表边,从+x的式子向-x的式子连边

   x边的容量为1,价值为me-ms,y边容量为k-ms-me,最后两个式子为源汇

   然后跑最大费用流就行啦www,记得最后要加上∑ms

//By SiriusRen
#include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define mem(x,y) memset(x,y,sizeof(x))
#define int long long
const int N=1005,M=666666,inf=0x3f3f3f3f;
int first[N],nxt[M],v[M],edge[M],cost[M],tot,ans;
int n,k,ms,me,s[N],w[N],S,T,with[N],vis[N],minn[N],d[N];
void Add(int x,int y,int C,int E){edge[tot]=E,cost[tot]=C,v[tot]=y,nxt[tot]=first[x],first[x]=tot++;}
void add(int x,int y,int C,int E){Add(x,y,C,E),Add(y,x,-C,0);}
bool tell(){
    mem(with,0),mem(vis,0),mem(minn,0x3f),mem(d,0x3f);
    queue<int>q;q.push(S);d[S]=0;
    while(!q.empty()){
        int t=q.front();q.pop();vis[t]=0;
        for(int i=first[t];~i;i=nxt[i]){
            if(d[v[i]]>d[t]+cost[i]&&edge[i]){
                d[v[i]]=d[t]+cost[i],minn[v[i]]=min(minn[t],edge[i]),with[v[i]]=i;
                if(!vis[v[i]])vis[v[i]]=1,q.push(v[i]);
            }
        }
    }return d[T]<inf;
}
int zeng(){
    for(int i=T;i!=S;i=v[with[i]^1])
        edge[with[i]]-=minn[T],edge[with[i]^1]+=minn[T];
    return minn[T]*d[T];
}
signed main(){
    mem(first,-1);
    scanf("%lld%lld%lld%lld",&n,&k,&ms,&me);
    for(int i=1;i<=n;i++)scanf("%lld",&s[i]),ans+=s[i];
    for(int i=1;i<=n;i++)scanf("%lld",&w[i]);
    S=n-k+3,T=n-k+4,add(S,n-k+1,0,k-ms),add(n-k+2,T,0,k-ms);
    for(int i=1;i<=n;i++)add(i<=k?n-k+1:i-k,i<=n-k?i:n-k+2,s[i]-w[i],1);
    for(int i=1;i<=n-k+1;i++)add(i>1?i-1:n-k+1,i<=n-k?i:n-k+2,0,k-ms-me);
    while(tell())ans-=zeng();
    printf("%lld\n",ans);
}

 

T3:

   呼叫北大中文系支援=_=,本人已死,有事烧纸QAQ

   

   

   

sol  :尝试去理解一下题意吧QAQ

   先将字符串集合处理成一棵字典树,定义一下三种操作:

   ans1(A):点A在字典树tiee[i]中,返回sum[A],sum[]表示该点到该字典树内其他所有点的距离和

   ans2(A1,B1):点A1,B1分别位于tree[i],tree[j]中,返回siz[j]*sum[A1]+siz[i]*sum[B1]+siz[i]*siz[j]

   ans3(A1,B1,B2,C1):点A1在tree[i]中,点B1,B2在tree[j]中,点C1在tree[k]中,返回sum[A1]*siz[k]+sum[C1]*siz[i]+siz[i]*siz[k]*(dis[B1,B2]+2)

   那么以最复杂的第三问为例,需要我们求出4个点A1,,B1,B2,C1,使∑ans1+ans2(A1,B1)+ans2(B2,C1)+ans3(A1,B1,B2,C1)最大or最小化

   忽略常数后,我们发现答案仅与sum[B1],sum[B2],dis[B1.B2]有关,显然最小值当sum[B1]=sum[B2]最小时取到,dis=0

   考虑当B1为根时,dis[B1,B2]=dep[B2],先暴力处理第一个点,然后考虑换根至son[B1]=x的影响:x的子树+1,其余部分-1

   那么我们就可以用线段树来维护dfs序,操作分为区间加减、min、max,即可在O(nlogn)的时间内解决该问题

   P.S.经题解提示换根可以O(n)做,然而我并不是很明白.......

 

#include <cstdio>
  
const int N = 1000000 + 5;
typedef long long ll;
  
inline int min(int a, int b) {
    return a < b ? a : b;
}
  
inline int max(int a, int b) {
    return a > b ? a : b;
}
  
inline ll min(ll a, ll b) {
    return a < b ? a : b;
}
  
inline ll max(ll a, ll b) {
    return a > b ? a : b;
}
  
struct Trie {
    int hd[N], nxt[N], ch[N], fa[N];
    int nw_trie;
      
    struct InfoOut {
        int cnt;
        ll sum;
        inline InfoOut(int _cnt = 0, ll _sum = 0): cnt(_cnt), sum(_sum) {}
    };
    InfoOut down[N], up[N];
    ll a_self;
    ll mnsum, mxsum;
      
    inline ll size() const;
    inline int trans(int u, int c);
    inline void read_init();
    void dfs_dp_down(int u);
    void dfs_dp_up(int u);
    inline void solve_q_self();
    inline void solve_mnmxsum();
};
  
inline ll Trie::size() const {
    return nw_trie;
}
  
inline Trie::InfoOut operator + (const Trie::InfoOut &a, const Trie::InfoOut &b) {
    return Trie::InfoOut(a.cnt + b.cnt, a.sum + b.sum);
}
  
inline Trie::InfoOut operator - (const Trie::InfoOut &a, const Trie::InfoOut &b) {
    return Trie::InfoOut(a.cnt - b.cnt, a.sum - b.sum);
}
  
inline Trie::InfoOut operator << (const Trie::InfoOut &a, int p) {
    return Trie::InfoOut(a.cnt, a.sum + a.cnt * p);
}
  
inline void Trie::dfs_dp_down(int u) {
    down[u] = InfoOut(1, 0);
    for(int v = hd[u]; v; v = nxt[v]) {
        dfs_dp_down(v);
        down[u] = (down[u] + (down[v] << 1));
    }
}
  
inline void Trie::dfs_dp_up(int u) {
    up[u] = up[u] + down[u];
    for(int v = hd[u]; v; v = nxt[v]) {
        up[v] = (up[u] - (down[v] << 1)) << 1;
        dfs_dp_up(v);
    }
}
  
inline void Trie::solve_q_self() {
    a_self = 0;
    for(int i = 1; i <= nw_trie; ++i) a_self += up[i].sum;
    a_self /= 2;
}
  
inline void Trie::solve_mnmxsum() {
    mnsum = 0x3f3f3f3f3f3f3f3fLL, mxsum = 0;
    for(int i = 1; i <= nw_trie; ++i) mnsum = min(mnsum, up[i].sum);
    for(int i = 1; i <= nw_trie; ++i) mxsum = max(mxsum, up[i].sum);
}
  
char buf[10000005];
int buf_b;
  
inline int getchar_buf() {
    if(buf[buf_b] == '\0') return -1;
    else return buf[buf_b++];
}
  
inline bool is_digit(int c) {
    return c >= '0' && c <= '9';
}
  
inline int Trie::trans(int u, int c) {
    for(int v = hd[u]; v; v = nxt[v]) if(ch[v] == c) return v;
    return -1;
}
  
inline void Trie::read_init() {
    nw_trie = 1;
    int c, a, u = 1;
    while(true) {
        gets(buf); buf_b = 0;
        c = getchar_buf();
        while(!is_digit(c) && c != -1) c = getchar_buf();
        if(c != -1) break;
    }
    while(c != -1) {
        a = c - '0';
        while(is_digit(c = getchar_buf())) a = a * 10 + c - '0';
        if(a == 8) {
            if(u != 1) u = fa[u];
        } else {
            int v = trans(u, a);
            if(v == -1) {
                v = ++nw_trie;
                nxt[v] = hd[u]; hd[u] = v; ch[v] = a;
                fa[v] = u;
            }
            u = v;
            a = 0;
        }
        while(!is_digit(c) && c != -1) c = getchar_buf();
    }
    dfs_dp_down(1);
    dfs_dp_up(1);
    solve_q_self();
    solve_mnmxsum();
}
  
Trie t1, t2, t3;
  
inline void solve_query_1() {
    printf("%lld %lld\n", t1.a_self, t1.a_self);
}
  
inline void solve_query_2() {
    ll base = t1.a_self + t2.a_self + t1.size() * t2.size();
    printf("%lld %lld\n", base + t1.mnsum * t2.size() + t2.mnsum * t1.size(),
    base + t1.mxsum * t2.size() + t2.mxsum * t1.size());
}
  
namespace q3dp {
    ll mndown1[N], mndown2[N], mxdown1[N], mxdown2[N], cdis;
    ll mnans, mxans;
      
    void dfs_down(int u) {
        mnans = min(mnans, mndown1[u] + mndown2[u]);
        mxans = max(mxans, mxdown1[u] + mxdown2[u]);
        for(int v = t2.hd[u]; v; v = t2.nxt[v]) {
            dfs_down(v);
            mnans = min(mnans, mndown1[u] + mndown2[v] + cdis);
            mnans = min(mnans, mndown2[u] + mndown1[v] + cdis);
            mxans = max(mxans, mxdown1[u] + mxdown2[v] + cdis);
            mxans = max(mxans, mxdown2[u] + mxdown1[v] + cdis);
            mndown1[u] = min(mndown1[u], mndown1[v] + cdis);
            mndown2[u] = min(mndown2[u], mndown2[v] + cdis);
            mxdown1[u] = max(mxdown1[u], mxdown1[v] + cdis);
            mxdown2[u] = max(mxdown2[u], mxdown2[v] + cdis);
        }
    }
      
    void work() {
        dfs_down(1);
    }
}
  
inline ll solve_query_3_mn() {
    ll ans = t1.mnsum * (t2.size() + t3.size())
        + t3.mnsum * (t1.size() + t2.size());
    ans += q3dp::mnans;
    return ans;
}
  
inline ll solve_query_3_mx() {
    ll ans = t1.mxsum * (t2.size() + t3.size())
        + t3.mxsum * (t1.size() + t2.size());
    ans += q3dp::mxans;
    return ans;
}
  
inline void solve_query_3() {
    ll base = t1.a_self + t2.a_self + t3.a_self + t1.size() * (t2.size() + t3.size())
        + (t1.size() + t2.size()) * t3.size();
    q3dp::mnans = 0x3f3f3f3f3f3f3f3fLL;
    q3dp::mxans = 0;
    q3dp::cdis = t1.size() * t3.size();
    for(int i = 1; i <= t2.nw_trie; ++i) {
        q3dp::mndown1[i] = q3dp::mxdown1[i] = t2.up[i].sum * t1.size();
        q3dp::mndown2[i] = q3dp::mxdown2[i] = t2.up[i].sum * t3.size();
    }
    q3dp::work();
    printf("%lld %lld\n", base + solve_query_3_mn(), base + solve_query_3_mx());
}
  
int main() {
    t1.read_init();
    t2.read_init();
    t3.read_init();
    solve_query_1();
    solve_query_2();
    solve_query_3();
    return 0;
}

 

posted @ 2017-03-07 21:59  Czarina  阅读(177)  评论(0)    收藏  举报