A
link
首先,对于一个数(比如说\(x\)),它和它加一一定互质(也就是\(x\)和\(x+1\)一定互质),那么它和它加一组成的区间(\([x,x+1]\))一定是好区间,也一定是最小好区间,因为除了本身\([x,x+1]\)、两个数\([x,x]\),\([x+1,x+1]\)和空集不包含其他区间了,而相等两个数一定不互质,所以那两个数的区间(\([x,x]\),\([x+1,x+1]\)),一定不是好区间。
有了这个,我们就可以说,对于任意长度大于\(2\)的区间,一定不是最小好区间,因为它一定会包含一些长度为\(2\)的区间,所以我们只要统计有多少区间长度恰好为\(2\)即可。
但是还有一个特例:\([1,2]\),这个区间包含的\([1,1]\)是一个好区间,所以\([1,2]\)并不是一个最小好区间,而如果\(l\)~\(r\)的范围包括了\([1,2]\),那么算上\([1,2]\)而不算\([1,1]\)与正确做法算上\([1,1]\)而不算\([1,2]\)对答案没有影响,所以只需要特判\(l\)和\(r\)均等于\(1\)的情况。
点击查看代码
#include<bits/stdc++.h>using namespace std;int l,r;void qwq(){cin >> l >> r;if(l == 1&&r == 1) cout << 1 << endl;else cout << r-l << endl;
}signed main(){int t;cin >> t;while(t--) qwq();return 0;}
B
link
一个小前提:我们让\(w\)代表区间里一共有几个数,也就是\(r-l+1\)。
首先,我们肯定是想通过翻转把不在区间中的前\(w\)小和在区间中的非前\(w\)小换换位置(就是在区间内选想换出去的\(k\)个,在区间外选想换进来的\(k\)个,做一下翻转即可),可是我们只能做一次操作,而不在区间中的前\(w\)小可能在区间前面,也可能在区间后面。也就是说我们要么把区间里的大数和前面换,要么和后面换,也就是要么选\([1,r]\)的前\(w\)小(和前面换),要么选\([l,n]\)的前\(w\)小(和后面换),比较一下哪个更小即可。
点击查看代码
#include<bits/stdc++.h>#define int long longusing namespace std;int n,l,r;
int a[100005];
int b[100005]; void qwq(){cin >> n >> l >> r;for(int i = 1;i <= n;++ i)cin >> a[i],b[i] = a[i];sort(a+1,a+1+r);sort(b+l,b+1+n);int ans1 = 0,ans2 = 0;int w = r-l+1;for(int i = 1,j = l;i <= w;++j,++ i)ans1 += a[i],ans2 += b[j];cout << min(ans1,ans2) << endl;}signed main(){int t;cin >> t;while(t--) qwq();return 0;}
C
link
脑补一棵树,我们想如果一个点有\(k\)条出边,那么它将会有\(k-1\)个儿子(还有父亲的那一条出边),那么如果把它删除,将会出现\(k\)个联通块(上面的和它所有的儿子)。
这样,删除一个点的就非常好做了。
正常来说,第二个点应该和第一个点是差不多的,但是,
看这个图,我们第一次删除的是灰色点,那么我们要是第二次删除紫色点,那么仍然会多出它出边数个联通块,也就是\(4\)个,但是如果我们第二次删除的是黄色点,那么问题来了,(大家可以脑补一下画面),我们第一次删除灰色点时,它连出去的边也没有了,那么黄色点将会少一条出边。
这时有人会说了,那么我们直接把和第一次选的点相邻的点出边数减一不就行了吗,那么看这个图,
我们想要把和第一次选的点相邻的点出边数减一,我们就要存第一次选的点的编号,一个黄色点和两个紫色点都是最优答案,如果我们存的是黄色点怎么办,答案会少(大家这里可以手推一下,选那两个紫色点是最优方案)。
所以我们就要换一种实现方式。我们不存第一次选的点,我们只存最大的出边数,再存一下每个点与几个出边数为最大出边数的点相邻(也就是与几个可以作为第一次选的点的点相邻)(包括它自己,原因见下),补充一下,我们把以作为第一次选的点的点称作最优点,那么我们一定可以知道第一次选的点一定会在最优点中选,如果它相邻的最优点个数和所有最优点个数相等,那么它就一定会和最优点相邻,直接减一即可,否则不需要减一。
那么为什么统计相邻最优点个数时要把本身加上呢?因为如果本身是一个最优点,而且最优点均在它周围,这种情况是要减一的,因为一定和最优点相邻。这时如果不加本身,会少数一个,认为还有一个最优点不与它相邻(其实这个最优点就是它本身),就不会减一了,答案就会错。
这里最终答案是要减一的,因为这两个点之间的那一部分会被两个点分别算一遍,一共算两遍,要去掉一遍。
点击查看代码
#include<bits/stdc++.h>using namespace std;int n;
vector<int> ed[200005];
int out[200005];
int lg[200005];void qwq(){cin >> n;for(int i = 1;i <= n;++ i)ed[i].clear(),out[i] = 0,lg[i] = 0;for(int i = 1;i < n;++ i){int u,v;cin >> u >> v;ed[v].push_back(u);ed[u].push_back(v);out[v]++;out[u]++;}int w = 0,ans = 0,sum = 0;for(int i = 1;i <= n;++ i)ans = max(ans,out[i]);for(int i = 1;i <= n;++ i){if(out[i] == ans){w++;lg[i]++;for(int j = 0;j < ed[i].size();++ j){int v = ed[i][j];lg[v]++;}}}for(int i = 1;i <= n;++ i){if(w == 1&&out[i] == ans) continue;//如果只有一个最优点还就是i,那么一定不可能把它作为第二次选的点,跳过即可if(lg[i] != w)sum = max(sum,ans+out[i]);else sum = max(sum,ans+out[i]-1);}cout << sum-1 << endl;}signed main(){int t;cin >> t;while(t--) qwq();return 0;}
D
link
我们考虑想让三角形尽可能大,一定要从两头选点。
那么我们考虑第\(i\)步到第\(i+1\)步。
1.如果有一边没法选了,那么撤回前一次选这边的那一步在接着选。
2.看看在上面选两个更好还是在下面选两个更好。
3.更新上下各选了几组两个。
那么一共可以选几次呢?(我们让\(n\)比\(m\)小)如果\(n\)的两倍还不到\(m\),那么最多只能组成\(n\)个三角形(头在\(n\)那,脚在\(m\)那),否则一定是\(\frac{n+m}{3}\)(感性理解一下)。
点击查看代码
#include<bits/stdc++.h>#define int long longusing namespace std;int read(){int x = 0,y = 1;char ch = getchar();while(ch > '9'||ch < '0'){if(ch == '-') y = -1;ch = getchar();}while(ch >= '0'&&ch <= '9'){x = x*10+ch-48;ch = getchar();}return x*y;
}int n,m;
int a[200005];
int b[200005];
int c[200005];
int d[200005];
int f[200005];void qwq(){n = read();m = read(); if(n > m){swap(n,m);for(int j = 1;j <= m;++ j)b[j] = read();for(int i = 1;i <= n;++ i)a[i] = read();}else{for(int i = 1;i <= n;++ i)a[i] = read();for(int j = 1;j <= m;++ j)b[j] = read();}sort(a+1,a+1+n);sort(b+1,b+1+m);int k;if(2*n <= m) k = n;else k = (n+m)/3;printf("%lld\n",k);for(int i = 1;2*i <= n;++ i)c[i] = a[n-i+1]-a[i];for(int j = 1;2*j <= m;++ j)d[j] = b[m-j+1]-b[j];int sta = 0,stb = 0,ans = 0;for(int i = 1;i <= k;++ i){if(2*sta+stb == n){ans = ans-c[sta]+d[stb+1];stb++;sta--;}if(2*stb+sta == m){ans = ans-d[stb]+c[sta+1];sta++;stb--;}if((2*sta+stb <= n-2)&&(2*stb+sta == m-1||c[sta+1] > d[stb+1])) ans += c[sta+1],sta++;else ans += d[stb+1],stb++;f[i] = ans;}for(int i = 1;i <= k;++ i)printf("%lld ",f[i]);puts("");}signed main(){int t = read();while(t--) qwq();return 0;}