DP cf题
D. Weight the Tree
给一棵树,每个点有权重,如果一个点的权重等于它周围直接相连点的权重,就称为好点,求权重的分配方案,使得好点个数最大化,且权重值之和最小,
做法:设\(dp[x][1/0]\)表示当前点是否作为好点时最大好点数,最小权值,我们可以知道,如果一个点为好点,他周围的点就不会是好点,权重就是1,那么他的权重最小就为度数 ,所以$$dp[x][1]= \sum_{v,son[x]} dp[v][0], dp[x][0] = \sum_{v,son[x]} min(dp[v][0],dp[v][1]) $$
点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int maxn=2e5+10;
class node{
public:
int x,y;
node(int _x=0,int _y=0){
x=_x;
y=_y;
}
node operator + (const node &rhs){
node ans;
ans.x=x+rhs.x;
ans.y=y+rhs.y;
return ans;
}
bool operator < (const node &rhs){
if(x==rhs.x) return y<rhs.y;
else return x>rhs.x;
}
};
vector<int>G[maxn];
node dp[maxn][2];
int du[maxn];
int ans[maxn];
void dfs1(int x,int f){
for (auto v:G[x]){
if(v==f) continue;
dfs1(v,x);
dp[x][1]=dp[x][1]+dp[v][0];
if(dp[v][0]<dp[v][1]){
dp[x][0]=dp[x][0]+dp[v][0];
}else dp[x][0]=dp[x][0]+dp[v][1];
}
dp[x][1]=dp[x][1]+node(1,du[x]);
dp[x][0]=dp[x][0]+node(0,1);
}
void dfs2(int x,int f,int num){
if(num==0) ans[x]=1;
else ans[x]=du[x];
for (auto v:G[x]){
if(v==f) continue;
if(num==1){
dfs2(v,x,0);
}else {
if(dp[v][0]<dp[v][1]){
dfs2(v,x,0);
}else dfs2(v,x,1);
}
}
}
int main(){
int n;
scanf("%d",&n);
for (int i=1;i<n;i++){
int u,v;
scanf("%d%d",&u,&v);
G[u].push_back(v);
G[v].push_back(u);
du[v]++;
du[u]++;
}
if(n==2){
printf("2 2\n1 1\n");
return 0;
}
dfs1(1,1);
if(dp[1][0]<dp[1][1]) cout<<dp[1][0].x<<" "<<dp[1][0].y,dfs2(1,1,0);
else cout<<dp[1][1].x<<" "<<dp[1][1].y,dfs2(1,1,1);
puts("");
for (int i=1;i<=n;i++) printf("%d ",ans[i]);
return 0;
}
Codeforces Round #772 (Div. 2)
D. Infinite Set
题意:给出n个不同数\(a_i\)和p (集合S中所有元素均小于2^p),维护集合S S,该集合里的元素x满足:
\(n,p<=10^5\) ,$ a_i<=10^9 $
-
\(a_i\)都在集合里
-
\(y=2*x+1\) ,x在集合S中
-
\(y=4*x\)
做法:因为2^p很大,所以考虑到可以按位来做,设dp[i],表示\(2^i<=x<=2^{i+1}\)情况下答案为多少,那么对于\(y=2*x+1\),\(y=4*x\),就等价于dp[i]=dp[i-1]+dp[i-2],对于每个数,统计其对应dp[i]的答案即可,在统计的时候为了避免答案重复,需要把a[i]中能够互相表出的数删掉,留下不能由其它数表出的数,但这道题卡场,不要直接用map查和做,常数很大,用set记录是否有解就好
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define Max(a,b) (a)>(b)?(a):(b)
typedef long long ll;
const int maxn=2e5+10;
const ll mod=1e9+7;
ll dp[maxn];
map<int,bool>mp;
int main(){
int n,p;
scanf("%d%d",&n,&p);
for (int i=1;i<=n;i++){
int x;
scanf("%d",&x);
if(mp.find(x)==mp.end())
mp[x]=1;
}
ll ans=0;
set<int>s;
for (auto it=mp.begin();it!=mp.end();it++){
int x=it->first;
bool flag=1;
while(x>0){
if(s.find(x)!=s.end()) {flag=0;}
if(x&1){
x>>=1;
}else if(x%4) break;
else{
x>>=2;
}
}
if(flag)
s.insert(it->first);
}
auto it=s.begin();
int num=0,sum=1;
for (;it!=s.end();it++){
// cout<<*it<<endl;
int x=*it;
while(x>=sum*2){
sum*=2;
num++;
}
dp[num]++;
}
dp[1]+=dp[0];
for (int j=2;j<p;j++){
//printf("%lld ",dp[j]);
dp[j]=(dp[j-1]+dp[j-2]+dp[j])%mod;
}
for (int j=0;j<p;j++){
ans=(ans+dp[j])%mod;
}
printf("%lld\n",ans%mod);
return 0;
}
Bayan 2015 Contest Warm UpD. CGCDSSQ
做法,固定右端点,去更新做端点以获得区间,我们知道区间gcd最多只有Log(maxnum)个不同的值,所以处理复杂度其实就是nlognlogc,因为再套上了一个map
点击查看代码
#include <bits/stdc++.h>
using namespace std;
int a[100010];
typedef long long ll;
int gcd(int x,int y){
if(y==0) return x;
return gcd(y,x%y);
}
int main(){
#ifdef lmj_debug
freopen("1.in","r",stdin);
#endif
int n;
scanf("%d",&n);
for (int i=1;i<=n;i++) scanf("%d",&a[i]);
vector< pair<int,int> >g[2];
map<int,ll>ans;
int l1=0,l2=0;
for (int i=1;i<=n;i++){
l1=l2;
l2=l1^1;
g[l2].clear();
g[l2].push_back(make_pair(i,a[i]));
int last=a[i];
for (auto x:g[l1]){
int p=gcd(x.second,a[i]);
if(p==last){
g[l2][g[l2].size()-1]=make_pair(x.first,p);
}else {
g[l2].push_back(make_pair(x.first,p));
last=p;
}
}
last=i;
for (auto x:g[l2]){
int now=x.first;
// cout<<x.second<<" "<<x.first<<" ";
ans[x.second]+=(ll)(last-now+1);
last=now-1;
}
//cout<<endl;
}
int q;
cin>>q;
while(q--){
int x;
cin>>x;
cout<<ans[x]<<endl;
}
return 0;
}
Codeforces Round #766 (Div. 2) D. Not Adding
考虑到几个数的gcd不会大于这几个数中的任意一个,我们就从1-max扫一遍,看这些数哪个可以被表出来,而且每个数都是x的倍数,那么就类似做埃式筛法,最后复杂度是\(O(n)+alog(a)\)
点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e6+10;
int vis[maxn];
int main(){
#ifdef lmj_debug
freopen("1.in","r",stdin);
#endif
int n,maxx=0,x;
cin>>n;
for (int i=1;i<=n;i++){
scanf("%d",&x);
vis[x]++;
maxx=max(maxx,x);
}
int ans=0;
for (int i=1;i<=maxx;i++){
int pre=0;
if(vis[i]) continue;
for (int j=i;j<=maxn;j+=i){
if(!vis[j]) continue;
pre=__gcd(pre,j);
if(pre==i){
ans++;
break;
}
}
}
cout<<ans<<endl;
return 0;
}
Codeforces Round #764 (Div. 3) E. Masha-forgetful
考虑到长度大于3的每个串可以由长度为2和3构成,那么我们就直接把每个串拆成长度2和3的小串,然后dp就好,设\(dp[i][0/1]\)表示到第i个数采用长度为,\(2/3\)的串,转移就好
点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int maxn=1010;
class my{
public:
int l,r,id;
my(int _l=0,int _r=0,int _id=0){
l=_l,r=_r,id=_id;
}
bool operator<(const my &rhs)const{
return id<rhs.id;
}
};
map<string,my>mp;
int dp[maxn][2];
int main(){
#ifdef lmj_debug
freopen("1.in","r",stdin);
#endif
int T;
scanf("%d",&T);
while(T--){
mp.clear();
int n,m;
getchar();
getchar();
cin>>n>>m;
string s;
for (int i=0;i<=m;i++) dp[i][0]=dp[i][1]=0;
for (int k=1;k<=n;k++){
cin>>s;
for(int i=0;i<m;i++){
string p1="",p2="";
if(i+1<m){
for (int o=i;o<=i+1;o++)
p1+=s[o];
my x(i+1,i+2,k);
if(mp.find(p1)==mp.end()) mp.insert(pair<string, my>(p1, x));
}
if(i+2<m){
for (int o=i;o<=i+2;o++)
p2+=s[o];
my x(i+1,i+3,k);
if(mp.find(p2)==mp.end()) mp.insert(pair<string, my>(p2, x));
}
}
}
char S[1010];
S[0]='0';
scanf("%s",S+1);
s=S;
dp[0][0]=1;
dp[0][1]=1;
for (int i=2;i<=m;i++){
for (int j=0;j<2;j++){
if(i>=2 && dp[i-2][j]){
string p="";
for (int o=i-1;o<=i;o++)
p+=s[o];
if(mp.find(p)!=mp.end()) dp[i][0]=1;
}
if(i>=3 && dp[i-3][j]){
string p="";
for (int o=i-2;o<=i;o++)
p+=s[o];
if(mp.find(p)!=mp.end()) dp[i][1]=1;
}
}
}
if(!dp[m][1] && !dp[m][0]){puts("-1");continue;}
vector<my>ans;
int pre=m;
while(pre>=2){
if(dp[pre][0]){
string p="";
for (int o=pre-1;o<=pre;o++)
p+=s[o];
ans.push_back(mp[p]);pre-=2;
}else {
string p="";
for (int o=pre-2;o<=pre;o++)
p+=s[o];
ans.push_back(mp[p]);pre-=3;
}
}
reverse(ans.begin(),ans.end());
cout<<ans.size()<<endl;
for (auto x:ans){
printf("%d %d %d\n",x.l,x.r,x.id);
}
}
return 0;
}