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 }
View Code

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 }
View Code

 

posted @ 2021-09-15 12:05  HZOI_LYM  阅读(41)  评论(0编辑  收藏  举报