C Delete the Tree(结论)
给出一棵树,有两种操作:1、一种是删除一个节点及所有连向它的边;2、对于度严格为2的点,可以将它去掉,在它的子节点和父节点之间连边。目的是删除整棵树,尽量少用1操作,问最少需要几次1操作。
思路:大胆猜想!队友手摸几组发现是度为1的点,即所有的叶节点。也是可以证明的,我们缩点只能缩其中的节点,叶子结点是没有办法缩的。
AC Code:
#include <bits/stdc++.h>
typedef long long ll;
const int N=1e6+5;
int t,n;
int cnt[N];
int main(){
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
std::cin>>t;
while(t--){
std::cin>>n;
for(int i=1;i<=n;i++) cnt[i]=0;
for(int i=1;i<n;i++){
int u,v;
std::cin>>u>>v;
cnt[u]++,cnt[v]++;
}
if(n==1){
std::cout<<1<<'\n';
continue;
}
int ans=0;
for(int i=1;i<=n;i++){
if(cnt[i]==1) ans++;
}
std::cout<<ans<<'\n';
}
return 0;
}
os:辣鸡PTA!它卡memset!脑血栓几年才能想出这种怪东西啊!!!
H Step Debugging(模拟,栈)
给出了一种语言,求其中library计算了多少次。
思路:模拟。。。
AC Code:
#include <bits/stdc++.h>
typedef long long ll;
const int N=1e6+5;
const int mod=20220911;
std::string s;
int main(){
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
std::stack<ll>sta;
ll ans=0;
while(std::cin>>s){
if(s=="fin") break;
if(s=="library"){
if(sta.empty()) ans++;
else sta.top()++;
}
else if(s=="repeat") sta.push(0);
else if(s[0]>='0'&&s[0]<='9'){
ll num=stoll(s);
ll dd=sta.top();
sta.pop();
dd=(dd*num)%mod;
if(sta.empty()) ans=(ans+dd)%mod;
else sta.top()=(sta.top()+dd)%mod;
}
}
while(!sta.empty()){
ans=(ans+sta.top())%mod;
sta.pop();
}
std::cout<<ans<<'\n';
return 0;
}
os:真的,这个题真的气死我了,我们D都搞出来了这个题写不出来,写了将近四个半小时。。
D Find the Number(打表+二分;贪心)
给出一个范围,找到这个范围内任意一个,满足条件的数字:二进制中后缀0和全部1的个数相等的数字,若没有则输出-1。
思路:(1)因为数据范围是1e9,也就32位,可以直接在范围内打表,然后二分答案。赛后才想到这种“暴力”做法,感觉很妙;
(2)贪心做法。从下限开始找,如果下限数字满足要求,直接输出即可;若不满足,判断是0多还是1多的情况,若是0多则一定存在加一个1使得数字满足情况的条件(前提是这个数不超过上限);若是1多,则在最低为1的位+1,使1减少,再判断0多还是1多的情况,继续按照之前的分类操作。
AC Code:
(1)康康严格鸽的题解叭,ICPC 网络赛 D 的几种枚举方式 - 知乎 (zhihu.com)
(2)
#include <bits/stdc++.h>
typedef long long ll;
#define int long long
ll t,l,r;
int get0(ll x){
int cnt=0;
while(x%2==0) x>>=1,cnt++;
return cnt;
}
int get1(ll x){
int cnt=0;
while(x){
if(x&1) cnt++;
x>>=1;
}
return cnt;
}
int lowbit(ll x){
return x&(-x);
}
ll cal0(ll x);
ll cal1(ll x);
signed main(){
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
std::cin>>t;
while(t--){
std::cin>>l>>r;
int a=get0(l);
int b=get1(l);
if(a==b){
std::cout<<l<<'\n';
continue;
}
if(a>b) std::cout<<cal0(l)<<'\n';
else std::cout<<cal1(l)<<'\n';
}
return 0;
}
ll cal0(ll x){
int cnt0=get0(x);
int cnt1=get1(x);
x+=(1ll<<(cnt1+1));
if(x>r) return -1;
cnt0=get0(x);
cnt1=get1(x);
if(cnt0==cnt1) return x;
if(cnt0>cnt1) return cal0(x);
else return cal1(x);
}
ll cal1(ll x){
int xx=lowbit(x);
x+=xx;
if(x>r) return -1;
int cnt0=get0(x);
int cnt1=get1(x);
if(cnt0==cnt1) return x;
if(cnt0>cnt1) return cal0(x);
else return cal1(x);
}
os:这个题写的非常的顺。。。很可惜啊,可惜我们签到题没出。。。
A 01 Sequence
给出一个01序列,每次次操作可以删去一个1和它两侧的0,或者将一个位置的数字倒置,q次询问,对于给出的区间l,r,问经过最少几次倒置操作使得可以将序列删为空。
思路:有个结论,连续的一段1所需要的操作次数是1的个数/2,对于一个区间,如果t是这个区间的操作次数,那答案是max(0,len/3-t),len是区间长度。对于求贡献,即区间需要的操作数,可以用前缀和处理,每次对于一个区间找在其中最靠近两侧0的位置,这样其中的区间的贡献可以直接相减得到,其他的位置的1都是相连的,可以直接计算。
AC Code:
#include <bits/stdc++.h>
typedef long long ll;
const int N=1e6+5;
int n,q;
std::string s;
int pre[N],suf[N],dif[N];
int cal(int l,int r){
int ll=l+pre[l],rr=r-suf[r];
if(ll>=rr) return (r-l+1)>>1;
return dif[rr]-dif[ll-1]+(pre[l]+suf[r]+1)/2;
}
int main(){
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
std::cin>>n>>q;
std::cin>>s;
s=' '+s;
int last=-1;
for(int i=1;i<=n;i++){
if(s[i]=='1'){
if(last!=i-1) dif[i]=1,last=i;
else last=-1;
}
dif[i]+=dif[i-1];
}
for(int i=1;i<=n;i++){
if(s[i]=='1') suf[i]=suf[i-1]+1;
else suf[i]=0;
}
for(int i=n;i>=1;i--){
if(s[i]=='1') pre[i]=pre[i+1]+1;
else pre[i]=0;
}
while(q--){
int l,r;
std::cin>>l>>r;
int t=cal(l,r);
int len=(r-l+1)/3;
std::cout<<std::max(0,len-t)<<'\n';
}
return 0;
}
补不动啦