正睿20NOIp前冲刺day2
估分:100+20+20+10=150
实际:0+20+0+0=20
T1:
没注意只有两条入边,所以花了好多时间搞这玩意,最后还写炸了
因为每个点只有两条入边和出边,所以把新建图,把出边相连,入边相连,答案就是2环数
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #include<algorithm> 5 using namespace std; 6 7 const int N = 200010; 8 const int mod = 998244353; 9 10 int n; 11 int dout[N], din[N], fa[N], siz[N]; 12 13 int find1(int x) 14 { 15 return fa[x] == x ? x : fa[x] = find1(fa[x]); 16 } 17 18 void add(int a, int b) 19 { 20 a = find1(a), b = find1(b); 21 if (siz[a] < siz[b]) swap(a, b); 22 fa[b] = fa[a]; 23 siz[a] += siz[b]; 24 } 25 26 int main() 27 { 28 scanf("%d", &n); 29 for (int i = 1; i <= n * 2; i++) fa[i] = i, siz[i] = 1; 30 for (int i = 1; i <= n * 2; i++) 31 { 32 int a, b; 33 scanf("%d%d", &a, &b); 34 if (!dout[a]) dout[a] = i; 35 else add(i, dout[a]); 36 if (!din[b]) din[b] = i; 37 else add(i, din[b]); 38 } 39 int ans = 1; 40 for (int i = 1; i <= n * 2; i++) 41 { 42 if (find1(i) == i) ans = ans * 2 % mod; 43 } 44 printf("%d", ans); 45 }
T2:
又是一个神仙DP,只写了20分全排列暴力
因为p是一个排列,所以若有ai=i则一定无解。若有ai>i,我们就要把ai向右移动到ai处,当ai<i时向左移,所以我们就可以弄出若干个类似于p[i]<p[i-1]或p[i]>p[i-1]的限制,然后就能DP。
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #include<algorithm> 5 using namespace std; 6 7 const int N = 5010; 8 const int mod = 1e9 + 7; 9 10 int n; 11 int a[N], b[N]; 12 int sum1[N], sum2[N]; 13 int f[N][N]; 14 15 int main() 16 { 17 scanf("%d", &n); 18 for (int i = 1; i <= n; i++) scanf("%d", &a[i]), a[i]++; 19 for (int i = 1; i <= n; i++) 20 { 21 if (a[i] == i) 22 { 23 puts("0"); 24 return 0; 25 } 26 } 27 28 for (int i = 1; i <= n; i++) 29 { 30 int l = i, r = a[i], k = 1; 31 if (l > r) swap(l, r), k = -1; 32 for (int j = l; j < r - 1; j++) 33 { 34 if (b[j] == -k) 35 { 36 puts("0"); 37 return 0l; 38 } 39 b[j] = k; 40 } 41 } 42 43 f[1][1] = 1; 44 for (int i = 2; i < n; i++) 45 { 46 for (int j = 1; j < i; j++) sum1[j] = (sum1[j - 1] + f[i - 1][j]) % mod; 47 for (int j = i - 1; j; j--) sum2[j] = (sum2[j + 1] + f[i - 1][j]) % mod; 48 for (int j = 1; j <= i; j++) 49 { 50 if (b[i - 1] == 1) f[i][j] = sum1[j - 1]; 51 else f[i][j] = sum2[j]; 52 } 53 } 54 55 int ans = 0; 56 for (int i = 1; i < n; i++) ans = (ans + f[n - 1][i]) % mod; 57 printf("%d", ans); 58 }
T3:
写了n=2的20分,但没开longlong
找规律,就能找出这样的规律
只需要把前两行和前两列变成0即可,如果此时还不能把整个矩阵归零那么必然无解。证明就考虑任意一个3*3的矩阵,a(1,1) − a(1,2) − a(2,1) + a(2,3) + a(3,2) − a(3,3) 的值在这些操作下永远不变。然后就能构造出来了,最多只需要4000步以下
1 #include<cstdio> 2 #include<cstring> 3 #include<vector> 4 #include<iostream> 5 #include<algorithm> 6 using namespace std; 7 8 #define pb push_back 9 10 typedef long long LL; 11 12 const int N = 5010; 13 14 int n, m; 15 LL t[N][N]; 16 17 struct Node 18 { 19 LL ex, id, v; 20 Node() {} 21 Node(LL ex, LL id, LL v) :ex(ex), id(id), v(v) {} 22 }; 23 24 vector<Node> ans; 25 26 void add(int id, int k, LL x) 27 { 28 ans.pb(Node(id, k, x)); 29 switch (id) 30 { 31 case 1: 32 { 33 for (int i = 1; i <= m; i++) t[k][i] += x; 34 break; 35 } 36 case 2: 37 { 38 for (int i = 1; i <= n; i++) t[i][k] += x; 39 break; 40 } 41 case 3: 42 { 43 for (int i = 1; i <= n; i++) 44 { 45 if (i + k <= m && i + k >= 1) t[i][i + k] += x; 46 } 47 break; 48 } 49 } 50 } 51 52 int main() 53 { 54 scanf("%d%d", &n, &m); 55 for (int i = 1; i <= n; i++) 56 { 57 for (int j = 1; j <= m; j++) 58 { 59 scanf("%lld", &t[i][j]); 60 } 61 } 62 63 for (int i = n; i >= 1; i--) 64 { 65 for (int j = 1; j <= 2; j++) 66 { 67 if (t[i][j]) j == 1 ? add(1, i, -t[i][j]) : add(3, j - i, -t[i][j]); 68 } 69 } 70 71 for (int j = 1; j <= m; j++) 72 { 73 for (int i = 2; i >= 1; i--) 74 { 75 if (t[i][j]) i == 2 ? add(2, j, -t[i][j]) : add(3, j - i, -t[i][j]); 76 } 77 } 78 79 for (int i = 1; i <= n; i++) 80 { 81 for (int j = 1; j <= m; j++) 82 { 83 if (t[i][j]) 84 { 85 puts("-1"); 86 return 0; 87 } 88 } 89 } 90 91 printf("%d\n", ans.size()); 92 for (auto i : ans) printf("%lld %lld %lld\n", i.ex, i.id, i.v); 93 }
T4:
10分的暴力DP写炸了
斜率优化DP,不会
总结:
这次考得很不好,代码全炸,可能写得慌了,读题也是个问题,就因为第一题题目没读清楚浪费了好多时间,导致后面没时间对拍检验了。以后还是一题一题的来,在四个题之间反复横跳效果极其不好。