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; }

浙公网安备 33010602011771号