10.9 做题笔记
T1#
非常水,处理处前驱后继即可。
代码:
#include<bits/stdc++.h>
#define dd double
#define ld long double
#define ll long long
#define uint unsigned int
#define ull unsigned long long
#define N 500010
#define M number
using namespace std;
const int INF=0x3f3f3f3f;
template<typename T> inline void read(T &x) {
x=0; int f=1;
char c=getchar();
for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
for(;isdigit(c);c=getchar()) x=x*10+c-'0';
x*=f;
}
template<typename T> inline T Min(T a,T b){return a<b?a:b;}
int t,n,l[N],r[N];
ll ans;
char s[N];
int main(){
// freopen("my.in","r",stdin);
// freopen("my.out","w",stdout);
read(t);
for(int i=1;i<=t;i++){
ans=0;
read(n);scanf("%s",s+1);
for(int i=0;i<=n+1;i++) l[i]=r[i]=INF;
for(int i=1;i<=n;i++){
if(s[i]=='1') l[i]=0;
else l[i]=l[i-1]+1;
}
for(int i=n;i>=1;i--){
if(s[i]=='1') r[i]=0;
else r[i]=r[i+1]+1;
}
for(int i=1;i<=n;i++){
ans+=1ll*Min(l[i],r[i]);
// printf("i:%d l:%d r:%d\n",i,l[i],r[i]);
}
printf("Case #%d: %lld",i,ans);if(i!=t) puts("");
}
}
T2#
这个构造比较巧妙,用到了二进制下的构造,没做出来说明我还是对进制下构造不够熟悉。
容易发现最长链不会超过 ,所以可以按照以前题目的套路来做,不过不同的是不能再用拓扑序了。
考虑二进制,设 为 最高二进制位。
对于任意一条极长链,我们发现相邻的两个数的最高二进制位必然不相同,所以我们可以把最高二进制位当做拓扑序,然后按照 个分, 个分,分别分成小组和大组。因为在最劣情况下( 的幂 )这种构造仍然有效,所以这个题目就做完了。
代码:
#include<bits/stdc++.h>
#define dd double
#define ld long double
#define ll long long
#define uint unsigned int
#define ull unsigned long long
#define N 1010
#define M number
using namespace std;
const int INF=0x3f3f3f3f;
template<typename T> inline void read(T &x) {
x=0; int f=1;
char c=getchar();
for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
for(;isdigit(c);c=getchar()) x=x*10+c-'0';
x*=f;
}
int f[N],n,ans[N][N];
ll x[N];
inline int F(ll x){
if(!x) return 1;
int cnt=0;while(x){x>>=1;cnt++;}return cnt;
}
int main(){
read(n);
for(int i=1;i<=n;i++) read(x[i]);
for(int i=1;i<=n;i++) f[i]=F(x[i]);
for(int i=1;i<=n;i++){
for(int j=i+1;j<=n;j++){
if(f[i]/4==f[j]/4) ans[i][j]=1;
else if(f[i]/16==f[j]/16) ans[i][j]=2;
else ans[i][j]=3;
}
}
for(int i=1;i<=n-1;i++){
for(int j=1;j<=i;j++){
printf("%d ",ans[j][i+1]);
}
puts("");
}
return 0;
}
T3#
这个题就差一点就能想出来。做题还是太少。
首先,不难发现的是如果不连通一定不行。
否则,最终状态是确定的,我们可以得到最终状态。
那么我们考虑从最终状态如何到达初始状态。
不难发现,这就是一个最长公共子序列问题。
注意是环,且翻转同构。
因为两边都是 的阶乘,所以可以转化成最长上升子序列。
代码:
#include<bits/stdc++.h>
#define dd double
#define ld long double
#define ll long long
#define uint unsigned int
#define ull unsigned long long
#define N 1010
#define M number
using namespace std;
const int INF=0x3f3f3f3f;
template<typename T> inline void read(T &x) {
x=0; int f=1;
char c=getchar();
for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
for(;isdigit(c);c=getchar()) x=x*10+c-'0';
x*=f;
}
template<typename T> inline T Max(T a,T b){return a<b?b:a;}
template<typename T> inline T Min(T a,T b){return a<b?a:b;}
struct edge{
int to,next;
inline void Init(int to_,int ne_){
to=to_;next=ne_;
}
}li[N<<1];
int head[N],tail;
inline void Add(int from,int to){
li[++tail].Init(to,head[from]);
head[from]=tail;
}
int n,b[N],bt,c[2][N],a[N];
bool vis[N];
inline void Dfs(int k){
vis[k]=1;b[++bt]=k;
for(int x=head[k];x;x=li[x].next){
int to=li[x].to;
if(vis[to]) continue;
Dfs(to);
}
}
int f[N],rk[N],ans=INF;
//返回 a 和 c[id] 的 LIS
inline int GetLIS(int id){
int len=1;
for(int i=1;i<=n;i++) a[i]=b[i];
for(int i=1;i<=n;i++) rk[c[id][i]]=i;
for(int i=1;i<=n;i++) a[i]=rk[a[i]];
f[len]=a[1];
for(int i=2;i<=n;i++){
if(a[i]>f[len]) f[++len]=a[i];
else{
int w=lower_bound(f+1,f+len+1,a[i])-f;
f[w]=a[i];
}
}
return len;
}
inline void Turn(){
int now=b[1];
for(int i=2;i<=n;i++) b[i-1]=b[i];
b[n]=now;
}
inline void Clear(){
ans=INF;bt=0;
for(int i=1;i<=n;i++) head[i]=0;
for(int i=1;i<=n;i++) vis[i]=0;
tail=0;
}
int main(){
// freopen("my.in","r",stdin);
// freopen("my.out","w",stdout);
while(scanf("%d",&n)!=EOF&&n){
for(int i=1;i<=n;i++){
int a,b;read(a);read(b);
Add(i,a);Add(i,b);
}
for(int i=1;i<=n;i++){
c[0][i]=i;c[1][i]=n-i+1;
}
Dfs(1);
if(bt!=n){
puts("Not solvable.");Clear();
continue;
}
puts("Knot solvable.");
for(int i=1;i<=n;i++){
int now1=GetLIS(0);
int now2=GetLIS(1);
ans=Min(ans,Min(n-now1,n-now2));
Turn();
}
printf("%d\n",ans);
Clear();
}
return 0;
}
T4#
不难想到的是我们枚举两条边,同时维护 。能够拿到 分。不过这个不能再继续优化了。
我们考虑另一种做法,也就是说我们改为枚举点,枚举某个点就相当于断开其与父亲的连边。
设 表示节点 的子树大小,这里我们默认以 为根。
如果 为 的祖先,那么三个连通块大小就是 。
否则,三个连通块大小就是 。
我们枚举 ,考虑寻找 。通过计算得知(就是把带绝对值的式子列出来然后划到数轴上)对于祖先关系, 应该是要最接近 ,而下面这个是 。
这其实我们维护所有可行 的集合。我们设法维护这个集合并放在 里面,然后在里面二分查找就可以把复杂度降至 。
我们关注一下 dfs 过程,设 集合表示已经遍历的还未回溯的点, 集合表示已经遍历且已经回溯的点。
对于一个 ,我们在回溯到 的时候在 里找对应 。这个是更新祖先关系的答案。
对于一个 ,我们在遍历到 的时候在 里找对应 。这个是更新并列关系的答案。
代码:
#include<bits/stdc++.h>
#define dd double
#define ld long double
#define ll long long
#define uint unsigned int
#define ull unsigned long long
#define N 200010
#define M number
using namespace std;
const int INF=0x3f3f3f3f;
template<typename T> inline void read(T &x) {
x=0; int f=1;
char c=getchar();
for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
for(;isdigit(c);c=getchar()) x=x*10+c-'0';
x*=f;
}
template<typename T> inline T Min(T a,T b){return a<b?a:b;}
template<typename T> inline T Max(T a,T b){return a<b?b:a;}
struct edge{
int from,to,next;
inline void Init(int fr_,int to_,int ne_){
from=fr_;to=to_;next=ne_;
}
}li[N];
int head[N],tail;
inline void Add(int from,int to){
li[++tail].Init(from,to,head[from]);
head[from]=tail;
}
int Siz[N],flag=-1,n;
int none;
inline void dfs(int k,int fa){
Siz[k]=1;
for(int x=head[k];x;x=li[x].next){
if(flag==x||x==flag+1) continue;
int to=li[x].to;assert(to!=none);
if(to==fa) continue;
dfs(to,k);Siz[k]+=Siz[to];
}
}
int ans1=INF,ans2=-INF,ans=INF;
inline void Dp(int rt,int k,int fa){
for(int x=head[k];x;x=li[x].next){
if(x==flag||x==flag+1) continue;
int to=li[x].to;
if(to==fa) continue;
int now1=Siz[rt]-Siz[to],now2=Siz[to];
if(abs(now1-now2)<abs(ans1-ans2)){
ans1=now1;ans2=now2;
}
Dp(rt,to,k);
}
}
inline void Update(int val){
ans=Min(ans,Max(ans1,Max(ans2,val))-Min(ans1,Min(ans2,val)));
}
inline void Solve(){
for(int i=1;i<=tail;i+=2){
flag=i;
none=li[i].to;dfs(li[i].from,-1);
none=li[i].from;dfs(li[i].to,-1);
ans1=INF;ans2=-INF;
Dp(li[i].from,li[i].from,-1);
Update(Siz[li[i].to]);
ans1=INF;ans2=-INF;
Dp(li[i].to,li[i].to,-1);
Update(Siz[li[i].from]);
}
}
int main(){
// freopen("my.in","r",stdin);
// freopen("my.out","w",stdout);
read(n);
for(int i=1;i<=n-1;i++){
int from,to;read(from);read(to);
Add(from,to);Add(to,from);
}
Solve();
printf("%d\n",ans);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!
· 零经验选手,Compose 一天开发一款小游戏!