2026 暑期每日一题

ooliver 发布于 2026-06-26 381 次阅读 OI


AI 摘要

边权拆给点,答案竟分毫不差?四元组异或和暗藏不变性?贪心删点竟能保证线段不相交……暑期每日一题,带你领略模型转化的惊艳瞬间。

2026 暑期每日一题

6月25日 P4643 [国家集训队] 阿狸和桃子的游戏

观察到题目要求的是得分之差,如果我们将边权均分给两个点,会惊喜的发现对答案没有任何影响,之后就贪心即可。

代码:

C++
#include<bits/stdc++.h>
using namespace std;

const int N=1e5+5;
int n,m,ans;
int a[N];

int main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++) cin>>a[i],a[i]<<=1;
    for(int i=1;i<=m;i++){
        int u,v,w;
        cin>>u>>v>>w;
        a[u]+=w,a[v]+=w;
    }
    sort(a+1,a+1+n);
    for(int i=1;i<=n;i++) ans+=pow(-1,i)*a[i];
    cout<<(ans>>1);
    return 0;
}

6月26日 P15410 「TBOI Round 1」Niton & Matrix

会发现一个点无论怎么变只可能出现在 $(i,j),(n-i+1,j),(i,m-j+1),(n-i+1,m-j+1)$ 这四个位置,并且无论怎么取反都不会影响它们的异或和,所以直接根据这个性质判断即可。

代码:

C++
#include<bits/stdc++.h>
using namespace std;

const int N=1e5+5;
int n,m;
vector<int> a[N],b[N];

void solve(){
    int y=1;
    for(int i=0;i<N;i++) a[i].clear(),b[i].clear();
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        a[i].push_back(0);
        for(int j=1;j<=m;j++){
            int x;
            cin>>x;
            a[i].push_back(x);
        }
    }
    for(int i=1;i<=n;i++){
        b[i].push_back(0);
        for(int j=1;j<=m;j++){
            int x;
            cin>>x;
            b[i].push_back(x);
        }
    }
    for(int i=1;i<=(n>>1);i++) for(int j=1;j<=(m>>1);j++)
        if(a[i][j]^a[n-i+1][j]^a[i][m-j+1]^a[n-i+1][m-j+1]!=b[i][j]^b[n-i+1][j]^b[i][m-j+1]^b[n-i+1][m-j+1])
            y=0;
    cout<<(y?"Yes\n":"No\n");
    return;

}

signed main(){
    int T;
    cin>>T;
    while(T--) solve();
    return 0;
}

