Codeforces Round #530 (Div. 2)F. Cookies
https://codeforces.com/contest/1099/problem/F
题意:
给你一棵树,每个节点上有若干饼干,并且给出每个节点上吃一块饼干需要多少时间,同时给出走过一条边所需时间。
总时限为 TT,两个人轮流进行操作:
Mitya从当前节点选择一个子节点向下走,或者直接结束游戏并往根回动吃饼干;
Vasya割断当前节点到其某个子节点的边,或者什么都不做。
问Mitya可以吃到的最多的多少块饼干。
思路参考:Dream_maker_yk的博客
线段树维护前缀和。
首先我们以时间ti为坐标向线段树中插入节点。保存两个值,把子树吃光所用的总时间sum,子树中的cookie总数cnt。
然后根据剩余时间val查询,如果左子树的sum比val小,那么说明左子树可以吃光,那么查询结果就是:
cnt左子树 + 对右子树查询val - sum左子树
这样我们就可以用logn的时间实现贪心策略了。
卡在线段树 。
线段树的下表是时间戳。
就是 吃一块所需要的时间是i 的饼的数量和总消耗时间。
然后 每次就是维护线段树,维护的是dfs一条链的不同饼的和。
#include <bits/stdc++.h>
using namespace std;
#define mo 100005
long long num_x[mo],time_y[mo];
void read(long long a[],long long n)
{
for(long long i=1;i<=n;i++)
cin>>a[i];
}
#define mk make_pair
vector< pair<long long ,long long > > tree[mo];
long long s_tree[mo*40],s_trees[mo*40];
void update(long long pos,long long l,long long r,long long x,long long t)
{
//cout<<pos<<' '<<l<<' '<<r<<endl;
s_tree[pos]+=t*x;
s_trees[pos]+=x;
if(l==r) return ;
long long mid=(l+r)/2;
if(t<=mid)
update(pos*2,l,mid,x,t);
else
update(pos*2+1,mid+1,r,x,t);
}
long long query(long long pos,long long l,long long r,long long val)
{
//cout<<"asd asd "<<s_tree[pos]<<' '<<s_trees[pos]<<' '<<l<<' '<<r<<' '<<val<<endl;
if(l==r)
return min(s_trees[pos],val/l);
long long mid=(l+r)/2;
if(s_tree[pos*2]<=val)
return s_trees[pos*2]+query(pos*2+1,mid+1,r,val-s_tree[pos*2]);
else
return query(pos*2,l,mid,val);
}
long long dfs(long long node,long long times)
{
// cout<<node<<' '<<times<<endl;
update(1,1,1e6,num_x[node],time_y[node]);
long long ans=query(1,1,1e6,times);
long long son,t,f1=0,f2=0,num;
//cout<<node<<' '<<ans<<' '<<' '<<f1<<' '<<f2<<' '<<times<<' '<<num_x[node]<<' '<<time_y[node]<<endl;
for(long long i=0;i<tree[node].size();i++)
{
son=tree[node][i].first;
t=tree[node][i].second;
if(times<2*t) continue ;
num=dfs(son,times-2*t);
if(num>f1)
{
f2=f1;
f1=num;
}
else if(num>f2)
{
f2=num;
}
}
update(1,1,1e6,-num_x[node],time_y[node]);
if(node==1) return max(f1,ans);
return max(f2,ans);
}
int main()
{
long long n,k;
while(cin>>n>>k)
{
memset(num_x,0,sizeof(num_x));
memset(time_y,0,sizeof(time_y));
read(num_x,n);
read(time_y,n);
//cout<<"asd asd "<<endl;
long long x,y;
for(long long i=2;i<=n;i++)
{
cin>>x>>y;
tree[x].push_back(mk(i,y));
}
cout<<dfs(1,k)<<endl;
}
}
F. MST Unification
https://codeforces.com/contest/1108/problem/F
题解
https://www.cnblogs.com/shuaihui520/p/10320158.html
#include<bits/stdc++.h>
using namespace std ;
const int maxn = 2e5+10;
int n,m,fa[maxn];
struct no{
int u,v,w;
}e[maxn];
bool cmp(no a , no b)
{
return a.w<b.w;
}
void init(){
for(int i=1 ; i<=n ; i++)
fa[i]=i;
}
int Find(int x)
{
return x==fa[x] ? x:fa[x]=Find(fa[x]);
}
void Merge(int x, int y)
{
int fx = Find(x), fy = Find(y);
if(fx != fy)
fa[x] = fy;
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
init();
for(int i=0 ; i<m ; i++)
scanf("%d%d%d",&e[i].u , &e[i].v , &e[i].w);
sort(e , e+m , cmp);
int ans=0;
for(int i=0 ; i<m ; )
{
int num=0;
int j=i;
while(e[i].w ==e[j].w) ///找到权值相同的尾部坐标
j++;
for(int k=i ; k<j ; k++) ///i到j都是边权最小而且都是相同的边
{
int fx = Find(e[k].u) , fy=Find(e[k].v);
if(fx!=fy)///统计可以选择的合法边的数量
num++;
}
///注意,我们还得判断合法边里的冲突边 ,才是我们想要的答案
for(int k=i ; k<j ; k++)
{
int fx=Find(e[k].u) , fy=Find(e[k].v);
if(fx!=fy)///从合法边中减去非冲突边(即可以被选入到同一个方案里,不互相冲突的边)
Merge(fx,fy) , num--;
}
i=j;
ans+=num;
}
printf("%d\n",ans);
}
return 0;
}
E. Sonya and Matrix Beauty
https://codeforces.com/contest/1080/problem/E
做法第一步 字符串 哈希 各种姿势的哈希都行 我是用的1007进制 长无符号整形
第二步 异或前缀和 快速判断字符串是否符合回文串要求
第三步 枚举矩阵的两边 l r 将一整列放入马拉车算法(manachar) 跑有多少回文串
ans+=p[i]/2
p[i] 代表这个为中心的字符串总长度为p[i] 能产生p[i]/2个回文字符串
#include<bits/stdc++.h>
using namespace std;
#define ull unsigned long long
#define mo 520
ull hash_str[mo][mo];
ull base=1007,hash_n[mo];
void hashnum()
{
// cout<<"asd asd "<<endl;
hash_n[0]=1;
for(int i=1;i<26;i++)
{
hash_n[i]=hash_n[i-1]*base;
//cout<<hash_n[i]<<' ';
}
// cout<<endl;
}
void HashStr(string &s,ull a[])
{
ull ans=0;
for(int i=0;s[i];i++)
{
ans=ans+hash_n[s[i]-'a'];
a[i]=ans;
// cout<<ans<<' ';
}
//cout<<endl;
}
int sum[mo][mo];
bool check (int i,int l,int r)
{
ull x=sum[i][r]^sum[i][l-1];
return x==0||(!(x-(x&-x)));
}
vector<ull> manc;
int n,m;
long long p[mo];
long long ans=0;
void manacher()
{
long long id=0,mid=0;
for(int i=1;i<=2*n;i++)
{
if(manc[i]!=511)
{
//p[i]=mx>=i?min (p[id*2-i],mx-i):1;
if(i<id)
{
p[i]=min(p[2*mid-i],id-i);
}
else p[i]=1;
while(manc[i+p[i]]==manc[i-p[i]]&&manc[i+p[i]]!=511&&i-p[i]>0) p[i]++;
if(p[i]+i>id)
{
id=p[i]+i;
mid=i;
}
ans+=p[i]/2;
}
}
}
int main()
{
hashnum();
while(cin>>n>>m)
{
string s;
for(int i=0;i<n;i++)
{
cin>>s;
HashStr(s,hash_str[i]);//诡异hash
sum[i][0]=(1<<(s[0]-'a'+1));
for(int j=0;j<m;j++)
{
sum[i][j]=sum[i][j-1]^((1<<(s[j]-'a'+1)));//异或前缀和
}
}
for(int i=0;i<m;i++)
{
for(int j=i;j<m;j++)
{
manc.clear();
manc.push_back(511);
for(int k=0;k<n;k++)
{
manc.push_back(0);
if( check(k,i,j) )
{
// cout<<"s sd "<<k<<' '<<i<<' '<<j<<endl;
manc.push_back(hash_str[k][j]-hash_str[k][i-1]);
}
else
{
manc.push_back(511);
}
}
manc.push_back(0);
// cout<<"l r = "<<i<<' '<<j<<endl;
// for(int k=0;k<manc.size();k++) cout<<manc[k]<<' ';cout<<endl;
manacher();
// cout<<"xxxxxxx "<<ans<<endl;
}
}
cout<<ans<<endl;
}
}