2016 ACM/ICPC Asia Regional Shenyang Online
I:QSC and Master
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5900
题意:
给出n对数keyi,vali表示当前这对数的键值和权值,可以操作将连续的两个数合并,如果满足gcd(a[i],a[i+1])>1,得到的价值是两个数的权值和,每次合并两个数之后,这两个数就会消失,然后旁边的数会接上比如1 2 3 4 合并了 2 3 则 剩下1 4也可以合并
思路:区间dp
1:处理出任意区间内的所有数是否可以合并
对于当前的[l,r]区间,如果区间[l+1,r-1]可以合并,且gcd(a[l]+a[r])!=1的话,则整个区间[l,r]可以合并,价值也就是前缀和
同理处理出其他的情况 [l,r-2] [l+1,r]
2:区间dp,对于当前的l,r区间,如果可以合并,则直接加上区间和
反之,则枚举一个中间值k,找出区间内最大满足情况的值
#include<bits/stdc++.h> using namespace std; const int maxn=310; typedef long long ll; ll n; ll num[maxn],val[maxn]; ll dp[maxn][maxn]; ll out[maxn][maxn]; ll sum[maxn]; ll gcd(ll x,ll y){ return x==0?y:gcd(y%x,x); } //确定1-n的最大值 void Dp(){ memset(out,0,sizeof(out)); for(ll l=2;l<=n;l++){ for(ll i=1;i+l-1<=n;i++){ ll j=i+l-1; if(dp[i][j]==1) out[i][j]=sum[j]-sum[i-1]; else{ for(ll k=i;k<j;k++){ out[i][j]=max(out[i][j],out[i][k]+out[k+1][j]); } } } } } //判断区间是否能合并 void query(){ memset(dp,0,sizeof(dp)); for(ll i=1;i<n;i++){ if(gcd(num[i],num[i+1])!=1) dp[i][i+1]=1; } for(ll l=2;l<=n;l+=2){ for(ll i=1;i+l-1<=n;i++){ ll j=i+l-1; if(gcd(num[i],num[j])!=1&&dp[i+1][j-1]==1) dp[i][j]=1; if(gcd(num[i],num[i+1])!=1&&dp[i+2][j]==1) dp[i][j]=1; if(gcd(num[j-1],num[j])!=1&&dp[i][j-2]==1) dp[i][j]=1; } } Dp(); } int main(){ ll t; scanf("%lld",&t); while(t--){ scanf("%lld",&n); memset(sum,0,sizeof(sum)); for(ll i=1;i<=n;i++){ scanf("%lld",&num[i]); } for(ll i=1;i<=n;i++){ scanf("%lld",&val[i]); sum[i]=sum[i-1]+val[i]; } query(); cout<<out[1][n]<<endl; } }
odd-even number
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5898
题意:
给你一个区间,问你这个区间中满足连续的偶数的位数为奇数,连续的奇数的位数是偶数的个数
题解:
设dp[i][j][k][l]为考虑当前第i位,上一位的奇偶性为j,已经连续了k位,是否有前导零
然后记忆化搜就行了
#include<bits/stdc++.h> #define F(i,a,b) for(int i=a;i<=b;++i) using namespace std; typedef long long ll; int dig[20],len; ll dp[20][2][20][2]; ll dfs(int pos,int pre=0,int ln=0,bool inf=1,bool ze=1)//pre 1为奇;0为偶,ze是否有前导零 1代表有,0没有;inf判断当前位最大值 1表示最大值为dig[pos] { if(!pos)//搜索完成时,根据连续的长度和当前连续的是奇数还是偶数判断反回1还是0 { if(pre)return (ln&1)==0; else return ln&1; } if(!inf&&dp[pos][pre][ln][ze]!=-1)return dp[pos][pre][ln][ze]; int en=inf?dig[pos]:9;//当前位最大放几 //cout<<pos<<" "<<en<<endl; ll ans=0; F(i,0,en) { if(i&1)//当前数为奇数 { //cout<<i<<" "<<"?"<<endl; if(ze)//上一位为0的情况 { ans+=dfs(pos-1,1,1,inf&&i==en,0); } else if(pre==0)//上一位为偶数,因为本次是奇数,所以ln从1开始 { if(ln&1) ans+=dfs(pos-1,1,1,inf&&i==en,ze); } else if(pre==1)//上一位为奇数 { ans+=dfs(pos-1,1,ln+1,inf&&i==en,ze); } } else//偶数 { if(ze) { if(i==0)ans+=dfs(pos-1,0,0,inf&&i==en,1); else ans+=dfs(pos-1,0,1,inf&&i==en,0); } else if(pre==0)ans+=dfs(pos-1,0,ln+1,inf&&i==en,ze); else if(pre==1) { if((ln&1)==0)ans+=dfs(pos-1,0,1,inf&&i==en,ze); } } } if(!inf)dp[pos][pre][ln][ze]=ans; return ans; } int main(){ int t,ic=1;ll l,r; scanf("%d",&t); while(t--) { memset(dp,-1,sizeof(dp)); scanf("%lld%lld",&l,&r),l--; for(len=0;l;l/=10)dig[++len]=l%10;//将数转化为字符串但是逆序存放 ll tp=dfs(len); for(len=0;r;r/=10)dig[++len]=r%10; printf("Case #%d: %lld\n",ic++,dfs(len)-tp); } return 0; }
List wants to travel
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5893
题目大意:
给出一棵树上的两种操作:
1.将节点a到节点b路径上的边权全部修改为c;
2.查询节点a到节点b路径上边权的段数。
解题思路:
先进行树链剖分,将问题转化到区间上,之后统计段数是经典的线段树区间合并问题。修改操作直接修改就好了,对于查询操作,需要注意链的合并,可以先求一次a与b的LCA,之后将路径分为两段分别查询,最后看拼接处权值是否相同。
#include<bits/stdc++.h> using namespace std; #define maxn 40010 const int MAXM = 50050 ; struct Edge { int to,next; int len; }e[MAXM*2]; int first[maxn]; int top[maxn];//top[v]表示v所在的重链的顶端节点 int fa[maxn]; //父亲节点 int deep[maxn];//深度 int num[maxn];//num[v]表示以v为根的子树的节点数 int p[maxn];//p[v]表示v与其父亲节点的连边在线段树中的位置 int fp[maxn];//和p数组相反 int son[maxn];//重儿子 int pos,tot,Lc,Rc; int c[maxn],a[maxn]; void add(int u,int v,int len) { tot++; e[tot].next=first[u];e[tot].to=v;e[tot].len=len; first[u]=tot; } void dfs1(int u,int pre,int d) //第一遍dfs求出fa,deep,num,son { deep[u] = d; fa[u] = pre; num[u] = 1; for(int i = first[u];i != -1; i = e[i].next) { int v = e[i].to; if(v != pre) { a[v]=e[i].len; dfs1(v,u,d+1); num[u] += num[v]; if(son[u] == -1 || num[v] > num[son[u]]) son[u] = v; } } } void getpos(int u,int sp) //第二遍dfs求出top和p { top[u] = sp; if(son[u] != -1) { p[u] = ++pos; fp[p[u]] = u; c[pos]=a[u]; getpos(son[u],sp); } else { p[u] = ++pos; fp[p[u]] = u; c[pos]=a[u]; return; } for(int i = first[u] ; i != -1; i = e[i].next) { int v = e[i].to; if(v != son[u] && v != fa[u]) getpos(v,v); } } //线段树 struct Node { int l,r; int num; int cl,cr; int flag; }segTree[MAXM*3]; void push_up(int i) { segTree[i].cl=segTree[i<<1].cl; segTree[i].cr=segTree[i<<1|1].cr; segTree[i].num=segTree[i<<1].num+segTree[i<<1|1].num; if(segTree[i<<1].cr==segTree[i<<1|1].cl) segTree[i].num--; } void push_down(int i){ if(segTree[i].flag==1){ segTree[i<<1].flag=segTree[i<<1|1].flag=1; segTree[i<<1].cl=segTree[i<<1].cr=segTree[i<<1|1].cl=segTree[i<<1|1].cr=segTree[i].cl; segTree[i<<1].num=segTree[i<<1|1].num=1; segTree[i].flag=-1; } } void build(int i,int l,int r) { segTree[i].l = l; segTree[i].r = r; segTree[i].flag=-1; if(l == r){ segTree[i].cl=segTree[i].cr=c[l]; segTree[i].num=1; return ; } int mid = (l+r)/2; build(i<<1,l,mid); build((i<<1)|1,mid+1,r); push_up(i); } void update(int l,int r,int color,int i) { if(segTree[i].l == l && segTree[i].r == r) { segTree[i].num=1; segTree[i].flag=1; segTree[i].cl=segTree[i].cr=color; return; } push_down(i); int mid = (segTree[i].l + segTree[i].r)/2; if(r <= mid)update(l,r,color,i<<1); else if(l>mid) update(l,r,color,i<<1|1); else{ update(l,mid,color,i<<1); update(mid+1,r,color,i<<1|1); } push_up(i); } int query(int l,int r,int i,int L,int R) { if(segTree[i].l == L ) Lc=segTree[i].cl; if(segTree[i].r==R) Rc=segTree[i].cr; if(segTree[i].l==l&&segTree[i].r==r){ return segTree[i].num; } push_down(i); int mid = (segTree[i].l + segTree[i].r)/2; if(r <= mid)return query(l,r,i<<1,L,R); else if(l > mid)return query(l,r,i<<1|1,L,R); else{ int num=query(l,mid,i<<1,L,R)+query(mid+1,r,i<<1|1,L,R); if(segTree[i<<1].cr==segTree[i<<1|1].cl) num--; return num; } } int solve(int u,int v) { int f1 = top[u], f2 = top[v]; int tmp = 0; int pre1=-1,pre2=-1; while(f1 != f2) { if(deep[f1] < deep[f2]) { swap(pre1,pre2); swap(f1,f2); swap(u,v); } tmp +=query(p[f1],p[u],1,p[f1],p[u]); if(pre1==Rc) tmp--; pre1=Lc; u = fa[f1]; f1 = top[u]; } if(u!=v){ if(deep[u]<deep[v]){ swap(pre1,pre2); swap(u,v); } tmp+=query(p[son[v]],p[u],1,p[son[v]],p[u]); if(pre1!=-1 && Rc==pre1)tmp--; if(pre2!=-1 && Lc==pre2)tmp--; } else if(pre1==pre2) tmp--; return tmp; } void gengxin(int u,int v,int value) { int f1,f2; f1=top[u];f2=top[v]; while(f1!=f2){ if(deep[f1]<deep[f2]){ swap(f1,f2);swap(u,v); } update(p[f1],p[u],value,1); u=fa[f1]; f1=top[u]; } if(deep[u]<deep[v]){ swap(u,v); } if(u!=v){ update(p[son[v]],p[u],value,1); } } int main() { int i,j,n,m,u,v,f,g,h,w; char s[10]; while(scanf("%d%d",&n,&m)!=EOF) { memset(first,-1,sizeof(first)); memset(son,-1,sizeof(son)); pos=0;tot=0; for(i=1;i<=n-1;i++){ scanf("%d%d%d",&u,&v,&w); add(u,v,w);add(v,u,w); } dfs1(1,0,1); getpos(1,1); build(1,1,pos); for(i=1;i<=m;i++){ scanf("%s",s); if(s[0]=='Q'){ scanf("%d%d",&f,&g); printf("%d\n",solve(f,g)); } else{ scanf("%d%d%d",&f,&g,&h); gengxin(f,g,h); } } } return 0; }