NOIP模拟53
T1:
分析题目性质,根据逻辑顺序,对于当前按钮位置对应的物品之起判断是否可以购买的作用,
于是发现,对于数量大于1的物品可以直接贪心使其数量变为1
考虑剩下的问题,比较显然为有限制的最优性问题,考虑如何转移,发现最优策略下,
f[i]对应的按钮一定要比i先按才会造成更多的贡献,否则若先按i,则f[i]为空,f[f[i]]无法造成贡献
因此可以用有向图建立这种联系,考虑那么问题转化为在有向图上存在最大路径,继续分析发现
若由i向f[i]连边形成的将是内向基环树,反之为外向
考虑基环树的常见解题方法为拆环进行树形DP,这种方法的限制为要么换大小并不大,要么
拆环对答案无影响,然而对于本题而言,环的大小无限制,拆环显然对最大路径产生影响,
那么若采用拆环DP的方式复杂度为O(S * n)(其中S为环大小),
考虑最大路径是否可以采用最大生成树的方式,众所周知,最值生成树的原理为贪心,在之前
说过,本题为有限制的最大路(每个点仅被经过一次),那么很显然贪心是错的,比如可以将一条
大边替换为几条较小边
那么考虑正解,本质上也是一种带悔贪心,考虑由于需要在基环树上找最大路径且每个点只能
经过一次,那么我们将问题进行划分,可以先求出对于每个点的出边(将边翻转即可建成外向基环树)
中选出最大值累积答案,发现这种决策下,当环上每个点都选择环上边作为出边时显然不符合条件
特盘即可
需要考虑的情况有基环树,树(可能带自环)
判环的几种方式:Dfs,Topsort,Tarjan
代码如下:
1 #include <bits/stdc++.h> 2 using namespace std; 3 #define I int 4 #define C char 5 #define B bool 6 #define V void 7 #define LL long long 8 #define P pair<I,I> 9 #define MP make_pair 10 #define fir first 11 #define sec second 12 const I N = 1e5 + 3; 13 I n,cnt,f[N],a[N],c[N],d[N],MA; 14 I tot,head[N],to[N],nxt[N],wgt[N],Outit[N],Init[N]; 15 B jud,vis[N],top[N]; 16 P item[N]; 17 LL ans; 18 inline I read() { 19 I x(0),y(1); C z(getchar()); 20 while (!isdigit(z)) { if (z == '-') y = -1; z = getchar(); } 21 while ( isdigit(z)) x = x * 10 + (z ^ 48), z = getchar(); 22 return x * y; 23 } 24 inline I max (I a,I b) { return a > b ? a : b; } 25 inline V found (I x,I y,I z) { 26 to[++tot] = y, nxt[tot] = head[x], wgt[tot] = z, head[x] = tot; 27 } 28 V topsort () { 29 queue <I> q; 30 for (I i(1);i <= n; ++ i) if (!Outit[i]) 31 q.push (i); 32 while (!q.empty ()) { //找环 33 I x(q.front ()); q.pop (); top[x] = 1; 34 if (d[f[x]] - c[x] <= 0) continue; 35 if (-- Outit[f[x]] == 0) q.push (f[x]); 36 } 37 } 38 V Line (I x) { 39 I tmp (0); vis[x] = 1; 40 for (I i(head[x]),y(to[i]); i ;i = nxt[i],y = to[i]) 41 Line (y), tmp = max (tmp,wgt[i]); 42 ans += tmp; 43 } 44 V Ring (I x) { 45 I pos(0),tmp(0),val(0); vis[x] = 1; 46 for (I i(head[x]),y(to[i]); i ;i = nxt[i],y = to[i]) { 47 if (top[y]) tmp = max (tmp,wgt[i]), Line (y); else val = wgt[i]; if (!vis[y]) pos = y; 48 } 49 MA = max (MA,tmp - val); ans += tmp >= val ? (jud = 1, tmp) : val; if (pos) Ring (pos); 50 } 51 signed main () { 52 n = read(); 53 for (I i(1);i <= n; ++ i) 54 f[i] = read(), c[i] = read(), d[i] = read(), a[i] = read(); 55 for (I i(1);i <= n; ++ i) if (d[f[i]] - c[i] > 0) 56 item[++cnt].fir = c[i] - d[f[i]], item[cnt].sec = f[i]; 57 sort (item + 1,item + cnt + 1); //稳 58 for (I i(1);i <= cnt; ++ i) 59 ans += 1ll * (a[item[i].sec] - 1) * -item[i].fir, a[item[i].sec] = 1; 60 for (I i(1);i <= n; ++ i) if (d[f[i]] - c[i] > 0) { 61 Outit[f[i]] ++ , Init[i] ++ ; 62 found (f[i],i,d[f[i]] - c[i]); 63 } 64 topsort (); 65 for (I i(1);i <= n; ++ i) if (!vis[i]) { 66 if (!Init[i]) Line (i); 67 if (!top[i]) { 68 MA = INT_MIN, jud = 0, Ring (i); 69 if (!jud && f[i] != i) ans += MA; //判环 70 } 71 } 72 printf ("%lld\n",ans); 73 }
T2:
首先YY的做法:
考虑比较显然的思路为删串判断最终能否清空,然而是假的(并不完全假),考虑假的原因在于
可能存在一种字符串前后两部分相同,那么直接删可能会删除由前串+前串组成的串,那么回事得
两个后串无法进行匹配,那么考虑换一种删串方式,从中间(本质上来说是非首尾)删除,考虑
正确性,从中间删除使得该串只可能为前串后串进行搭配组成,即使存在非同一串的前后进行搭配
那么两串的两外一部分显然仍然可以搭配。考虑时间复杂度,若严格从中间进行删除,可以近似看作二分
(一般情况下为分治),那么考虑最多进行logn层,每层复杂度为n,考虑枚举长度+匹配串为n^2,
那么综合复杂度为O(Tn^3logn),由于本题分治复杂度并不稳定,且复杂度较满,需要卡常+剪枝
(如果具备优秀的分治技巧并不需要卡)
考虑正解做法:
仍然枚举长度+匹配串,只是将判断方法转化DP判断,设f[i][j]代表i~j是否合法,区间DP,转移时
判断i~j中除匹配串外剩余部分是否能构成匹配串的前缀即可O(n^2)且严重跑不满
考虑卡常+剪枝+记忆化,快到起飞
代码如下(朴素版,基本无剪枝):
1 #include <bits/stdc++.h> 2 using namespace std; 3 #define I int 4 #define C char 5 #define B bool 6 #define V void 7 I T,len,tot,gcd,buc[26]; 8 C s[205],ans[205]; 9 B dp[205][205]; 10 inline I read() { 11 I x(0),y(1); C z(getchar()); 12 while (!isdigit(z)) { if (z == '-') y = -1; z = getchar(); } 13 while ( isdigit(z)) x = x * 10 + (z ^ 48), z = getchar(); 14 return x * y; 15 } 16 I GCD (I a,I b) { return !b ? a : GCD (b,a % b); } 17 inline V Init () { 18 scanf ("%s",s + 1); len = strlen (s + 1); 19 tot = gcd = 0, memset (buc,0,sizeof buc); 20 } 21 inline B Check (I x) { 22 for (I i(1);i <= len; ++ i) 23 for (I j(1);j <= len - i + 1; ++ j) { 24 if (dp[j][j + i - 1] |= dp[j][j + i - 2] & (s[j + i - 1] == ans[(i - 1) % x + 1])) continue; 25 for (I k(1);k * x <= i; ++ k) 26 if (dp[j][j + i - 1] |= dp[j][j + i - 1 - k * x] & dp[j + i - k * x][j + i - 1]) break; 27 } 28 return dp[1][len]; 29 } 30 inline B Figure (I x) { 31 for (I i(1);i <= len - x + 1; ++ i) { 32 memcpy (ans + 1,s + i,x); memset (dp,0,sizeof dp); 33 dp[i][i + x - 1] = 1; for (I j(1);j <= len - x + 1; ++ j) dp[j][j] = (s[j] == s[i]); 34 if (Check (x)) { ans[x + 1] = 0; printf ("%s\n",ans + 1); return true; } 35 } 36 return false; 37 } 38 signed main () { 39 T = read(); 40 while (T -- ) { Init (); 41 for (I i(1);i <= len; ++ i) buc[s[i] - 'a'] ++ ; 42 for (I i(0);i < 26; ++ i) if (buc[i]) gcd = GCD (buc[i],gcd); 43 for (I i(0);i < 26; ++ i) if (buc[i]) tot += (buc[i] /= gcd); 44 for (I i(1);i <= len; ++ i) 45 if (i % tot == 0 && len % i == 0 && Figure (i)) break; 46 } 47 }