5/12
不好,说不清这是哪种题,可能是思维(?技巧(?经验(?题,赛时我会手推但是不会写代码,反正写着不顺。
已过非太水的D
//https://ac.nowcoder.com/acm/contest/46810/D
//只考虑深度_小典
#include <bits/stdc++.h>
using namespace std;
#define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
typedef long long ll;
#define int ll
#define pb push_back
#define m_p make_pair
#define mem(a,b) memset(a,b,sizeof a)
#define pii pair<int,int>
#define inf 0x3f3f3f3f3f3f3f3f
const int N = 3e5 + 50;
int f[N],arr[N],num[N];
vector<int> a[N];
int find(int n){
int cnt=1;int t=n;
while(t!=1){
cnt++;t=f[t];
}
return cnt;
}
void bfs(){
queue<pii>q;
q.push(m_p(1,1));
while(!q.empty()){
pii t=q.front();q.pop();
num[t.first]=t.second;
for(int i=0;i<a[t.first].size();++i){
q.push(m_p(a[t.first][i],t.second+1));
}
}
}
void work() {
int n;cin>>n;
for(int i=2;i<=n;++i){
int b;
cin>>b;
a[b].pb(i);
}for(int i=1;i<=n;++i){
cin>>arr[i];
}sort(arr+1,arr+1+n);
bfs();
sort(num+1,num+1+n);
int ans=0;
for(int i=1;i<=n;++i){
ans+=num[i]*arr[i];
}
cout<<ans<<'\n';
}
signed main() {
io;
work();
return 0;
}
H.
H-Tokitsukaze and K-Sequence_2023牛客寒假算法基础集训营2 (nowcoder.com)
思路:
赛时用的upper_bound过了,但觉得有必要记一记典。
单独考虑算贡献。
#include <bits/stdc++.h>
using namespace std;
#define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
typedef long long ll;
#define int ll
#define pb push_back
#define m_p make_pair
#define mem(a,b) memset(a,b,sizeof a)
#define inf 0x3f3f3f3f3f3f3f3f
const int N = 3e5 + 50;
int sum,cnt[N];
void work() {
mem(cnt,0);
multiset<int>v;//如果这里用vector之后sort的话会t耶
int n;cin>>n;
for(int i=1;i<=n;++i){
int a;
cin>>a;cnt[a]++;//记录出现次数的数组
}
for(int i=1;i<=1e5+10;++i){
if(cnt[i]) v.insert(cnt[i]);//排set
}
int ge=v.size();//还有多少种数在k个分组中无法做到每组至多有一个
sum=0;//可以做到上述的数的个数(注意不是种数),这是每个k的一部分贡献
for(int i=1;i<=n;++i){
while(ge>0&&(*v.begin())<=i){//当k增大到有一种数可以满足了
sum+=(*v.begin()); //该数的贡献已经确定不会再加了
v.erase(v.begin()); //把该数从做不到的set中删掉
ge--;
}
cout<<sum+ge*(i-1)<<'\n'; //答案是确定的贡献+做不到的数*(k-1)个数组里都仅有一个该数
}
}
signed main() {
io;
int t;
cin >> t;
while (t--) {
work();
}
return 0;
}
L.
L-Tokitsukaze and Three Integers_2023牛客寒假算法基础集训营2 (nowcoder.com)
思路:
预处理出arr[ i ]*arr[ j ] %p的值,枚举k,另开一个数组去重数组d记录当结果为x时有多少对ij是包含i的。
#include <bits/stdc++.h>
using namespace std;
#define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
typedef long long ll;
#define int ll
#define pb push_back
#define m_p make_pair
#define mem(a,b) memset(a,b,sizeof a)
#define inf 0x3f3f3f3f3f3f3f3f
const int N = 5e3 + 50;
int arr[N],c[N],d[N][N];
void work() {
int n,p;cin>>n>>p;
for(int i=1;i<=n;++i){
cin>>arr[i];
arr[i]%=p;
}
for(int i=1;i<=n;++i){
for(int j=1;j<=n;++j){
if(i==j) continue;//特别注意
c[arr[i]*arr[j]%p]++;
d[i][arr[i]*arr[j]%p]+=2;//因为我们的ij都是从1->n的循环所以记录每一个i*2即可
}
}
for(int i=0;i<p;++i){
int ans=0;
for(int j=1;j<=n;++j){
int tar=(i-arr[j]+p)%p; //arr[i]*arr[j]%p
ans+=c[tar]-d[j][tar]; //ans+=总对数-含k的对数
}
cout<<ans<<" \n"[i==p-1];
}
}
signed main() {
io;
work();
return 0;
}
C.
C-Tokitsukaze and a+b=n (hard)_2023牛客寒假算法基础集训营2 (nowcoder.com)
思路:
同L,所有i!=j的区间结果=所有结果-i==j的结果。
用一个差分数组记录每一个区间,前缀和一下得到num数组(每个数字出现次数),对于a+b=n的所有结果可以用num[a]*num[n-a]+num[b]*num[n-b]得到,然后减掉同一个区间里出现的所有结果,即区间取交。
(不是说不赋初值的时候自动赋0吗,怎么一直有4%的数据过不去memset了一下就过了。
------->2023.2.2更新,群友说开在全局的数组一定是0,局部的不一定。
#include <bits/stdc++.h>
using namespace std;
#define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
typedef long long ll;
#define int ll
#define pb push_back
#define m_p make_pair
#define mod 998244353
#define mem(a,b) memset(a,b,sizeof a)
#define pii pair<int,int>
#define inf 0x3f3f3f3f3f3f3f3f
const int N = 4e5 + 50;
void work() {
int n,m;
cin>>n>>m;
vector<pii> a(m+1);
int c[N];mem(c,0);
for(int i=1;i<=m;++i){
cin>>a[i].first>>a[i].second;
c[a[i].first]++;
c[a[i].second+1]--;
}
for(int i=1;i<=N;++i){
c[i]=c[i]+c[i-1];
c[i]%=mod;
}
int ans=0;
for(int i=1;i<=n;++i){
ans+=c[i]*c[n-i]%mod; //求所有情况
ans%=mod;
}
for(int i=1;i<=m;++i){
int l=a[i].first,r=a[i].second;
int t=n-l,w=n-r;
ans-=max(0ll,min(t,r)-max(w,l)+1);//区间取交
(ans+=mod)%=mod;
}
cout<<ans<<'\n';
}
signed main() {
io;
work();
return 0;
}
K.
K-Tokitsukaze and Synthesis and Traits_2023牛客寒假算法基础集训营2 (nowcoder.com)
思路:
妙妙题。第一次输入记录度,之后按度的大小建立有向图,这样在之后遍历的时候就可以避免很多出度的菊花图(?炸鸡块哥哥这么说的)浪费时间。
可以去了解一下三元环计数。
(试了一下stl果然比直接开数组慢)
#include <bits/stdc++.h>
using namespace std;
#define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
typedef long long ll;
#define int ll
#define pb push_back
#define m_p make_pair
#define mod 998244353
#define mem(a,b) memset(a,b,sizeof a)
#define pii pair<int,int>
#define inf 0x3f3f3f3f3f3f3f3f
const int N = 3e5 + 50;
void work() {
int n,m,q;cin>>n>>m>>q;
vector<int>d[N];
vector<int>a(m+1),b(m+1),c(n+1);
for(int i=1;i<=m;++i){
cin>>a[i]>>b[i];
c[a[i]]++;c[b[i]]++;
}
for(int i=1;i<=m;++i){
if(c[a[i]]>c[b[i]]) d[b[i]].pb(a[i]);
else d[a[i]].pb(b[i]);
}
vector<int>now(n+1);
for(int i=1;i<=q;++i){
int ans=0;
int k;cin>>k;
vector<int>t(k+1);
for(int j=1;j<=k;++j){
cin>>t[j];now[t[j]]=i;
}
for(int j=1;j<=k;++j){
for(auto to:d[t[j]]){
if(now[to]==i) ans++;
}
}
cout<<ans<<'\n';
}
}
signed main() {
io;
work();
return 0;
}
F.
F-Tokitsukaze and Gold Coins (easy)_2023牛客寒假算法基础集训营2 (nowcoder.com)
思路:
算是有一些限制的bfs/dfs(?,看到有个写不知道算不算双向bfs的,位运算很妙,感觉上次和位运算智慧博弈了一晚上之后对位运算没那么抵触了。
(谔谔,就样例而言,手动清空数组比memset快了2倍,只清空需要的部分比清空全部快了4倍,int比bool快了1ms。
#include <bits/stdc++.h>
using namespace std;
#define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
typedef long long ll;
//#define int ll
#define pb push_back
#define m_p make_pair
#define mod 998244353
//#define mem(a,b) memset(a,b,sizeof a)
#define pii pair<int,int>
#define inf 0x3f3f3f3f3f3f3f3f
const int N = 5e5 + 50;
int mp[4][N], f[4][N];
int vis[4][N];
int n, k;
void work() {
cin >> n >> k;
for(int i=1;i<=3;i++){
memset(f[i],0,n*4+50);
memset(mp[i],0,n*4+50);
memset(vis[i],0,n*4+50);
}
while (k--) {
int a, b;
cin >> b >> a;
vis[a][b] ^= 1;
}
int ans = 0;
mp[3][n] = 1;
for (int i = n; i >= 1; --i) {
for (int j = 3; j > 0; --j) {
if (vis[j][i])
continue;
mp[j][i] |= (mp[j + 1][i] | mp[j][i + 1]);
}
}
mp[1][1] = 0;
f[1][1] = 1;
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= 3; ++j) {
if (vis[j][i])
continue;
f[j][i] |= (f[j - 1][i] | f[j][i - 1]);
if (f[j][i] && mp[j][i])
ans++;
}
}
cout << ans << '\n';
}
int main() {
io;
int t;
cin >> t;
while (t--) {
work();
}
return 0;
}
E.
E-Tokitsukaze and Function_2023牛客寒假算法基础集训营2 (nowcoder.com)
思路:
数学+二分。
由均值不等式可知sqrt(n)时一定是最小值,但是加下取整后,该最小值就不一定唯一了。而且sqrt直接下取整还需要判断一下是下取整最小还是上取整最小。之后二分,我们可以知道对于所有下取整式,至少小于等于原式且结果相差不过1,于是可以想出下取整式的图像一定是如下平台。
那我们需要找的一定是与sqrt同水平线上的最小x,所以二分成立条件应该只有==。
(jls:那就直接二分不要动脑子! :)懂了,二分是无脑算法bushi)
红线横坐标应该能接上的,画的很丑意会即可。
#include <bits/stdc++.h>
using namespace std;
#define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
typedef long long ll;
#define int ll
#define pb push_back
#define m_p make_pair
#define mod 998244353
#define mem(a,b) memset(a,b,sizeof a)
#define pii pair<int,int>
#define inf 0x3f3f3f3f3f3f3f3f
const int N = 3e5 + 50;
void work() {
int n,l,r;cin>>n>>l>>r;
int t=sqrt(n);
int p=l;
int mn=(n/l+l-1);
if(mn>n/r+r-1){
p=r;
mn=n/r+r-1;
}
if(t<=r&&t>=l&&mn>=n/t+t-1){
p=t;
mn=n/t+t-1;
}
t++;
if(t<=r&&t>=l&&mn>n/t+t-1){
p=t;
mn=n/t+t-1;
}
int lo=l,hi=p;
while(lo<hi){
int mid=(lo+hi)/2;
if(n/mid+mid-1==mn) hi=mid;
else lo=mid+1;
}
cout<<lo<<'\n';
}
signed main() {
io;
int t;
cin >> t;
while (t--) {
work();
}
return 0;
}
I.
I-Tokitsukaze and Musynx_2023牛客寒假算法基础集训营2 (nowcoder.com)
思路:
一些聪明的枚举。(jls:这个简单!
对于每一个x[ i ],都存在4个h可以让该值发生改变,我们只需要枚举这4*n个h即可。开一个vector<pii>或者set(但是set去重会不会有影响呀没试不知道),记录<h,v[ j+1 ]-v[ j ] >,之后只需要一边枚举一边记录最大答案即可。
注意开闭区间鸭!
- jls这样枚举的正确性说明:因为对pii的sort是先按first再按second,所以对于不同的h一定是从小到大排的,可以保证在当前res+的时候,小于h的所有影响已经全部加上了;对于相同的h,理论上每一种ans的变化都必须使res加全h的所有变化再去比较,但是由于h相同对second排序,若之前second之和大于res了,那么之后的所有second必为正,于是直到h结束的每一次ans都会被res更新;若之前的second之和小于res,那么在此之前ans不可能被res更新,如果之后res又增大了,我们也可以保证res已经加过了每一个比当前second小的影响。
(vector<pii>的时候用push_back(make_pair(a,b))或者emplace(a,b)
jls牛蛙!他的代码清晰明了高端优雅。从明天起培养书写好习惯,拒绝电子答辩。
#include <bits/stdc++.h>
using namespace std;
#define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
typedef long long ll;
#define int ll
#define pb push_back
#define m_p make_pair
#define mod 998244353
#define mem(a,b) memset(a,b,sizeof a)
#define pii pair<int,int>
#define inf 0x3f3f3f3f3f3f3f3f
const int N = 3e5 + 50;
void work() {
int n;cin>>n;
int x[n+1];
for(int i=1;i<=n;++i){
cin>>x[i];
}
int a,b,c,d;cin>>a>>b>>c>>d;
d++;//开区间
int v[6];
for(int i=1;i<=5;++i){
cin>>v[i];
}
int res=0;int ans=-inf;
vector<pii> el;
for(int i=1;i<=n;++i){
res+=v[1];
el.emplace_back(a-x[i],v[2]-v[1]);
el.emplace_back(b-x[i],v[3]-v[2]);
el.emplace_back(c-x[i],v[4]-v[3]);
el.emplace_back(d-x[i],v[5]-v[4]);
}
ans=res;
sort(el.begin(),el.end());
for(auto[a,x]:el){
res+=x;
ans=max(ans,res);
}
cout<<ans<<'\n';
}
signed main() {
io;
int t;
cin >> t;
while (t--) {
work();
}
return 0;
}
小小总结
线段树要不算了,鸽鸽。最近好摆,快救救我。
火车头越来越长了。
我真的很不会那种言语难以说明白的题(比如出现次数个数),可能就是脑子线程不够用理不清楚,总会有怪里怪气但是代码又写不了的手推方法,大概是做题太少见得不多。
二分/各种变化的搜索不熟&&思维不好。感觉有的方法想不到是因为没见过,有的是因为对题意||算法理解不到位,有的就是因为脑子不好。(要是脑子好+理解透没见过也能想出来。
卡常真是一门艺术。
jiang宝的本质是复读智能人吗可爱可爱可爱。