2021牛客暑期多校训练营6
文章目录
C:Delete_Edges
解释
结论题。题目转换为从n个点中任意选择三个点,使得三个点不会重复出现的方案数。
输出 ( x + y + z ) % n = = 0 , 1 < = x < y < z < = n (x + y + z) \% n == 0,1 <= x < y < z <= n (x+y+z)%n==0,1<=x<y<z<=n
代码
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
//#define int long long
struct Node{
int a,b,c;
};
vector<Node> ans;
signed main(){
IOS
#ifdef ddgo
freopen("C:\\Users\\asus\\Desktop\\ddgoin.txt","r",stdin);
#endif
int n; cin>>n;
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++){
int z = n - i - j;
if(z <= 0) z += n;
if(z <= j) continue;
ans.push_back({i,j,z});
}
cout<<(int)ans.size()<<endl;
for(auto i : ans) cout<<i.a<<" "<<i.b<<" "<<i.c<<"\n";
return 0;
}
F:Hamburger_Steak
解释
二分,假设答案时间为m,按顺序放,当放到一个物品超过m则分开,放到下一个锅。l一定大于等于最长时间,这样可以保证不会有一个汉堡不会在一个时间出现在两个锅里面。
最优时间可以计算。 m i d = m a x ( m a x ( a i , a i + 1 , . . . ) , ( s u m + m − 1 ) / m ) mid = max(max(a_i,a_{i+1},...),(sum+m-1)/m) mid=max(max(ai,ai+1,...),(sum+m−1)/m)
这样可以去掉二分部分。直接调用slove。
代码
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const double eps = 1e-5;
const int N = 2e5+10;
const int mod = 998244353;
int a[N],n,m;
bool check(int mid){
int cursum = 0,j = 1;
for(int i=1;i<=n;i++){
if(cursum + a[i] > mid){
j ++;
cursum = cursum + a[i] - mid;
}else{
cursum += a[i];
if(cursum == mid){
if(i != n) j ++;
cursum = 0;
}
}
}
return j <= m;
}
void slove(int mid){
int cursum = 0,j = 1;
for(int i=1;i<=n;i++){
if(cursum + a[i] > mid){
j ++; int k = cursum;
cursum = cursum + a[i] - mid;
cout<<2<<" "<<j<<" "<<0<<" "<<cursum<<" "<<j-1<<" "<<k<<" "<<mid<<endl;
}else{
cursum += a[i];
cout<<1<<" "<<j<<" "<<cursum-a[i]<<" "<<cursum<<endl;
if(cursum == mid){
if(i != n) j ++;
cursum = 0;
}
}
}
}
signed main(){
IOS
int ma = 0; cin>>n>>m;
for(int i=1;i<=n;i++) cin>>a[i],ma = max(ma,a[i]);
int l = ma,r = 1e14;
while(l < r){
int mid = (l + r) >> 1;
if(check(mid)) r = mid;
else l = mid + 1;
}
slove(r);
return 0;
}
H:Hopping_Rabbit
解释
跳的路线可以画成一个环,然后还可以把每(0 ~ d) x (0 ~ d) 的一个区域重叠到一起,然后将所有区域分割合到一个区域,之后就是扫描线格式,枚举 (0 ~ d) 当某一列或者某一行不存在被覆盖的位置,则输出那个位置.
分割需要注意。下面让 ( x 1 , y 1 ) , ( x 2 − 1 , y 2 − 1 ) (x1,y1),(x2-1,y2-1) (x1,y1),(x2−1,y2−1) 代表可跳矩阵,然后对d取mod后移到相应位置。因为扫描线是要用矩阵,所以可跳矩阵变成 ( x 1 , y 1 ) , ( x 2 + 1 , y 2 + 1 ) (x1,y1),(x2+1,y2+1) (x1,y1),(x2+1,y2+1) ,之后维护扫描线。
代码
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int cnt = 0,n,d;
struct Point{
int x,y1,y2;
int k;
bool operator <(const Point&T)const{
return x < T.x;
}
}point[N];
#define l(x) t[x].l
#define r(x) t[x].r
#define cnt(x) t[x].cnt
#define len(x) t[x].len
struct SegmentTree{
int l,r,cnt;
double len;
}t[N << 2];
void push_up(int p,int l,int r){
if(cnt(p)) len(p) = r - l + 1;
else if(l == r) len(p) = 0;
else len(p) = len(p<<1) + len(p<<1|1);
}
void build(int p,int l,int r){
l(p) = l,r(p) = r;cnt(p) = len(p) = 0;
if(l == r) return ;
int mid = (l + r) >> 1;
build(p<<1,l,mid);build(p<<1|1,mid+1,r);
}
void update(int p,int l,int r,int k){
if(l <= l(p) && r(p) <= r){
cnt(p) += k;
push_up(p,l(p),r(p));
return ;
}
int mid = (l(p) + r(p)) >> 1;
if(l <= mid) update(p<<1,l,r,k);
if(r > mid) update(p<<1|1,l,r,k);
push_up(p,l(p),r(p));
}
int query(int p){
if(len(p) == 0) return l(p);
int mid = (l(p) + r(p)) >> 1;
if(len(p<<1) < (mid - l(p) + 1)) return query(p<<1);
else return query(p<<1|1);
}
void md(int &x){
x = (x % d + d) % d;
}
void split(int x1,int x2,int y1,int y2){
md(x1),md(x2),md(y1),md(y2);
x2 ++;y2 ++;
if(x1 < x2 && y1 < y2){
point[++ cnt] = {x1,y1,y2,1};point[++ cnt] = {x2,y1,y2,-1};
}else if(x1 > x2 && y1 < y2){
point[++ cnt] = {0,y1,y2,1};point[++ cnt] = {x2,y1,y2,-1};
point[++ cnt] = {x1,y1,y2,1};point[++ cnt] = {d,y1,y2,-1};
}else if(x1 < x2 && y1 > y2){
point[++ cnt] = {x1,0,y2,1};point[++ cnt] = {x2,0,y2,-1};
point[++ cnt] = {x1,y1,d,1};point[++ cnt] = {x2,y1,d,-1};
}else{
point[++ cnt] = {0,0,y2,1};point[++ cnt] = {x2,0,y2,-1};
point[++ cnt] = {0,y1,d,1};point[++ cnt] = {x2,y1,d,-1};
point[++ cnt] = {x1,0,y2,1};point[++ cnt] = {d,0,y2,-1};
point[++ cnt] = {x1,y1,d,1};point[++ cnt] = {d,y1,d,-1};
}
}
signed main(){
IOS
#ifdef ddgo
freopen("C:\\Users\\asus\\Desktop\\ddgoin.txt","r",stdin);
#endif
cin>>n>>d;
for(int i=0;i<n;i++){
int x1,x2,y1,y2; cin>>x1>>y1>>x2>>y2;
x2 --, y2 --;
if(x2 - x1 + 1 >= d) x1 = 0,x2 = d-1;
if(y2 - y1 + 1 >= d) y1 = 0,y2 = d-1;
split(x1,x2,y1,y2);
}
sort(point+1,point+cnt+1);
build(1,0,d-1);
int ansx = -1;
for(int i=0,j=1;i<d;i++){
while(j <= cnt && point[j].x <= i){
int l = point[j].y1,r = point[j].y2-1,k = point[j].k;
update(1,l,r,k);
j ++;
}
if(len(1) == d) continue;
ansx = i;
break;
}
if(ansx == -1) cout<<"NO"<<endl;
else cout<<"YES\n"<<ansx<<" "<<query(1)<<endl;
return 0;
}
I:Intervals_on_the_Ring
解释
将l > r 的区间开,1 ~ l , r ~ n, 将区间排序。
假设得到区间 ( l 1 , r 1 ) , ( l 2 , r 2 ) , ( l 3 , r 3 ) (l1,r1),(l2,r2),(l3,r3) (l1,r1),(l2,r2),(l3,r3)
输出 ( l 3 , r 2 ) , ( l 2 , r 1 ) , ( l 1 , r 3 ) (l3,r2),(l2,r1),(l1,r3) (l3,r2),(l2,r1),(l1,r3) 枚举每一个间隙,输出除这个间隙以外的区间。
代码
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e3 + 10;
const int mod = 1e9 + 7;
const double eps = 1e-8;
const int INF = 0x3f3f3f3f;
typedef pair<int,int> pii;
typedef long long ll;
signed main(){
IOS
int tt; cin>>tt;
while(tt --){
int n,m; cin>>n>>m;
vector<pii> ve;
int minr = INF,maxl = -1;
for(int i=1;i<=m;i++){
int l,r; cin>>l>>r;
if(l > r){
maxl = max(maxl,r);
minr = min(minr,l);
}else{
ve.push_back({l,r});
}
}
if(minr <= n) ve.push_back({minr,n});
if(maxl >= 1) ve.push_back({1,maxl});
sort(ve.begin(),ve.end());
int len = (int)ve.size();
cout<<len<<endl;
for(int i=len-1;i>=1;i--) cout<<ve[i].first<<" "<<ve[i-1].second<<endl;
cout<<ve[0].first<<" "<<ve[len-1].second<<endl;
}
return 0;
}
J:Defend_Your_Country
解释
连通块偶数节点贡献乘上1,奇数乘上-1,求删边,使得所有贡献值之和最大。
- 若n为偶数,则不删。
- 若n为奇数,则删一个最小值的点。若为割点且删掉之后子连通块有奇数,则往后面找。
用tarjan找割点顺便求出删除这个割点是否满足子连通块全为偶数
注意各种初始化
代码
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#include<bits/stdc++.h>
using namespace std;
const int N = 2e6 + 10;
typedef long long ll;
int ne[N],e[N],h[N],w[N],idx=1;
int dfn[N],low[N],cut[N],tim;
int si[N],ok[N];
void add(int a,int b){
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void tarjan(int u,int fa){
dfn[u] = low[u] = ++tim;
int child = 0;
si[u] = 1;
for(int i=h[u];i;i=ne[i]){
int j = e[i];
if (!dfn[j]){
tarjan(j,fa);
low[u] = min(low[u],low[j]);
if(low[j] >= dfn[u] && u != fa) cut[u] = 1;
if(u == fa) child++;
si[u] += si[j];
if(low[j] >= dfn[u] && si[j] % 2) ok[u] = 0;
// 写成if(cut[u] && si[j] % 2) 则最后还要额外判断fa的子连通块是否有奇数
}
low[u] = min(low[u],dfn[j]);
}
if(child >= 2 && u == fa) cut[u] = 1;
}
signed main(){
int tt; scanf("%d",&tt);
while(tt --){
int n,m; scanf("%d %d",&n,&m);
ll sum = 0;
for(int i=1;i<=n;i++) scanf("%d",&w[i]),sum += w[i];
for(int i=1;i<=n;i++) cut[i] = dfn[i] = low[i] = 0,ok[i] = 1;
for(int i=1;i<=idx;i++) h[i] = 0;
tim = 0; idx = 1;
for(int i=1;i<=m;i++){
int a,b; scanf("%d %d",&a,&b);
add(a,b);add(b,a);
}
if(n % 2 == 0) printf("%lld\n",sum);
else{
tarjan(1,1);
ll res = 1e18;
for(int i=1;i<=n;i++) if(ok[i]) res = min(res,(ll)w[i]);
sum -= 2*res;
printf("%lld\n",sum);
}
}
return 0;
}