这把依然和 sigma 姐姐打这场多校。出了4题rank300多也还可以的,该罚坐的题还是罚坐,该做的也都做了。
D. Jobs (Easy Version) 题目描述
题目分析 这题就是说,有 $n$ 家公司,然后主人公有 $q$ 个朋友,每个公司有一定的职位,如果你至少符合一个公司的 $3Q(IQ,EQ,AQ)$ 要求,那么这家公司就会给你发 offer,然后问你这些朋友分别能拿多少 offer。这题防止读入量太大给你一个随机生成器去产生数据,最后还要求强制在线。
因为 3Q 的范围都比较小,然后当然我们就可以存一个最低的要求,因为我只用符合一个职位就可以了,我们最开始的想法是:开个数组:$dp[i][j][k][b]$ 表示第 $i$ 家公司 $IQ=j,EQ=k,AQ=b$ 能拿到多少职位。那么我们读入公司的职位要求然后先给当前点都 $+1$然后前缀以下,查询的时候只需要把相应的公司,3Q 输入下标查询是否为 0 即可,为 0 就说明没有 offer 嘛。
但是一算数据量发现编译器就阻拦住了,因此换一种思路,因为我们不需要知道一个人在一个公司能拿多少 offer 我们只关心拿没拿 offer,因此我们可以降维度去做。 $dp[i][j][k]$ 表示第 $i$ 家公司,$IQ=j,EQ=k$ 的情况下,能至少拿一个职位需要的最低 $AQ$。那么查询的时候我们只需要把 $i\ j\ k$ 给下标取出最低要求判断我的 $AQ$ 是否大于等于即可。然后就是抄随即发生器,在 $seed^{q-i}$ 可以写快速幂去算,最重要的一点:一定不要溢出,可以的话都开 long long 一定没事,或者为了提醒自己,时刻加上 assert(v>=0)
至少提交的时候虽然浪费 20 罚时,但是你看到运行错误你就知道是溢出了,而不是一脸懵逼不知道哪里出错了。
标程 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 #include <bits/stdc++.h> #include <random> #define maxn 200005 #define INF 0x3f3f3f3f #define MOD 998244353 int seed;using namespace std;int a[maxn],b[maxn];char s[maxn];int n,m;int dp[10 ][401 ][401 ];long long qpow (int base,int exp) { long long ans=1 ; for (long long k=base;exp;exp>>=1 ){ if (exp&1 ){ ans=(ans*k)%MOD; } k=k*k%MOD; } return ans; } void solve () { scanf ("%d%d" ,&n,&m); memset (dp,0x3f ,sizeof (dp)); for (int i=0 ;i<n;i++){ int num,IQ,EQ,AQ; scanf ("%d" ,&num); for (int j=1 ;j<=num;j++){ scanf ("%d%d%d" ,&IQ,&EQ,&AQ); dp[i][IQ][EQ]=min (dp[i][IQ][EQ],AQ); } } for (int i=0 ;i<n;i++){ for (int j=1 ;j<=400 ;j++){ for (int k=1 ;k<=400 ;k++){ dp[i][j][k]=min (dp[i][j][k],min (dp[i][j][k-1 ],dp[i][j-1 ][k])); } } } scanf ("%d" ,&seed); std::mt19937 rng (seed) ; std::uniform_int_distribution<>u (1 ,400 ); int lastans=0 ; long long qans=0 ; while (m--){ int IQ=(u (rng)^lastans)%400 +1 ; int EQ=(u (rng)^lastans)%400 +1 ; int AQ=(u (rng)^lastans)%400 +1 ; int ans=0 ; for (int i=0 ;i<n;i++){ if (dp[i][IQ][EQ]<=AQ){ ans++; } } lastans=ans; qans+=lastans*qpow (seed,m); assert (qans>=0 ); qans%=MOD; } printf ("%lld\n" ,qans); } signed main () { #ifndef ONLINE_JUDGE freopen ("1.in" ,"r" ,stdin); #endif int t=1 ; while (t--)solve (); return 0 ; }
H. Wall Builder II 题目描述
题目分析 这题的话是赛时我和 sigma 都写出来了的题目。自己还交错几发亏了点罚时,因为没看到说一定要矩形,以为可以任意形状的。就是说给你高度为1的砖头,长度为 1 的有 $n$ 块,长度为 $2$ 的有 $n-1$ 块……长度为 $n$ 的有一块。
容易发现不管怎么放面积十肯定不会变的,那么就变成了我们以前聊到的话题:面积相同的情况下周长怎么最短。那就是长宽接近的时候,因为这题面积不大,所以我们直接 $sqrt$ 往后寻找最大的较小因数作为高度,然后面积/高得到宽度。因为砖头块高为 1,所以我们每一行每一行考虑,不难发现我们先放大的,最后放小的一定能放下。根据这个贪心策略我们就可以很容易写出这题,因为要按顺序输出方案,最后再把方案总结起来然后排个序输出即可。
标程 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 #include <bits/stdc++.h> #define maxn 1005 using namespace std;int a[maxn];char s[maxn];int n,m;int dp[maxn],sum1[maxn],sum2[maxn];int num[maxn];queue<int >q[maxn]; void push (int line,int length) { if (length==0 )return ; for (int i=min (length,n);i>=1 ;i--){ if (num[i]){ q[line].push (i); num[i]--; push (line,length-i); break ; } } } struct pu { int x,y,x1,y1; bool operator <(const pu&a){ return (x1-x)<(a.x1-a.x); } }b[200005 ]; void solve () { scanf ("%d" ,&n); int ans=0 ; for (int i=1 ;i<=n;i++){ ans+=(n+1 -i)*i; num[i]=n+1 -i; } int k=(int )sqrt (ans); int width=0 ,height=0 ; for (int i=k;i>=1 ;i--){ if (ans%i==0 ){ printf ("%d %d\n" ,i,ans/i); height=i; width=ans/i; break ; } } printf ("%d\n" ,width*2 +height*2 ); for (int i=1 ;i<=height;i++){ push (i,width); } int cnt=0 ; for (int i=1 ;i<=height;i++){ int start=0 ; while (q[i].size ()){ int len=q[i].front (); q[i].pop (); b[++cnt]={start,i-1 ,start+len,i}; start+=len; } } sort (b+1 ,b+1 +cnt); for (int i=1 ;i<=cnt;i++){ printf ("%d %d %d %d\n" ,b[i].x,b[i].y,b[i].x1,b[i].y1); } } signed main () { #ifndef ONLINE_JUDGE freopen ("1.in" ,"r" ,stdin); #endif int t=1 ; cin>>t; while (t--)solve (); return 0 ; }
sigma的题目分析 利用公式 $\frac{n * (n + 1) * (n + 2)}{6} $得到面积,当面积确定时,最接近 $sqrt(s)$ 的长宽为让周长最小的方案。将较长的边作为底边,按照长度从大到小枚举所有砖头的插入方案,倒序输出即可。
也贴一下sigma姐姐的博客链接
sigma的标程 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 #include <bits/stdc++.h> #define int long long using namespace std;const int N = 1e5 + 10 ;int len[N];struct node { int x1, y1, x2, y2; }; vector<node> ans; void solve () { int n; cin >> n; int s = n * (n + 1 ) * (n + 2 ) / 6 ; ans.clear (); if (n == 1 ) { cout << "4" << endl << "0 0 1 1" << endl; return ; } int a = 0 , b = 0 ; for (b = sqrt (s); b <= s; b++) { a = s / b; if (s % b == 0 ) break ; } cout << 2 * (a + b) << endl; for (int i = 0 ; i <= b; i++) { len[i] = 0 ; } if (a > b) swap (a, b); for (int i = n; i >= 1 ; i--) { for (int j = 1 ; j <= n - i + 1 ; j++) { for (int lie = 1 ; lie <= a; lie++) { if (b - len[lie] >= i) { ans.push_back ({len[lie], lie - 1 , len[lie] + i, lie}); len[lie] += i; break ; } } } } for (int i = ans.size () - 1 ; i >= 0 ; i--) { cout << ans[i].x1 << " " << ans[i].y1 << " " << ans[i].x2 << " " << ans[i].y2 << endl; } } signed main () { ios::sync_with_stdio (0 ), cin.tie (0 ), cout.tie (0 ); int T = 1 ; cin >> T; while (T--) solve (); return 0 ; }
K. NIO’s Sword sigma姐姐做出来的!
题目描述
题目分析 初始时攻击力为 $0$,每次可以任意在攻击力之后添加一个数字,仅有在攻击力模 $n$ 与 $i$ 同余时才能够击杀第 $i$ 个敌人,问需要的最少升级次数。
暴力跑结论公式即可,注意特判 $n=1$ 时的情况。
警惕开场 $10$ 分钟暴力猜结论输入什么输出什么,喜提罚时。
标程 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 #include <bits/stdc++.h> #define int long long using namespace std;const int N = 1e6 + 10 ;void solve () { int n; cin >> n; int ans = 0 ; for (int i = 1 ; i <= n; i++) { int now = 1 ; for (int j = 0 ; j <= N; j++) { if (((i - (i - 1 ) * (now % n) % n) % n + n) % n < now) { ans += j; break ; } now = now * 10 ; } } cout << ans << endl; } signed main () { ios::sync_with_stdio (0 ), cin.tie (0 ), cout.tie (0 ); int T = 1 ; while (T--) solve (); return 0 ; }
N. Particle Arts sigma姐姐做出来的!
题目描述
题目分析 给定一个数组 $n$,数组中的任意两个元素 $a,b$ 都可能产生碰撞,并形成两个新的元素 $a&b$ 和 $a|b$ ,而原先的元素 $a,b$ 将消失。求若干次后该数组趋近的稳定方差是多少。
容易得到样例中最后得到的数组应为: $0\ 0\ 1\ 7\ 7$ ,即对于原数组中所有元素各位上的 $1$,都优先全部 分配给稳定数组中的同一位。
采用方差公式:$\frac{n\times psum-sum\times sum}{n\times n}$
其中 $n$ 为样本数,$sum=\sum_{i=1}^{n}{x_i}$ , $psum=\sum_{i=1}^{n}{x_i^2}$ 。
标程 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 #include <bits/stdc++.h> #define int long long #define endl "\n" using namespace std;const int N = 1e5 + 10 ;int a[N], b[N], cnt[N];void solve () { int n; cin >> n; int sum = 0 ; for (int i = 1 ; i <= n; i++) { cin >> a[i]; sum += a[i]; for (int j = 0 ; j < 15 ; j++) { if ((a[i] >> j) & 1 ) { cnt[j]++; } } } for (int i = n; i >= 1 ; i--) { for (int j = 0 ; j < 15 ; j++) { if (cnt[j]) { b[i] += (1 << j); cnt[j]--; } } } int ans = 0 ; int psum = 0 ; for (int i = 1 ; i <= n; i++) { psum += b[i] * b[i]; } ans = n * psum - sum * sum; int ans2 = n * n; if (ans == 0 ) { cout << "0/1" << endl; return ; } int gg = __gcd(ans, ans2); ans /= gg, ans2 /= gg; cout << ans << "/" << ans2 << endl; } signed main () { ios::sync_with_stdio (false ), cin.tie (0 ), cout.tie (0 ); int T = 1 ; while (T--) solve (); return 0 ; }
小结 冲冲冲吧,今天还算不错的!