T1:科技树(TechTree)
题目大意
给你一棵树,每一次你可以点一个没点过的点,问你任意一个点最早什么时候点?最晚什么时候点?
解法
题目都已经说了,是树,那么最早就是它前面所有的前置科技都已经点完之后再点它,即 \(dfn_x\)。最晚的即是以它为根的这棵子树不点,其他的都点。因为以他为根的这棵子树里面必须要点他,而其他的点对他没有任何要求,即 \(n-siz_x\) 。
代码:
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=1e5+5;
vector<int> g[maxn];
int dep[maxn],siz[maxn];
void dfs(int x,int fa)
{siz[x]=1,dep[x]=dep[fa]+1;for(auto &to:g[x]){if(to==fa) continue;dfs(to,x);siz[x]+=siz[to];}
}
signed main()
{freopen("TechTree.in","r",stdin);freopen("TechTree.out","w",stdout);ios::sync_with_stdio(0);cin.tie(0); cout.tie(0);int t;cin>>t;while(t--){int n;cin>>n;for(int i=1;i<n;i++){int u,v;cin>>u>>v;g[u].push_back(v);g[v].push_back(u);}dfs(1,0);for(int i=1;i<=n;i++) cout<<dep[i]<<' '<<n-siz[i]+1<<'\n';for(int i=1;i<=n;i++) g[i].clear();}return 0;
}
T2:B. 增益选择(Buff)
题目大意
给你一个长度为数 \(n\) 的数组 \(a\),问你任意选两个点,这两个点之比积更大,有多少种方案?
解法
我们想一想有什么情况两个数之和比积更大?
- 负数乘正数
- 正数乘0
- 正数乘1(含1)
所以我们只需要统统计正数,负数,0和1的个数即可。
代码:
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=1e6+5;
signed main()
{freopen("Buff.in","r",stdin);freopen("Buff.out","w",stdout);ios::sync_with_stdio(0);cin.tie(0); cout.tie(0);int t;cin>>t;while(t--){int n,cntz=0,cntf=0,cnt1=0,cnt0=0;cin>>n;for(int i=1;i<=n;i++){int x;cin>>x;if(x>0) cntz++;if(x<0) cntf++;if(x==1) cnt1++;if(x==0) cnt0++;}int ans=0;ans+=cntz*cntf;//正数*负数ans+=cnt1*(cntz-cnt1);//正数*1(不含1)ans+=cnt1*(cnt1-1)/2;//1*1ans+=cnt0*cntz;//0*正数cout<<ans<<endl;}return 0;
}
T3:C. 树状数组(FenwickTree)
题目大意
给你一棵树状数组 \(c\) 的某位是否为0,请问至少操作多少次才能变成 \(c\)(具体数值自定,只需满足是否为0即可)?
解法
根据树状数组的原理,我们可以知道:
$ c_x=\sum^x_{i=x-lowbit(x)+1} a_i$
其中\(lowbit(x)\)为\(x\)的因数中最大的2的幂次方。如 \(lowbit(12)=4\)。
设 \(lowbit(x)=2^d\) 则 \(c_x\) 可以进一步表示成:
\(c_x=a_x+\sum^{d-1}_{i=0} c_{x-2^i}\)
比如 \(c_{16}=a_{16}+c_{15}+c_{14}+c_{12}+c_{8}\)。
至此不难发现,每一个位置只跟 \(a_c\) 和 \(c_{x-2^i}\) 有关,且每个位置至多操作一次。可以得出以下结论:
- 如果 \(c_x\) 为0且 \(c_{x-2^i}\) 中仅有一个非零,那么 \(a_x\) 必须被操作一次。
- 如果 \(c_x\) 不为0且 \(c_{x-2^i}\) 中只有零,那么 \(a_x\) 必须被操作一次。
从前往后扫一遍,每个位置最多来一遍时间复杂度为 \(O(n)\)。
代码:
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=2e5+5;
int a[maxn];
signed main()
{freopen("FenwickTree.in","r",stdin);freopen("FenwickTree.out","w",stdout);ios::sync_with_stdio(0);cin.tie(0); cout.tie(0);int t;cin>>t;while(t--){int n,ans=0;cin>>n;for(int i=1;i<=n;i++){char c;cin>>c;a[i]=c-'0';}for(int i=1;i<=n;i++){int cnt=0,d=i&(-i);for(int j=1;j<d;j*=2) cnt+=a[i-j];if(a[i]==0&&cnt==1) ans++;if(a[i]!=0&&cnt==0) ans++;}cout<<ans<<'\n';}return 0;
}
附:T4:D. 部队编号(Code) 50pts
题目大意
给你一个字符串 \(s\) 问里面有多少个“114514”串?
“114514” 定义:其可以分成6段非空的子段,其中第一段、第二段和第五段是相同的,第三段和第六段是相同的;
50pts解法
不难发现,一共就只有3种子串,枚举三者的长度与起点,判断一下即可。好悬啊
代码:
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=15;
signed main()
{freopen("Code.in","r",stdin);freopen("Code.out","w",stdout);ios::sync_with_stdio(0);cin.tie(0); cout.tie(0);int t;cin>>t;while(t--){string s;cin>>s;int n=s.size(),ans=0;s=' '+s;for(int i=1;i<=n;i++){for(int j=1;j+i+j+i+i<=n;j++){for(int k=1;i+j+k+i+j+i<=n;k++){for(int l=1;l+i+j+k+i+j+i-1<=n;l++){string s1=s.substr(l,i),s2=s.substr(i+l,i),s3=s.substr(i+i+l,j)//且由于其中有一个子串没有任何要求,所以就五个。,s5=s.substr(i+i+j+k+l,i),s6=s.substr(i+i+j+k+i+l,j);if(s1==s2&&s2==s5&&s3==s6) ans++;}}}}cout<<ans<<endl;}return 0;
}