A.AvtoBus
没啥好说的,分类讨论
#include "bits/stdc++.h"
using namespace std;
#define int long long
int t,n,res1,res2;
signed main(){
cin>>t;
while(t--){
cin>>n;
if(n&1||n<4){
puts("-1");
continue;
}
else{
if(n%4==0&&n%6==0){
cout<<n/6<<" "<<n/4<<endl;
continue;
}else if(n%4==0){
if((n-4)%6==0){
cout<<(n-4)/6+1<<" ";
}else if((n-8)%4==0){
cout<<(n-8)/6+2<<" ";
}
cout<<n/4<<endl;
continue;
}else if(n%6==0){
cout<<n/6<<" ";
if((n-6)%4==0){
cout<<(n-6)/4+1<<endl;
}
}else{
if((n-4)%6==0){
cout<<1+(n-4)/6<<" ";
}else if((n-8)%6==0){
cout<<2+(n-8)/6<<" ";
}
if((n-6)%4==0){
cout<<1+(n-6)/4<<endl;
}
}
}
}
}
B. Stone Age Problem
简单方法,套线段树赋值的板子
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=5e5+5;
#define int long long
int b[maxn];
struct node
{
int l,r,sum,lazy;
node()
{
l=r=sum=lazy=0;
}
} a[maxn];
inline void update(int k)
{
a[k].sum=a[k*2].sum+a[k*2+1].sum;
}
void build(int k,int l,int r)
{
a[k].l=l,a[k].r=r;
a[k].lazy=0;
if(l==r)
{
a[k].sum=b[l];
return ;
}
int mid=(l+r)/2;
build(k*2,l,mid);
build(k*2+1,mid+1,r);
update(k);
}
void pushdown(int k)
{
if(a[k].l==a[k].r||!a[k].lazy)
{
a[k].lazy=0;
return ;
}
a[k*2].sum=(a[k*2].r-a[k*2].l+1)*a[k].lazy;
a[k*2+1].sum=(a[k*2+1].r-a[k*2+1].l+1)*a[k].lazy;
a[k*2].lazy=a[k].lazy;
a[k*2+1].lazy=a[k].lazy;
a[k].lazy=0;
}
void changesegment(int k,int l,int r,int x)
{
if(a[k].l==l&&a[k].r==r)
{
a[k].sum=(r-l+1)*x;
a[k].lazy=x;
return ;
}
pushdown(k);
int mid=(a[k].l+a[k].r)/2;
if(r<=mid)
{
changesegment(k*2,l,r,x);
}
else if(l>mid)
changesegment(k*2+1,l,r,x);
else
{
changesegment(k*2,l,mid,x);
changesegment(k*2+1,mid+1,r,x);
}
update(k);
}
int query(int k,int l,int r)
{
if(a[k].lazy)
pushdown(k);
if(a[k].l==l&&a[k].r==r)
return a[k].sum;
int mid=(a[k].r+a[k].l)/2;
if(r<=mid)
return query(k*2,l,r);
if(l>mid)
return query(k*2+1,l,r);
return query(k*2,l,mid)+query(k*2+1,mid+1,r);
}
signed main()
{
int n,m;
scanf("%lld%lld",&n,&m);
for(int i=1;i<=n;i++)
scanf("%lld",&b[i]);
build(1,1,n);
for(int i=1;i<=m;i++){
int t;
scanf("%lld",&t);
if(t==1){
int i,x;
scanf("%lld%lld",&i,&x);
changesegment(1,i,i,x);
printf("%lld\n",query(1,1,n));
}
else{
int x;
scanf("%lld",&x);
changesegment(1,1,n,x);
printf("%lld\n",query(1,1,n));
}
}
}
C. Rooks Defenders
有两种方法,虽然方法都是树状数组但是还是有很大的区别的。
1.将行和列套上作为桶存放每一行或者每一列有没有放,放多少,要求行和列全满,即行的数量数量统计为n或者列的数量统计为m。当然,注意一行或者一列贡献值只能是1,但是删除的话删除的也只能是1个。所以代码如下
#include<iostream>
#include<stdio.h>
#include<algorithm>
using namespace std;
const int N=1e5+10;
int a[N];
int b[N];
int n;
int c[N];
int e[N];
inline int lowbit(int x) // 求二进制下最低位的1
{
return x & -x;
}
inline void add(int x, int d) //单点修改 给第x号元素 + d
{
while (x <= n) {
a[x] += d;
x += lowbit(x);
}
}
inline int ask(int x) // 求从第一个元素 到 第x个元素的总和
{
int res = 0;
while (x) {
res += a[x];
x -= lowbit(x);
}
return res;
}
inline int getsum(int x, int y) // 求区间[l, r]内的元素总和
{
return ask(y) - ask(x - 1);
}
inline void add1(int x, int d) //单点修改 给第x号元素 + d
{
while (x <= n) {
b[x] += d;
x += lowbit(x);
}
}
inline int ask1(int x) // 求从第一个元素 到 第x个元素的总和
{
int res = 0;
while (x) {
res += b[x];
x -= lowbit(x);
}
return res;
}
inline int getsum1(int x, int y) // 求区间[l, r]内的元素总和
{
return ask1(y) - ask1(x - 1);
}
inline void add2(int x, int d) //单点修改 给第x号元素 + d
{
while (x <= n) {
c[x] += d;
x += lowbit(x);
}
}
inline int ask2(int x) // 求从第一个元素 到 第x个元素的总和
{
int res = 0;
while (x) {
res += c[x];
x -= lowbit(x);
}
return res;
}
inline void add3(int x, int d) //单点修改 给第x号元素 + d
{
while (x <= n) {
e[x] += d;
x += lowbit(x);
}
}
inline int ask3(int x) // 求从第一个元素 到 第x个元素的总和
{
int res = 0;
while (x) {
res += e[x];
x -= lowbit(x);
}
return res;
}
inline int getsum2(int x, int y) // 求区间[l, r]内的元素总和
{
return ask2(y) - ask2(x - 1);
}
inline int getsum3(int x, int y) // 求区间[l, r]内的元素总和
{
return ask3(y) - ask3(x - 1);
}
signed main(){
int q;
scanf("%lld%lld",&n,&q);
while(q--){
int t;
scanf("%lld",&t);
if(t==1){
int x,y;
scanf("%lld%lld",&x,&y);
int u=getsum(x,x);
int v=getsum1(y,y);
if(u==0)
add(x,1);
if(v==0)
add1(y,1);
add2(x,1);
add3(y,1);
}
else if(t==2){
int x,y;
scanf("%lld%lld",&x,&y);
add2(x,-1);
add3(y,-1);
int u=getsum2(x,x);
int v=getsum3(y,y);
if(u==0)
add(x,-1);
if(v==0)
add1(y,-1);
}
else{
int x,y,x1,y1;
scanf("%lld%lld%lld%lld",&x,&y,&x1,&y1);
int u=getsum(min(x,x1),max(x,x1));
int v=getsum1(min(y,y1),max(y,y1));
if(u==abs(x-x1)+1||v==abs(y-y1)+1){
printf("YES\n");
}
else{
printf("NO\n");
}
}
}
}
第二种求行和列的覆盖的最小值,如果说这一行或者这一列全满,最小值肯定不为0,反之为0.口胡一下不写捏。
D.Toss a Coin to Your Graph...
这题一看到最大值最小,我们可以马上想到用二分。但是在比赛的时候我确实没有想出要怎么做的想法。还是等赛后大佬告诉了我思路我才明白怎么做,这题是补题捏。
那么这一题怎么做呢,我们只需要二分这个能得到的值,然后将所有能得到这个值的点和对应相关联的边连接起来,然后跑一遍topsort来寻找有没有环,然后再跑一遍dfs来确定最长的链能不能达到k,如果存在环就不用判断最长链能不能到k了,代码如下:
#include <bits/stdc++.h>
using namespace std;
#define int long long
vector<int> g[200005];
vector<int> v[200005];
int a[200005];
int n;
vector<int> b;
bool topo_sort() {
vector<int> degree(n+500, 0);
queue<int> q;
for (int i = 0; i <(int) b.size(); i++)
degree[b[i]]=0;
for (int i = 0; i <(int) b.size(); i++) {
for(int j=0;j<(int)g[b[i]].size();j++){
int y=g[b[i]][j];
degree[y]++;
}
}
for (int i = 0; i <(int) b.size(); i++) {
if (degree[b[i]] == 0) {
q.push(b[i]);
}
}
int cnt = 0;
while (!q.empty()) {
cnt++;
int root = q.front();
// cout<<root<<" ";
q.pop();
for (auto child : g[root]) {
degree[child]--;
if (degree[child] == 0) {
q.push(child);
}
}
}
int u=b.size();
if(cnt==u){
// cout<<111<<endl;
return false;
}
else{
return true;
}
// return (cnt != (int)b.size());
}
int f[200005];
void dfs(int x,int fa){
if(f[x]!=0)
return;
f[x]=1;
int res=0;
for(int i=0;i<(int)g[x].size();i++){
int y=g[x][i];
if(y==fa)
continue;
dfs(y,x);
res=max(res,f[y]);
}
f[x]+=res;
return;
}
bool solve(int x,int k){
for(int i=1;i<=n;i++)
g[i].clear();
b.clear();
for(int i=1;i<=n;i++)
if(a[i]<=x){
b.push_back(i);
for(int j=0;j<(int)v[i].size();j++){
int y=v[i][j];
if(a[y]<=x)
g[i].push_back(y);
}
}
int u=topo_sort();
if(u){
return 1;
}
for(int i=1;i<=200000;i++)
f[i]=0;
int res=0;
for(int i=0;i<b.size();i++)
dfs(b[i],0);
for(int i=0;i<(int)b.size();i++){
// cout<<b[i]<<endl;
res=max(res,f[b[i]]);
}
if(res>=k)
return 1;
else
return 0;
}
signed main() {
int m,k;
scanf("%lld%lld%lld",&n,&m,&k);
for(int i=1;i<=n;i++)
cin>>a[i];
for(int i=1;i<=m;i++){
int x,y;
scanf("%lld%lld",&x,&y);
v[x].push_back(y);
}
int l=1,r=1e9+500;
if(m==0){
int res=1e9;
for(int i=1;i<=n;i++)
res=min(res,a[i]);
if(k==1)
cout<<res;
else
cout<<-1;
return 0;
}
while(l<r){
int mid=(l+r)/2;
if(solve(mid,k)) r=mid;
else l=mid+1;
}
if(l>1000000000)
cout<<-1;
else
cout<<l;
return 0;
}