Practice on Codeforces and Atcoder in July
题意:
定义一个矩形
- 矩形中每一个元素
都为 其中之一 - 每一个
的子矩形都必须包含三个不同的字符 - 共用一条边的两个元素不相等
给定
,限制 ,限制
求满足所有条件的矩形是否存在。
题解:
考场上想半天差分约束,哎!
我们来推一推这个题的特殊性。
我们考虑用数字
考虑设横差
则有
由性质,有
两个相等的元素与两个不等的元素做差后再对其中一个取个相反数,枚举一下几种可能,则有
列设
那么就一行的
将这个视为该行/列的标签,那么对于限制条件
我们使用并查集,就可以判断是否有矛盾,进而求解了。图论染色同样可以。
这里有几个重要的套路:
-
关于 “3” 的问题,可以往 “2” 的解决方案想。
-
具有轮换性的题目,可以令
来寻找规律。用数字替代字母,研究数字的和差关系 -
运算的性质。异同的奇妙转化。
void dfs(int u){
for(int i=head[u];i;i=nxt[i]){
int v=ver[i],w=c[u]^cost[i];
if(c[v]==-1){
c[v]=w;dfs(v);
}
else if(c[v]!=w){
tag=1;
}
}
}
int main(){
int t;cin>>t;
while(t--){
cin>>n>>m;int k;cin>>k;
for(int i=1;i<=n+m;i++)c[i]=-1,head[i]=0;
tot=1,tag=0;
for(int i=1;i<=k;i++){
int x1,y1,x2,y2;cin>>x1>>y1>>x2>>y2;
int u=min(x1,x2),v=n+min(y1,y2),w=(x1+y1==x2+y2);
add(u,v,w);add(v,u,w);
}
for(int i=1;i<=n+m;i++){
if(c[i]==-1){
c[i]=1;dfs(i);
}
}
if(tag)cout<<"No\n";
else cout<<"Yes\n";
}
}
做题的启发:
- 用数字代替字母
- 从2*2开始对元素做差寻找性质,一定要手玩,由易到难,发现性质
- 欣赏轮换性与模运算的周期性结合的美
给定正整数数组
其中
面对这种问题,我们分类讨论。
先从
显然将
证明:邻项交换法
设
邻项交换
所以这是不优的。
当
先来考虑
那么
在
但
怎么办呢?不难想到找一个值一样字典序最小的值。
找什么呢?让我们先想想它的性质。
我们考虑一位一位地将后面的值提取到前面来。
那么此时后面单调不减一定是最优决策(之一),为什么?
因为如果不单调不减就会产生代价,进而没掉。
那什么样的点可以提到前面来呢?显然是不会增大最终代价的点。
设当前已经取了
那么必定要满足:
因为这样不会破坏最终状态(最终状态即为负减负) 因为拿走之后,这里的代价不能扩大 是满足1,2的下标最小值。
当然,
由此,我们可以枚举
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define N 505050
int a[N],n,t,c;
signed main() {
ios::sync_with_stdio(false);cin>>t;
while (t--) {
cin>>n>>c;
for(int i=0;i<n;i++)cin>>a[i];
sort(a,a+n);
if(c>=0){
for(int i=0;i<n;i++)cout<<a[i]<<" ";cout<<"\n";continue;
}
reverse(a,a+n);
for(int i=0;i<n;i++){
for(int j=n-1;j>i;--j){
int tag=0;
if(j<n-1)tag+=abs(a[j+1]-a[j-1]-c)-abs(a[j+1]-a[j]-c);
if(i==0)tag+=abs(a[i]-a[j]-c);
else tag+=abs(a[i]-a[j]-c)+abs(a[j]-a[i-1]-c)-abs(a[i]-a[i-1]-c);
if(tag==abs(a[j]-a[j-1]-c)){
for(int &k=j;k>i;--k)swap(a[k],a[k-1]);
}
}
}
for(int i=0;i<n;i++)cout<<a[i]<<" ";cout<<"\n";
}
return 0;
}
算法优化:
考虑到限制条件1本质上与限制条件2脱钩,用分离限制条件原则来维护一个解的集合。
详细地说,我们维护一个集合
然后注意到限制条件1和3 本质上是
那,什么样的数据结构是可行的呢? set!
我们事先将满足条件2的点加入
注意set是不可重集合,我们可以用下标当第二维度。
#include<iostream>
#include<cstdio>
#include<set>
#include<algorithm>
using namespace std;
#define pr pair<int,int>
#define mk make_pair
#define N 505050
int a[N],b[N],c,l[N],r[N],n,m;
set<pr >t;
int main(){
int T;cin>>T;
while(T--){
cin>>n>>c;
for(int i=0;i<n;i++)cin>>a[i];
if(c>=0){
sort(a,a+n);
for(int i=0;i<n;i++)cout<<a[i]<<" ";cout<<"\n";
continue;
}
for(int i=1;i<n;i++)l[i]=i-1;l[0]=n-1;
for(int i=0;i<n-1;i++)r[i]=i+1;r[n-1]=0;
sort(a,a+n);reverse(a,a+n);
for(int i=2;i<n-1;i++)if(a[r[i]]-a[l[i]]>=c)t.insert(mk(a[i],i));
b[0]=a[0];
for(int i=1;i<n;i++){
int u=0;auto it=t.lower_bound(mk(b[i-1]+c,0));
if(it==t.end())u=r[0];
else u=(*it).second,t.erase(it);
b[i]=a[u];
r[l[u]]=r[u];l[r[u]]=l[u];
t.erase(mk(a[r[u]],r[u]));
t.erase(mk(a[l[u]],l[u]));
if(l[u]!=0&&r[l[u]]!=0&&l[l[u]]!=0&&a[r[l[u]]]-a[l[l[u]]]>=c)t.insert(mk(a[l[u]],l[u]));
if(r[u]!=0&&r[r[u]]!=0&&l[r[u]]!=0&&a[r[r[u]]]-a[l[r[u]]]>=c)t.insert(mk(a[r[u]],r[u]));
}
for(int i=0;i<n;i++)cout<<b[i]<<" ";cout<<"\n";
}
}
由于各方面原因,这里没有详细的数学证明,感兴趣的读者可以看CF原题解(反正我是看懵了)。
看到n=10,考虑状态压缩动态规划。
设
注意到我们只关心十及其以下的数字,那么人为限制
考虑计算,我们来进行分类讨论。
首先枚举自己骰子这次取那些值,设为
,那么 s_new=(s|(1<<(k-1))|(s<<k))&((1<<10)-1)
f[i][(s|(1<<(k-1))|(s<<k))&((1<<10)-1)]+=f[i-1][s]*inv_ai
即可完成转移
将
的部分交给上一个处理,多出来的一定不会对新的集合产生影响,则直接有 f[i][j]+=(ai-10)*inv_ai*f[i-1][j]
挂点:没有往状压方向考虑,事实上看到状态这么小我应该思考状压的,尤其是当出现一个比较明显的常数在10的左右的时候
启发:概率DP,如果从时间轴上看,则必然每个时间的概率之和相等,否则挂
int f[505][1<<12],n;
signed main(){
cin>>n;
f[0][0]=1;
for(int i=1;i<=n;i++){
int x,y,inv;cin>>x;inv=power(x,p-2);y=min(x,10ll);
for(int j=0;j<(1<<10);j++){
for(int k=1;k<=y;k++){
f[i][(j|(1<<(k-1))|(j<<k))&((1<<10)-1)]+=f[i-1][j]*inv%p;
f[i][(j|(1<<(k-1))|(j<<k))&((1<<10)-1)]%=p;
}
f[i][j]+=(x-y)*inv%p*f[i-1][j]%p;
f[i][j]%=p;
}
}
int ans=0;
for(int i=1<<9;i<(1<<10);i++){
ans+=f[n][i];ans%=p;
}
cout<<ans<<"\n";
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!