校内NOIP模拟简要题解
校内NOIP模拟简要题解
7.15
T1
简要题意 : 给定一个N*M的矩阵(N,M小于1018),有多少种方案可以使得每行每列乘积都为K,K为1或-1
Solution : 在有解的情况下,显然最后一行和最后一列一定可以调整前面的值为K,所以答案为 2(n-1)(m-1)
显然无解当且仅当N,M不同奇偶,此时答案为0
#include<bits/stdc++.h> using namespace std; inline long long read() { long long f = 1 , x = 0; char ch; do { ch = getchar(); if(ch == '-') f = -1; }while(ch < '0' || ch > '9'); do { x = (x<<3) + (x<<1) + ch - '0'; ch = getchar(); }while(ch >= '0' && ch <= '9'); return f*x; } const int MOD = 1e9 + 7; long long n,m; long long k; inline long long Pow(long long a, long long b) { long long ans = 1 ,mul = a; while(b) { if(b&1) ans = (ans * mul) % MOD; mul = (mul * mul) % MOD; b >>= 1; } return ans % MOD; } int main() { freopen("field.in","r",stdin); freopen("field.out","w",stdout); n = read(),m = read();k = read(); if((n+m)&1&&k==-1) cout << 0 << endl; else printf("%lld\n",Pow(Pow(2,n-1),m-1)%MOD); return 0; } /* 2 1 1 2 2 2 2 3 4 */
T2
简要题意:
Solution:显然对于初始值大于i的,往前移一个值就会减一,同理可以考虑小于i的,然后记录一下什么时候大小关系发生改变,模拟一下即可
#include<bits/stdc++.h> using namespace std; inline long long read() { long long f = 1 , x = 0; char ch; do { ch = getchar(); if(ch == '-') f = -1; }while(ch < '0' || ch > '9'); do { x = (x<<3) + (x<<1) + ch - '0'; ch = getchar(); }while(ch >= '0' && ch <= '9'); return f*x; } const int MOD = 1e9 + 7; const int MAXN = 1e6 + 10; int n; int p[MAXN]; int a[MAXN]; int main() { freopen("mister.in","r",stdin); freopen("mister.out","w",stdout); n = read(); long long res = 0; long long ans = 0; int a1=0,a2=0; for(int i = 1; i<=n;i++) { p[i] = read(); res += abs(p[i] - i); a[(p[i]-i+n)%n]++; if(p[i]>i) a1++; else a2++; } ans = res; for(int i=1;i<n;i++) { res += (a2 - a1 - 1); res += (p[n-i+1] - 1) - (n - p[n-i+1]); a1 = a1 - a[i] + 1; a2 = a2 + a[i] - 1; if(res < ans) { ans = res; } } cout << ans << endl; } /* 2 1 1 2 2 2 2 3 4 */
T3
简要题意:给定一个序列,求出这个序列所有连续子区间最大值减最小值差值的和
Solution:显然只需要考虑一个数作为最大值和最小值出现的次数即可
维护第i个数左边第一个大于,小于它的数的位置,和右边第一个大于小于它的数的位置即可
代码找不到了
7.16
T1
不大会
T2
依旧不大会
T3
简单期望DP
先贴个代码
#include<bits/stdc++.h> using namespace std; inline long long read() { long long f = 1,x = 0; char ch; do { ch = getchar(); if(ch == '-') f = -1; }while(ch<'0'||ch>'9'); do { x = (x<<3) + (x<<1) + ch - '0'; ch = getchar(); }while(ch>='0'&&ch<='9'); return f*x; } #define Rg register const int MAXN = 500 + 10; const int MOD = 1e9 + 7; long long n,m,k; long long a[MAXN]; long long f[MAXN][MAXN]; long long sum[MAXN][MAXN]; inline long long Pow(long long a ,long long b) { long long ans = 1, mul = a; while(b) { if(b&1) ans = (ans * mul) % MOD; mul = mul * mul % MOD; b>>=1; } return ans % MOD; } int main() { freopen("kat.in","r",stdin); freopen("kat.out","w",stdout); n = read(),m = read(),k = read(); for(Rg int i=1;i<=m;i++) a[i] = read(); // cout << Pow(m,MOD-2) << endl; for(Rg int i=1;i<=m;i++) f[1][i] = Pow(m , MOD-2); for(Rg int i=1;i<=m;i++) sum[1][i] = (sum[1][i-1]+f[1][i])%MOD; for(Rg int i=2;i<=k;i++) { for(Rg int j=1;j<=m;j++) { f[i][j] = (sum[i-1][j-1] + f[i-1][j]*j%MOD) * Pow(m,MOD-2) % MOD; // cout << "f:" << f[i][j] << endl; sum[i][j] = (sum[i][j-1] + f[i][j]) % MOD; } } long long ans = 0; for(Rg int i=1;i<=m;i++) ans = (ans + f[k][i] * a[i])% MOD; printf("%lld\n",ans*(n-k+1)%MOD); }
7.17
T1
简要题意 :求1到N的序列中逆序对对数为K的一共有多少个,N,K<=1000
Solution:考虑到如果是在1-N-1的序列中加入一个N则一定不会有末尾为N的逆序对
于是考虑 $ f_{i,j} $ 表示加入到了第i个数有j个逆序对的方案数,显然DP即可,可以前缀和优化
#include<bits/stdc++.h> using namespace std; inline int read() { int f = 1,x = 0; char ch; do { ch = getchar(); if(ch == '-') f = -1; }while(ch < '0' || ch > '9'); do { x = (x<<3) + (x<<1) + ch - '0'; ch = getchar(); }while(ch >= '0' && ch <= '9'); return f*x; } const int MOD = 10000; const int MAXN = 2000 + 10; int T; int n,k; long long f[MAXN][MAXN]; inline void init() { f[1][0] = 1; for(int i=2;i<=1000 + 5;i++) { long long res = 0; for(int j=0;j<=min(1000 + 5,i*(i-1)/2);j++) { res = (res + f[i-1][j]) % MOD; if(j>=i) res -= f[i-1][j-i],res=(res+MOD)%MOD; f[i][j] = res % MOD; } } } int main() { freopen("permut.in","r",stdin); freopen("permut.out","w",stdout); T = read(); init(); while(T--) { n = read();k = read(); printf("%d\n",f[n][k]); } }
T2
简要题意:
Solution:
#include<bits/stdc++.h> using namespace std; inline int read() { int f = 1,x = 0; char ch; do { ch = getchar(); if(ch == '-') f = -1; }while(ch < '0' || ch > '9'); do { x = (x<<3) + (x<<1) + ch - '0'; ch = getchar(); }while(ch >= '0' && ch <= '9'); return f*x; } const int MAXN = 2000 + 10; int n,q; int a[MAXN]; int l[MAXN<<1],r[MAXN<<1]; int f[MAXN]; int dp[MAXN][MAXN]; int main() { freopen("beautiful.in","r",stdin); freopen("beautiful.out","w",stdout); n = read(); for(int i=1;i<=n;i++) a[i] = read(); for(int i=1;i<=n;i++) { for(int j=1;j<=2*n;j++) l[j] =r[j] = -1; l[n] = r[n] = 0; int res = 0; for(int j=i-1;j>=1;j--) { if(a[j]<=a[i]) res--; if(a[j]>a[i]) res++; l[n+res] = i - j; } res = 0; for(int j=i+1;j<=n;j++) { if(a[j]>=a[i]) res++; if(a[j]<a[i]) res--; r[n+res] = j - i; } for(int j=1-i;j<=i-1;j++) { if(r[n+j]>=0&&l[n-j]>=0) { f[i] = max(f[i],r[n+j]+l[n-j]+1); } } } for(int i=1;i<=n;i++) dp[i][i] = f[i]; for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++) dp[i][j] = max(dp[i][j-1],f[j]); q = read(); for(int i=1;i<=q;i++) { int l = read(),r = read(); printf("%d\n",dp[l][r]); } }
T3
Solution:
#include<bits/stdc++.h> using namespace std; inline int read() { int f = 1,x = 0; char ch; do { ch = getchar(); if(ch == '-') f = -1; }while(ch < '0' || ch > '9'); do { x = (x<<3) + (x<<1) + ch - '0'; ch = getchar(); }while(ch >= '0' && ch <= '9'); return f*x; } #define Rg register #define MAXN 500 #define pre(x) x>>8 #define suf(x) x&255 int N; int a[MAXN + 10][MAXN + 10]; char opt[50]; int main() { freopen("subset.in","r",stdin); freopen("subset.out","w",stdout); N = read(); for(Rg int i=1;i<=N;i++) { scanf("%s",opt+1); int x = read(); int val; if(opt[1]=='d') val=-1; else val = 1; if(opt[1]=='a'||opt[1]=='d') { int u = pre(x), v = suf(x) , w = v^255; a[u][v] += val; for(int S=w;S;S=(S-1)&w) a[u][S|v] += val; // S|v -> father } else { int u = pre(x) ,v = suf(x); long long ans = 0; ans += a[0][v]; for(int S=u;S;S=(S-1)&u) ans += a[S][v];// S -> u son printf("%lld\n",ans); } } }
10.30
T1
简要题意:
给定一个有向图,对其中一条边u,v,求u,v不经过该边的最短路
solution:
每次枚举起点,可以O(M)计算最短路和次短路
T3
给定一个位数小于1000的数N,重排每位数字,使凑出一个最大的被11整除的数
solution:
11的倍数的数满足奇数位和偶数位和相等
对于一个给定的N,可以直接算出奇数位和和偶数位和
贪心的考虑可以想到从大到小放入奇数位和偶数位,如果放不到奇数位就一点在偶数位
所以DP预处理出每种状态的可行性
#include<bits/stdc++.h> using namespace std; inline int read() { int f = 1 ,x = 0; char ch; do { ch = getchar(); if(ch == '-') f = -1; }while(ch < '0'||ch > '9'); do { x = (x<<3) + (x<<1) + ch - '0'; ch = getchar(); }while(ch >= '0'&&ch <= '9'); return f*x; } const int MAXN = 1000 + 10; char s[MAXN]; int x[MAXN]; int dp[MAXN][MAXN][11]; int P[MAXN]; bool vis[MAXN]; int col[MAXN],coll; vector<int>G[2]; int top[2]; int main() { freopen("sort.in","r",stdin); freopen("sort.out","w",stdout); scanf("%s",s+1); int len = strlen(s+1); int sum = 0; for(int i=1;i<=len;i++) x[i] = s[i] - '0',sum = (sum + x[i]) % 11; // P[0] = 1;for(int i=1;i<=len;i++) P[i] = (P[i-1] * 10) % 11; sum %= 11; sort(x+1,x+len+1); int res = (len+1)/2; memset(dp,0,sizeof(dp)); dp[0][0][0] = 1; for(int i=1;i<=len;i++) { //cout << x[i] << endl; for(int j=0;j<=min(res,i);j++) { for(int k=0;k<=10;k++) { // if(i <= 2) cout << i << " " << j << " " << dp[1][0][10] << endl; if(j > 0) dp[i][j][k] = dp[i][j][k]|dp[i-1][j-1][(k-x[i]+11)%11]; dp[i][j][k] = dp[i][j][k]|dp[i-1][j][k]; } } } if(sum & 1) sum += 11; int cur = sum / 2;coll = 1; // cout << cur << endl; int sum_o = 0,sum_e = 0; int cur_o = 0,cur_e = 0; // cout << cur << endl; int res_o = res,res_e = len/2; // cout << dp[len-1][res_o-1][(cur - x[len] + 11) % 11] << endl; for(int i=len;i>=1;i--) { if(coll == 1) { if(res_o < cur_o + 1) { while(i>=1) col[i] = 0,i--; continue; } while(res_o>=cur_o+1&&i>=1&&dp[i-1][res_o-cur_o - 1][(cur - sum_o + 11 -x[i]+11)%11] == 0) sum_e += x[i],sum_e %= 11,cur_e++,col[i] = 0,i--; if(!i) break; // cout << i << endl; col[i] = 1;sum_o += x[i];sum_o %= 11;cur_o++;coll^=1; } else { if(res_e < cur_e + 1) { while(i>=1) col[i] = 1,i--; continue; } // cout << i-1 << " " << res_e - 1 << " " << (cur - x[i] + 11) % 11 << endl; // return 0; // cout << dp[i-1][res_e-cur_e - 1][(cur - sum_e + 11-x[i]+11)%11] << endl; while(res_e>=cur_e+1&&i>=1&&dp[i-1][res_e-cur_e - 1][(cur - sum_e + 11-x[i]+11)%11] == 0) sum_o += x[i],cur_o++,col[i] = 1,i--,sum_o %= 11; if(!i) break; col[i] = 0;sum_e += x[i];sum_e %= 11;cur_e++;coll^=1; } } //cout << res_e << " " <<res_o << endl; // cout << 1 << endl; for(int i=len;i>=1;i--) G[col[i]].push_back(i); // cout << 1 << endl; int curr = 1; for(int i=1;i<=len;i++) { printf("%d",x[G[curr][top[curr]++]]); curr^=1; } // for(int i=len;i>=1;i--) if(vis[i]) printf("%d",x[i]); // for(int i=len;i>=1;i--) if(!vis[i]) printf("%d",x[i]); }