T1 矩阵(matrix)
【题目描述】
从前有个 n×m 的矩阵,初始时每个位置均为 0。你需要依次执行 q 个操作,每个操作会指定一行或一列,然后将该行或该列的所有元素全部赋为一个相同的值。 输出操作完成后的矩阵。
【输入格式】
从文件 matrix.in 中读入数据。 第一行包含三个整数 n,m,q,分别表示矩阵的大小和操作次数。 接下来 q 行,每行三个正整数 t,x,y,若 t = 1,则表示将第 x 行的所有元素赋为 y; 若 t = 2,则表示将第 x 列的所有元素赋为 y。
【输出格式】 输出到文件 matrix.out 中。 输出 n 行,每行 m 个由空格隔开的整数,表示操作完成后的矩阵。
【样例 1 输入】
3 3 3
1 1 3
2 2 1
1 2 2
【样例 1 输出】
3 1 3
2 2 2
0 1 0
【样例 2 输入】
5 3 5
1 1 1
1 3 1
1 5 1
2 1 1
2 3 1
【样例 2 输出】
1 1 1
1 0 1
1 1 1
1 0 1
1 1 1
【子任务】
对于 20% 的数据,n×m≤25;
对于 30% 的数据,q≤2000;
对于 100% 的数据,n,m≤1000,n×m≤10^5,q≤10^6。 数据保证任一时刻矩阵中所有元素小于 2^31。
对于这题 我们发现操作次数非常大 这说明很多操作都是没有用的(抽屉原理)
为了省去不必要的操作 我们可以从后往前赋值
遇到已经赋值的行或列就跳过 遇到已经赋值的元素也跳过
第一篇代码是我写的(gg却ac的代码) 第二篇是标程
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 using namespace std; 5 const int N=1e6+10; 6 int n,m,q; 7 int a[N],b[N]; 8 int c[N]; 9 int ans[1010][1010]; 10 bool h[1010],l[1010]; 11 int main() 12 { 13 freopen("matrix.in","r",stdin); 14 freopen("matrix.out","w",stdout); 15 scanf("%d%d%d",&n,&m,&q); 16 for(int i=1;i<=q;i++) 17 scanf("%d%d%d",&a[i],&b[i],&c[i]); 18 for(int i=q;i>=1;i--){ 19 if(a[i]==1){ 20 if(h[b[i]])continue; 21 else 22 for(int j=1;j<=m;j++){ 23 if(ans[b[i]][j])continue; 24 ans[b[i]][j]=c[i]; 25 } 26 h[b[i]]=1; 27 } 28 else 29 if(a[i]==2){ 30 if(l[b[i]])continue; 31 else 32 for(int j=1;j<=n;j++){ 33 if(ans[j][b[i]])continue; 34 ans[j][b[i]]=c[i]; 35 } 36 l[b[i]]=1; 37 } 38 } 39 for(int i=1;i<=n;i++){ 40 for(int j=1;j<=m;j++) 41 printf("%d ",ans[i][j]); 42 printf("\n"); 43 } 44 return 0; 45 }
1 #include<cmath> 2 #include<cstdio> 3 #include<cstring> 4 #include<iostream> 5 #include<algorithm> 6 using namespace std; 7 const int MAXN = 1000005; 8 const int MAXn = 2005; 9 int n, m, q, i, j, k, a[MAXn], b[MAXn]; 10 int t[MAXN], x[MAXN], y[MAXN]; 11 inline int get() 12 { 13 char c; 14 while ((c = getchar()) < 48 || c > 57); 15 int res = c - 48; 16 while ((c = getchar()) >= 48 && c <= 57) 17 res = res * 10 + c - 48; 18 return res; 19 } 20 int main() 21 { 22 freopen("matrix.in", "r", stdin); 23 freopen("matrix.out", "w", stdout); 24 cin >> n >> m >> q; 25 for(i = 1; i <= q; i ++) 26 { 27 t[i] = get(); x[i] = get(); y[i] = get(); 28 if (t[i] == 1) a[x[i]] = i; 29 else b[x[i]] = i; 30 } 31 for(i = 1; i <= n; i ++) 32 { 33 for(j = 1; j <= m; j ++) 34 printf("%d ", y[max(a[i], b[j])]); 35 puts(""); 36 } 37 }
另外 这题数据是很多的 不写快读不行 会卡scanf
T2 坐标系(coordinate)
【题目描述】
从前有个平面直角坐标系。 你每次可以向上、向左或向右走,但不能经过重复的点。 求出你从坐标原点出发,走 n 步有多少种不同的方案。 答案对 10^9 + 7 取模。
【输入格式】 从文件 coordinate.in 中读入数据。 第一行一个整数 n,表示需要走的步数。
【输出格式】 输出到文件 coordinate.out 中。 第一行,一个整数,表示答案。
【样例 1 输入】 2
【样例 1 输出】 7
【样例 1 解释】
从 (0,0) 出发走 2 步,共有 7 种方案: (0,0)→(0,1)→(0,2) (0,0)→(0,1)→(1,1) (0,0)→(0,1)→(−1,1) (0,0)→(1,0)→(2,0) (0,0)→(1,0)→(1,1) (0,0)→(−1,0)→(−2,0) (0,0)→(−1,0)→(−1,1)
【样例 2 输入】 3
【样例 2 输出】 17
【子任务】
对于 20% 的数据,n≤10;
对于 40% 的数据,n≤100;
对于 60% 的数据,n≤1000;
对于 80% 的数据,n≤10^6; 对于 100% 的数据,n≤10^9。
只能向上 向左 向右走 不能走重复的路
找规律 我们得到
f[ i ] = 2 f[i - 1] + f[i - 2]
tle—>打表是个好方法—>不会
我们可以用矩阵乘法优化!
矩阵加法:两个行数和列数相同的矩阵相加 新的矩阵上的元素等于前两个对应位置上的和
矩阵乘法:与矩阵加法不同 并不要求行列数一定相等
一个n*k的矩阵乘上k*m的矩阵会得到一个n*m的矩阵
乘积C的第m行第n列的元素等于矩阵A的第m行的元素与矩阵B的第n列对应元素乘积之和
#include<iostream> #include<cstdio> #include<algorithm> #include<cstdio> const int mod=1e9+7; struct node{ long long a[2][2]; node(){ a[0][0]=a[1][1]=a[1][0]=a[0][1]=0; } }qaq,qwq; node operator *(node x,node y){ node z; for(int i=0;i<=1;i++) for(int j=0;j<=1;j++) for(int k=0;k<=1;k++) z.a[i][j]+=(x.a[i][k]%mod)*(y.a[k][j]%mod)%mod; return z; } node pow(node x,int n){ node f; f.a[0][0]=f.a[1][1]=1; while(n){ if(n%2) f=f*x; x=x*x; n>>=1; } return f; } int main() { //freopen("coordinate.in","r",stdin); //freopen("coordinate.out","w",stdout); int n; scanf("%d",&n); qaq.a[0][0]=1; qaq.a[0][1]=3; qwq.a[0][0]=0; qwq.a[0][1]=1; qwq.a[1][0]=1; qwq.a[1][1]=2; node ans; ans=qaq*pow(qwq,n); printf("%lld",ans.a[0][0]%mod); return 0; }
T3 Stall
【题目描述】
有 n 头牛,每头牛有个喝水时间,这段时间它将 . 独 . 占一个 Stall。现在给出每头牛 的喝水时间段,问至少要多少个 Stall 才能满足它们的要求。
【输入格式】 从文件 a.in 中读入数据。 第一行一个正整数 n。 接下来 n 行每行两个正整数 a,b,表示每头牛的喝水时间段。
【输出格式】 输出到文件 a.out 中。 一行一个整数,表示最少要安排多少个 Stall 才能满足所有牛的需求。
【样例 1 输入】
3
1 2
2 3
3 4
【样例 1 输出】
2
【子任务】 对于 100% 的数据,1≤n≤50000,1≤a≤b≤10^6。
显而易见 求区间最大值
planA 区间赋值 对于[x,y]的数每个加上1
70分 三个点tle
planB 差分 在a[x]的位置上++ a[y+1]位置--
再求前缀和!
如0 0 0 0 0 0在1~4加一
则得到序列 0 1 0 0 0 -1
前缀和 0 1 1 1 1 0
#include<iostream> #include<cstdio> using namespace std; int a[1000010]; int main() { //freopen("a.in","r",stdin); //freopen("a.out","w",stdout); int n; cin>>n; int max1=0; for(int i=1,x,y;i<=n;i++) { scanf("%d%d",&x,&y); a[x]++; a[y+1]--; max1=max(max1,y+1); } int ans=0; int s=0; for(int i=1;i<=max1;i++) { s+=a[i]; ans=max(ans,s); } printf("%d",ans); return 0; }
T4控制开关
【题目描述】 你有一个可以调节明暗度的灯泡,这个灯泡有 n 个明暗度,分别为 1,2,3,··· ,m。 灯泡有一个遥控器,你每按一次遥控器,假设灯泡当前亮度为 x,按一次以后就变成了 x+ 1,如果 x = m,则按一次以后变成 1。每个灯泡在设计时都有一个按钮,且有一个 舒适值 k,你可以按一次按钮,无论你现在的亮度是多少,你的亮度都会变成 k。按一 次按钮或按一次遥控器都算是操作一次。 现在给你一个序列 a1,··· ,an,一开始你的亮度是 a1,然后你要将亮度调到 a2,再 到 a3,再到 a4...... 最后到 an,完成这个亮度变化的过程会得到一个最小的操作次数 T,现在问你如何指定舒适值(舒适值指定之后不能改变),使得 T 最小。
【输入格式】 从文件 b.in 中读入数据。 第一行两个正整数 n,m。 第二行 n 个整数,第 i 个整数表示 ai。
【输出格式】 输出到文件 b.out 中。 输出一个整数,表示 T 的最小值。
【样例 1 输入】
4
6 1 5 1 4
【样例 1 输出】
5
【样例 2 输入】
10
10 10 9 8 7 6 5 4 3 2 1
【样例 2 输出】
45
【子任务】 对于 60% 的数据,n,m≤3000; 对于 90% 的数据,n,m≤10^5; 对于 100% 的数据,2≤n,m≤10^6,1≤ai ≤m, a[i]不等于 a[i+1]。
#include<iostream> #include<cstring> #include<cstdio> using namespace std; int n,m,a[1000005]; long long ans=0; long long cnt[1000005][2];//贡献 long long check()//计算没有按钮的一个一个按的答案 { long long pre=0; for(int i=2;i<=n;i++) { if (a[i]>a[i-1]) pre+=a[i]-a[i-1]; else pre+=a[i]+m-a[i-1]; } return pre; } void change(int l, int r, int s, int d)//差分 初始值s 公差d { cnt[l][0]+=s; cnt[r+1][0]-=s; cnt[l+1][1]+=d; cnt[r+1][1]-=(r-l+1)*d; cnt[r+2][1]+=(r-l)*d; } int main() { cin>>n>>m; for(int i=1;i<=n;i++) scanf("%d",&a[i]); for(int i=2;i<=n;i++) { if(a[i]>a[i-1])//将要到达的位置在后面 { if(a[i]-a[i-1]>=2)//位置相差2才有贡献 5->6按钮无贡献 5->8 按钮在6无贡献 在7贡献为1 在8贡献为2 (等差数列) change(a[i-1]+2,a[i],1,1); } else//将要到达的位置在前面 { if (m-a[i-1]>=2) change(a[i-1]+2,m,1,1),change(1,a[i],m-a[i-1],1); if (m-a[i-1]==1) change(1,a[i],1,1); if (m==a[i-1])//如m=5 5->3 按钮在2开始的位置开始才有贡献 在1贡献0 2贡献1 3贡献2... { if(a[i]>=2) change(2,a[i],1,1); } } } //统计结果 for(int i=2;i<=m+1;i++) cnt[i][0] += cnt[i-1][0],cnt[i][1]+=cnt[i-1][1]; for(int i=2;i<=m+1;i++) cnt[i][1]+=cnt[i-1][1]; //找出贡献最大的位置 for(int i=1;i<=m;i++) ans=max(ans,cnt[i][0]+cnt[i][1]); cout<<check()-ans<<endl; return 0; }