CF 535C Tavas and Karafs
题目大意:给你一个无限长的等差数列,每次给一个起点L,可以吃T轮,每可以把M个数吃一口(-1),问最大的R使得区间[L,R]被吃完
思路:显然给定一个区间[L,R]后很容易贪心出能不能被吃完,并且发现该性质有单调性也就是如果[L,R]可以吃完,那[L,R-1]也可以,且存在最大的R使得[L,R+1]不满足条件,于是二分一下就可以了
1 #include<iostream> 2 #include<cstdio> 3 #define ll long long 4 using namespace std; 5 ll a,b,n,l,t,m,rr,lr; 6 int check(ll k) 7 { 8 ll f = a + (l - 1) * b,rrr = a + (k - 1) * b,su=((f+rrr)*(k-l+1))>>1; 9 if(t*m<su || rrr > t)return 0; 10 else return 1; 11 } 12 int main() 13 { 14 cin>>a>>b>>n; 15 for(int i=1;i<=n;i++) 16 { 17 cin>>l>>t>>m; 18 if(a+b*(l-1) > t) 19 { 20 puts("-1"); 21 continue; 22 } 23 lr = l; 24 rr = l + t+10; 25 while(lr<rr) 26 { 27 int mid = (lr + rr+ 1) >> 1; 28 if(check(mid))lr=mid;else rr=mid-1; 29 } 30 cout<<lr<<endl; 31 } 32 return 0; 33 }
CF 533B. Work Group
题目大意:给出一棵有根树,每个点都有点权,从中选出一些点,使得每个选中的点的子树中都有偶数个选中点,求选出点最大的点权
思路:很明显的树dp,显然选中当前点的话,那它的子数选中的点只能选择偶数,如果不选当前的点,子树可以选偶数或奇数个点,那接下来问题就转化成了对于一个点,它的子树可以选奇数个点或偶数的点,以及一个权值,如何选才能使权值最大并且点数为奇数/偶数。这个显然是一个一维的DP,于是问题得到解决
唔,细节处理还是很重要的,由于一开始什么0个点都没选的时候点数是偶数,因此需要一开始将奇数转移的地方封印起来,直到选了奇数个后才能用odd转移
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #define maxn 400000 5 using namespace std; 6 long long now=0,head[maxn],next[maxn],point[maxn],p[maxn]; 7 long long dp[400000][3],root; 8 void add(int x,int y) 9 { 10 next[++now]=head[x]; 11 head[x]=now; 12 point[now]=y; 13 } 14 long long dfs(long long k,long long par) 15 { 16 if(dp[k][par]!=-1)return dp[k][par]; 17 long long even = 0 ,odd = -1; 18 for(int i=head[k];i;i=next[i]) 19 { 20 long long pp = point[i],u=dfs(pp,0) , v = dfs(pp,1); 21 long long tempe = even , tempo = odd; 22 even += u;odd += u; 23 if(v != -1) 24 { 25 if(tempo != -1)even = max(even , tempo + v); 26 odd = max(odd , tempe +v); 27 } 28 } 29 dp[k][0]=even; 30 dp[k][1] = max(even + p[k], odd); 31 return dp[k][par]; 32 } 33 int main() 34 { 35 long long n,x; 36 memset(dp,-1,sizeof(dp)); 37 cin>>n; 38 for(int i=1;i<=n;i++) 39 { 40 cin>>x>>p[i]; 41 if(x==-1)root=i; 42 else 43 { 44 add(x,i); 45 } 46 } 47 cout<<max(dfs(root,0),dfs(root,1))<<endl; 48 return 0; 49 }
GCJ 2015 A. Counter Culture
题目大意:给你一个数,从0出发每次可以对这个数进行两个操作:1.将这个数反转:也就是1234变成4321 2.将这个数加一
问从0出发最少多少次操作能变成这个数
思路:这题做的,快苦了。。。首先20一下只能一步一步加,以上呢?先凑出来到这个位数至少需要多少步,比如到达100需要多少步,1000需要多少步,然后将末尾一半的数凑成高位,反转,再将后一半的数凑成高位就能过大数据了,可惜的是闹抽两句话写反了结果fst!!!!
1 #include<iostream> 2 #include<fstream> 3 #include<cstring> 4 #include<algorithm> 5 #define maxn 500 6 using namespace std; 7 ifstream fin("a.in"); 8 ofstream fout("a.out"); 9 //#define fin cin 10 //#define fout cout 11 int deg[100]={0,1,10,29,138,337,1436,3435,14434,34433,144432,344431,1444430,3444429,14444428}; 12 int main() 13 { 14 long long t,n,cas=0; 15 fin>>t; 16 while(t--) 17 { 18 long long ans=0; 19 fin>>n; 20 int flag=0; 21 if(n<=20) 22 { 23 fout<<"Case #"<<++cas<<": "<<n<<endl; 24 } 25 else 26 { 27 long long temp=n; 28 if(n%10==0) 29 { 30 n--; 31 flag=1; 32 } 33 int a[100],h=0; 34 while(n) 35 { 36 a[++h]=n%10; 37 n/=10; 38 } 39 int u=h>>1; 40 ans += deg[h]; 41 long long v=0; 42 for(int i=h-u+1;i<=h;i++) 43 { 44 v = v * 10 + a[i]; 45 } 46 ans += v; 47 v=0; 48 for(int i=h-u;i>=1;i--) 49 { 50 v= v*10 +a[i]; 51 } 52 ans+=v; 53 v=1; 54 for(int i=1;i<h;i++)v*=10; 55 if(flag)ans++; 56 fout<<"Case #"<<++cas<<": "<<min(ans,temp-v+deg[h])<<endl; 57 } 58 } 59 return 0; 60 }
Codeforces Round #300 B. Quasi Binary
题目大意:给出一个数(0到1e6),问你它能是最少多少quasibinary的和,一个十进制数是quasibinary当且仅当它十进制数位上只有0和1
思路:显然这个范围内quasibinary不多,先预处理出来,然后就是一个裸到不行的DP,记录路径就可以
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #define maxn 2000000 6 using namespace std; 7 int a[maxn],h=0,ini[maxn],dp[maxn],last[maxn],ans[maxn]; 8 void add(int temp[]) 9 { 10 int sum=0; 11 for(int i=1;i<=7;i++)sum = sum*10 + temp[i]; 12 ini[++h]=sum; 13 } 14 void dfs(int k) 15 { 16 if(k>7){add(a);return ;} 17 a[k]=1;dfs(k+1); 18 a[k]=0;dfs(k+1); 19 } 20 int dfs2(int k) 21 { 22 if(dp[k]!=-1)return dp[k]; 23 int ret=0x3f3f3f3f; 24 for(int i=1;i<=h;i++) 25 { 26 if(k-ini[i]>=0) 27 { 28 int u=dfs2(k-ini[i]); 29 if(u < ret) 30 { 31 ret = u; 32 last[k] = i; 33 } 34 } 35 else break; 36 } 37 return dp[k]=ret+1; 38 } 39 int main() 40 { 41 int n; 42 scanf("%d",&n); 43 dfs(1); 44 memset(dp,-1,sizeof(dp)); 45 dp[0]=0; 46 h--; 47 sort(ini+1,ini+1+h); 48 printf("%d ",dfs2(n)); 49 int v=0; 50 while(last[n]!=0) 51 { 52 ans[++v]=ini[last[n]]; 53 n-=ini[last[n]]; 54 } 55 sort(ans+1,ans+1+v); 56 for(int i=1;i<=v;i++)printf("%d ",ans[i]); 57 return 0; 58 }
Codeforces Round #298 (Div. 2) F. Simplified Nonogram
题目大意:有一个N×M的网格(n<=5 m<=20)每个格子可能黑可能白,告诉你每行没列有多少段连续的黑格(比如0是黑格1是白格 1100110101等于3)让你给出一种可行的棋盘方案
思路:先想朴素的办法,发现N比较小,于是可以枚举每列的状态,dfs更新行的状态即可,这样妥妥的超时
然后想办法记录,发现到第I列的时候,如果此时每行的状态是一样的(也就是到这一列的时候每行有多少连续的黑格子)那么两种状态是一样一样的,因此如果之前访问过了这种状态下次再访问到,就可以PASS了,这样剪枝了以后就可以了,注意压常数,一开始用vector在109组数据无情的挂了
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<vector> 5 #define maxn 35 6 using namespace std; 7 int a[6],b[maxn],c[maxn],n,m,g[11][50]; 8 int ans[maxn][maxn],next[maxn]; 9 bool visit[21][11][11][11][11][11][33]; 10 bool dfs(int k,int last) 11 { 12 if(k>m) 13 { 14 for(int i=1;i<=n;i++)if(a[i]!=0)return false; 15 return true; 16 } 17 int temp[6]; 18 if(visit[k][a[1]][a[2]][a[3]][a[4]][a[5]][last])return false; 19 for(int i=1;i<=g[b[k]][0];i++) 20 { 21 int u=g[b[k]][i],flag2=0; 22 memcpy(temp,a,sizeof(a)); 23 for(int j=0;j<n;j++) 24 { 25 if( (u&(1<<j))!=0 && (last& (1<<j))==0 ) 26 { 27 a[j+1]--; 28 if(a[j+1]<0){flag2=1;break;} 29 } 30 } 31 if(flag2) 32 { 33 memcpy(a,temp,sizeof(a)); 34 continue; 35 } 36 if(dfs(k+1,u)) 37 { 38 next[k]=u; 39 return true; 40 } 41 memcpy(a,temp,sizeof(a)); 42 } 43 visit[k][a[1]][a[2]][a[3]][a[4]][a[5]][last]=1; 44 return false; 45 } 46 int main() 47 { 48 scanf("%d%d",&n,&m); 49 for(int i=1;i<=n;i++)scanf("%d",&a[i]); 50 for(int i=1;i<=m;i++)scanf("%d",&b[i]); 51 for(int s=0;s<=((1<<n)-1);s++) 52 { 53 int v=0; 54 for(int i=1;i<=n;i++) 55 { 56 if(((s>>(i-1)) & 3) == 1)v++; 57 } 58 g[v][++g[v][0]]=s; 59 } 60 dfs(1,0); 61 for(int i=1;i<=m;i++) 62 for(int j=1;j<=n;j++) 63 { 64 if(next[i]&(1<<(j-1)))ans[j][i]=1;else ans[j][i]=0; 65 } 66 for(int i=1;i<=n;i++) 67 for(int j=1;j<=m;j++) 68 { 69 if(ans[i][j])printf("*");else printf("."); 70 } 71 puts(""); 72 return 0; 73 }
Codeforces Round #290 (Div. 2) D. Fox And Jumping
题目大意:给定N个数,有选它价值和能跳的步数,问要能跳到任意一个点所花费的最小价值
思路:显然如果有一种方案能拼到1,那么区间上所有的点都能跳了,假设选择lp,lq,lr...ls能凑成1,也就是lp*x+lq*y+...+ls*z=1;方程有整数解当且仅当(lp,lq,...ls)=1,因此dp[i]表示凑成gcd[i]最小的花费是多少,然后就可以转移了,发现gcd是递减的,所以能开一个队列乱搞
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<map> 5 #include<queue> 6 #define maxn 1000 7 using namespace std; 8 map<int,int>h; 9 queue<int>q; 10 int l[maxn],c[maxn]; 11 int main() 12 { 13 int n; 14 scanf("%d",&n); 15 for(int i=1;i<=n;i++)scanf("%d",&l[i]); 16 for(int i=1;i<=n;i++) 17 { 18 scanf("%d",&c[i]); 19 if(h[l[i]]==0) 20 { 21 q.push(l[i]); 22 h[l[i]]=c[i]; 23 } 24 else 25 { 26 h[l[i]]=min(h[l[i]],c[i]); 27 } 28 } 29 while(!q.empty()) 30 { 31 int u=q.front(); 32 q.pop(); 33 for(int i=1;i<=n;i++) 34 { 35 int v=__gcd(l[i],u),w=h[u]+h[l[i]]; 36 if(h[v]>w || h[v]==0) 37 { 38 h[v]=w; 39 q.push(v); 40 } 41 } 42 } 43 if(h[1]==0) 44 { 45 puts("-1"); 46 } 47 else printf("%d ",h[1]); 48 return 0; 49 }
Codeforces Round #300 E. Demiurges Play Again
题目大意:两个无聊的人在看两个无聊的人玩一个游戏,有一棵树,假设有M个叶子节点,每个叶子节点都有一个1到M的权值,从根节点开始,两人轮流操作,每轮操作将当前点移动到它的一个儿子上,先手的目的是使得最后到叶子节点的权值尽量大,,直到移动到叶子节点不能动为止,两个人毋庸置疑都采取最优的操作,两个无聊的人可以预先将叶子节点的权值重排,问重排以后能让分数最大是多少,最小多少?
思路:假设权值全部排好了,那么两个人玩游戏得到的分数也就确定了(他们都是聪明人),只要用树DP第一个人走的时候取子树能取到的最大值,第二个人取最小值,那假设没排好,dp[i][1]表示以i为根的子树先手先走能取到第K大的数(因为分数已经确定了,所以对于一个子树能取到第K大也就确定了),dp[i][0]表示以i为根的子树后手先走能取到第K大的数,然后先手是聪明人,一定取名次最小的那个子树,那我们顺水人情,把最大值放到这个子树就行了,如果后手走,肯定取名次最大的,容易发现最后取到的就是所有子树名词的和,然后就可以DP了
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #define maxn 400009 5 using namespace std; 6 int next[maxn],head[maxn],point[maxn],now=0,leaf; 7 bool visit[maxn]; 8 void add(int x,int y) 9 { 10 next[++now]=head[x]; 11 head[x]=now; 12 point[now]=y; 13 } 14 int dfs(int k,bool f) 15 { 16 visit[k]=1; 17 int minx=0x3f3f3f3f,ret=0,flag=1; 18 for(int i=head[k];i;i=next[i])if(!visit[point[i]]) 19 { 20 flag=0; 21 int u=point[i]; 22 if(f) 23 { 24 minx=min(minx,dfs(u,f^1)); 25 } 26 else 27 { 28 ret+=dfs(u,f^1); 29 } 30 } 31 if(flag) 32 { 33 leaf++; 34 return 1; 35 } 36 if(f)return minx;else return ret; 37 } 38 int main() 39 { 40 int n,x,y; 41 scanf("%d",&n); 42 for(int i=1;i<n;i++) 43 { 44 scanf("%d%d",&x,&y); 45 add(x,y); 46 add(y,x); 47 } 48 int u=dfs(1,1); 49 leaf=0; 50 memset(visit,0,sizeof(visit)); 51 int v = dfs(1,0); 52 printf("%d %d ",leaf-u+1,v); 53 return 0; 54 }
Codeforces Round #301 (Div. 2) D. Bad Luck Island
题目大意:一个岛上有三种人剪刀人石头人和布人,每次任意两个人相遇,相遇后如果不同种人按剪刀石头布的规则一个人把另一个人消灭,问最后只剩下剪刀人,只剩下石头人,只剩下布人的概率
思路:dp[r][s][p]表示三类人分别有r s p个人 ,最后只剩下r 的概率 ,(其中r能消灭s s能消灭p p能消灭r)
然后容易发现两个人的种类相同是无所谓的,所以两个人遇到不同的总数是r*s+r*p+s*p,然后就可以根据剪刀石头布的规则转移了
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 using namespace std; 5 double dp[109][109][109]; 6 bool visit[109][109][109]; 7 double dfs(int r,int s,int p) 8 { 9 if(r==0)return 0.0; 10 if(s==0 && p==0)return 1.0; 11 if(visit[r][s][p])return dp[r][s][p]; 12 visit[r][s][p]=1; 13 int tot = r*s + r*p + s*p; 14 dp[r][s][p]=0; 15 if(s)dp[r][s][p]+=1.0*dfs(r,s-1,p) * (1.0 * r * s) / (1.0*tot); 16 if(r)dp[r][s][p]+=1.0*dfs(r-1,s,p) * (1.0 * r * p) / (1.0*tot); 17 if(p)dp[r][s][p]+=1.0*dfs(r,s,p-1) * (1.0 * s * p) / (1.0*tot); 18 return dp[r][s][p]; 19 } 20 int main() 21 { 22 int r,s,p; 23 scanf("%d%d%d",&r,&s,&p); 24 printf("%.12f %.12f %.12f ",dfs(r,s,p),dfs(s,p,r),dfs(p,r,s)); 25 return 0; 26 }
Codeforces Round #296 (Div. 2) D. Clique Problem
题目大意:在数轴上有N个点,每个点有一个权重,两个点之间有边当且仅当它们的权重之和小于等于它们的距离,问这个图的最大团的数量
思路:好神的题,显然||xi- xj| <= wi +wj可以变成
xi- xj <= wi +wj || xi- xj <= wi +wj
其中两个不等式最后都可以化简成 xi - wi <= xj +wj
所以对一个点只需要记录Xi+ Wi 和Xi- Wi的信息就可以
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #define maxn 200000 5 #define inf 0x3f3f3f3f 6 using namespace std; 7 struct T 8 { 9 int x; 10 int y; 11 }a[maxn]; 12 int cmp(T x,T y) 13 { 14 return x.x < y.x; 15 } 16 int x,w; 17 int 18 main() 19 { 20 int n; 21 scanf("%d",&n); 22 for(int i=1;i<=n;i++) 23 { 24 scanf("%d%d",&x,&w); 25 a[i].x = x + w; 26 a[i].y = x - w; 27 } 28 sort(a+1, a+1+n,cmp); 29 int maxx = -inf,ans = 0; 30 for(int i=1;i<=n;i++) 31 { 32 if(a[i].y >= maxx) 33 { 34 maxx = a[i].x; 35 ans++; 36 } 37 } 38 printf ("%d ",ans); 39 return 0; 40 }