- 题意
- 分析
- ac代码
题意
有 n 块木板排成一排的围栏。
莫诺卡普决定按照以下规则粉刷围栏:
- 栅栏的每块木板都要涂上一种颜色;
- 栅栏上的每块木板都要涂上恰好一种颜色;在m种颜色中只能选两种颜色
- 涂上相同颜色的栅栏木板必须形成一个连续的序列,也就是说,所有涂上相同颜色的木板对之间都没有涂上不同颜色的木板。
Monocarp 公司有 m 种不同的油漆,其中 i 种颜色的油漆足以涂刷不超过 ai 块木板。Monocarp 不会再购买其他油漆。
你的任务是确定满足 Monocarp 所描述的所有愿望的不同栅油漆方法的数量。如果有一块木板在这两种涂漆方式下被涂上了不同的颜色,那么这两种涂漆方式就被认为是不同的。
输入
第一行包含一个整数 t( 1≤t≤1e4 ) - 测试用例数。
每个测试用例的第一行包含两个整数 n 和 m ( 2≤n,m≤2⋅105) --栅栏中木板的数量和 Monocarp 拥有的不同颜色油漆的数量。
第二行包含 m 个整数 a1,a2,…,am ( 1≤ai≤n ),其中 ai 是可以涂上颜色为 i 的油漆的最大木板数。
所有测试用例的 n之和不超过 2⋅1e5。所有测试用例中 m 的总和不超过 2⋅1e5 。
输出
对于每个测试用例,输出满足 Monocarp 所描述的所有愿望的不同栅栏喷漆方法的数量。
Example
Input
Copy
3
5 2
2 4
5 2
3 4
12 3
5 9 8
Output
Copy
4
6
22
注
在第一个测试案例中,栅栏有 44 种不同的涂色方法(下面列出了从左到右可涂色木板的色号序列):
- [1,2,2,2,2]
- [1,1,2,2,2]
- [2,2,2,1,1]
- [2,2,2,2,1]
在第二个测试案例中,有 66 种不同的方法来粉刷栅栏(下面列出了从左到右粉刷木板的色号序列):
- [1,2,2,2,2]
- [1,1,2,2,2]
- [1,1,1,2,2]
- [2,2,1,1,1]
- [2,2,2,1,1]
- [2,2,2,2,1]
分析
涂满长度为n的围栏, 只能用两种颜色
注意到题目要求必须使用到两种颜色, 所以对于a[i]==n, 必须先 -1 . (假如这颜色能涂n个木板, 实际最多也只能涂n-1个)
首先可以先对每种颜色可以涂的围栏数量,也就是a[]数组进行排序
从小到大遍历a数组, 对于每个a[i], 寻找能配对的颜色
什么才能配对呢?
当然是 选涂的木板的数量+a[i]>=n 的才可以
对每个符合的配对颜色 答案ans可以加上 ( a[i] - ( n - a[j] ) + 1 ) * 2
(如果不明白这步可以随便选一个长度n,和一对符合的颜色对来试着计算一下发现规律)
现在就可以很朴素地用双指针解决问题......了吗?
rep(i,1,m-1){for(int j=m;j>i;j--){if(a[i]+a[j]<n)break;ans+=(a[i]+a[j]-n+1)*2;}}
这会导致超时
注意到那么对里面那层循环分析, 假设循环了k次, 那么ans就加上了k个a[i] , a[m-k+1] 到 a[m] 的和, k个n, k个1, 这些的和乘2
那么就可以考虑用二分找到k, 再用前缀和O(1) 地计算a[m-k+1] 到 a[m] 的和了
前缀和可以优化循环的公式累加, 这或许是个常用的技巧
ac代码
#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define all(x) (x).begin(),(x).end()
#define endl '\n'
#define int long longconst int N=2e5+5;int a[N];
int pre[N];
int n,m;void solve(){int n,m;cin>>n>>m;rep(i,1,m){cin>>a[i];if(a[i]==n)a[i]-=1;// 处理a[i]==n}sort(a+1,a+1+m);rep(i,1,m)pre[i]=pre[i-1]+a[i];int ans=0;rep(i,1,m-1){// for(int j=m;j>i;j--){ //朴素的双指针解法: 超时// if(a[i]+a[j]<n)break;// ans+=(a[i]+a[j]-n+1)*2;// }// 二分查找从左往右第一个符合与a[i] 能配对的颜色,后面当然都可以配对啦int l=i,r=m+1;while(l+1!=r){int mid=l+r>>1;if(a[mid]+a[i]<n)l=mid;else r=mid;}// rif(r==m+1)continue;// 说明对于当前a[i],即使找能涂最多的木板的颜色也不能涂完所有的木板int cnt=m-r+1;ans+= (a[i]*cnt+pre[m]-pre[r-1]-n*cnt+cnt)*2; }cout<<ans<<endl;
}signed main(){ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);int _;cin>>_;while(_--)solve();return 0;
}
题目来自 Educational Codeforces Round 176 (Rated for Div. 2) C. Two Colors