6月30日 P14511 [NFLSPC #8] 轨道交通

假设最优策略下交点个数为 0。

当我们只管每个点的 $x$ 坐标,将所有点排序后选最前面两个颜色相等的点,然后把前面的点全部删掉,这样每个颜色不被配对但被删掉的点最多有 $n-1$ 个,而每个颜色又有 $n+1$ 个点,剩下两个点刚好可以配对,所以按照这样的策略一定能找到最优解。

注意纵坐标并不能忽略,我们在删除之前的点时没有删除与当前点横坐标相等的所有点,所以在横坐标相同的情况下还需要按照纵坐标排序以下,这样彻底规避了线段有交的情况。

代码:

C++
#include<bits/stdc++.h>
using namespace std;

const int N=1e3+5;
int n,T;
struct node{
	int col,x,y,id;
	bool operator<(const node &a)const{
		if(x!=a.x) return x<a.x;
        return y<a.y;
	}
};
deque<node> a;
int ans[N][2],tag[N];

void solve(){
	a.clear();
	memset(tag,0,sizeof tag);
	memset(ans,0,sizeof ans);
	cin>>n;
	for(int i=1;i<=n;i++) for(int j=1;j<=n+1;j++){
		int x,y;
		cin>>x>>y;
		a.push_back({i,x,y,j});
	}
	sort(a.begin(),a.end());
	for(int i=1;i<=n;i++){
		map<int,int> mp;
		while(a.size()){
			auto[col,x,y,id]=a.front();
			a.pop_front();
			if(mp[col]==0) mp[col]=id;
			else if(!tag[col]){
				ans[col][0]=mp[col],ans[col][1]=id;
				sort(ans[col],ans[col]+2);
				tag[col]=1;
				break;
			}
		}
	}
	for(int i=1;i<=n;i++) cout<<ans[i][0]<<" "<<ans[i][1]<<"\n";
}

int main(){
	cin>>T;
	while(T--) solve();
	return 0;
}

7月1日 P15061 琥峪枫

不难发现,可以用 $n$ 个次数求出:

i=1nask(i,1)=3i=1nfi2ififroot\sum_{i=1}^{n} ask(i,1) = 3\sum_{i=1}^{n}f_i - 2\sum_{i\in 叶子节点} f_i-f_{root}

再使用 $n-1$ 次 $ask(i,h+1)$ 得到根节点以及根节点的两个儿子,那 $2f_{root}=dis(lson,1)+dis(rson,1)-dis(root,2)$,叶子节点的权值和根据定义也很好求。

代码:

C++
#include<bits/stdc++.h>
using namespace std;

const int N=5e5+5;
long long a[N];

long long ask(int u,int d);

long long solve(int subtask,int h){
    int n=(1<<h)-1,rt=-1;
    vector<int>b;
    long long ans=0,c0=0,c1=0;
    for(int i=1;i<=n;i++) ans+=a[i]=ask(i,1);
    for(int i=1;i<n;i++) if(!ask(i,h+1)) b.push_back(i);
    if(b.size()<3) b.push_back(n);
    for(int i=0;i<3;i++){
        long long x=ask(b[i],h);
        if(!x) rt=b[i];
        else c0+=a[b[i]],c1+=x;
    }
    return (ans+(c1<<1)+(c0-ask(rt,2))/2)/3;
}

7月2日 P15303 『NFC-OI R1』序列陆

先看特殊情况:

1. $c=1$

直接询问 $1$ 的数量即可。

2. $c=2$

先询问 $1$ 的数量,再二分找到最后一个 $ask(1,i)=0$ 的位置,询问次数 $1+log_2(10^5)=18$,刚好。

3. $c=0$

先询问 $1$ 的数量,记为 $s1$。

令 $s2=ask(n-s1+1,n)$,若 $s1=s2$,说明所有的 $1$ 都在最后,直接输出即可。

否则序列应该长下面这个样子:(图中 $c$ 与输入的 $c$ 不一致)

再令 $s3=ask(n-s2+1,n)$,

若 $s3=s2$

说明 $s2,s3$ 的位置关系应该如下图:

这时图中的 $d=s2$,$b=s1-s2$,再和上面一样二分找到最后一个 $ask(1,i)=0$ 的位置即可。

若 $s3\not = s2$

$s2,s3$ 的位置关系大致如下图:

由上图可知,$c=s2-s3$,那么 $a=n-s1-c$,那就知道了第一个 $1$ 的位置,从第一个 $1$ 的位置开始二分,找到最后一个满足 $ask(pos+1,pos+mid)=mid$ 的位置,就相当于找到了 $b$,那就做出来了。

最后最多询问 $3+log_2(10^5)=20$,符合要求。

代码:

C++
#include<bits/stdc++.h>
using namespace std;

int n,k,c,x;

int ask(int l,int r){
    int xx;
    cout<<"? "<<l<<" "<<r<<endl;
    cin>>xx;
    return xx;
}

signed main(){
    cin>>n>>k>>c;
    if(c==1){
        x=ask(1,n);
        cout<<"! ";
        for(int i=1;i<=n-x;i++) cout<<"0 ";
        for(int i=1;i<=x;i++) cout<<"1 ";
        cout<<endl;
        return 0;
    }
    else if(c==2){
        int l=1,r=n;
        while(l<r){
            int mid=(l+r)>>1;
            x=ask(1,mid);
            if(x>0) r=mid;
            else l=mid+1;
        }
        int pos=l;
        x=ask(1,n);
        cout<<"! ";
        for(int i=1;i<pos;i++) cout<<"0 ";
        for(int i=1;i<=x;i++) cout<<"1 ";
        for(int i=pos+x;i<=n;i++) cout<<"0 ";
        cout<<endl;
        return 0;
    }
    int a,b;
    a=ask(1,n);
    b=ask(n-a+1,n);
    if(a==b){
        cout<<"! ";
        for(int i=1;i<=n-a;i++) cout<<"0 ";
        for(int i=1;i<=a;i++) cout<<"1 ";
        cout<<endl;
        return 0;
    }
    c=ask(n-b+1,n);
    if(b==c){
        int l=1,r=n;
        while(l<r){
            int mid=(l+r)>>1;
            x=ask(1,mid);
            if(x>0) r=mid;
            else l=mid+1;
        }
        int pos=l;
        cout<<"! ";
        for(int i=1;i<=pos-1;i++) cout<<"0 ";
        for(int i=1;i<=a-b;i++) cout<<"1 ";
        for(int i=1;i<=n-a-pos+1;i++) cout<<"0 ";
        for(int i=1;i<=b;i++) cout<<"1 ";
        cout<<endl;
        return 0;
    }
    else{
        int zero=a-b,pos=n-a-zero;
        int l=1,r=a;
        while(l<r){
            int mid=(l+r+1)>>1;
            x=ask(pos+1,pos+mid);
            if(x==mid) l=mid;
            else r=mid-1;
        }
        cout<<"! ";
        for(int i=1;i<=pos;i++) cout<<"0 ";
        for(int i=1;i<=l;i++) cout<<"1 ";
        for(int i=1;i<=zero;i++) cout<<"0 ";
        for(int i=1;i<=a-l;i++) cout<<"1 ";
        cout<<endl;
        return 0;
    }
    return 0;
}