水题 注意进位即可
#include<bits/stdc++.h> using namespace std; typedef long long int LL; int a,b; bool pal() { if((a%10)==(b/10)&&(a/10)==(b%10))return true; else return false; } void add() { b+=1; while(b>=60)b-=60,a+=1; while(a>=24)a-=24; } int main() { char c; scanf("%d%c%d",&a,&c,&b); int ans=0; while(!pal()) { add(); ans++; } printf("%d\n",ans); return 0; }
用线段树维护是显而易见的
不过由于先添加了所有点才进行所有询问,也就是说不会动态改变数据 所以用线段树是大材小用了。
用线性的方法也可以维护 这里只给出线段树代码。
#include<bits/stdc++.h> using namespace std; typedef long long int LL; const int N=1e7,R=200000; int size[N],adn[N],n,k,q; vector<int>num; void pushdown(int cur) { adn[cur*2]+=adn[cur]; adn[cur*2+1]+=adn[cur]; adn[cur]=0; } void add(int cur,int ln,int rn,int l,int r) { if(l<=ln&&r>=rn){adn[cur]++;return ;} int mid=(ln+rn)>>1; pushdown(cur); if(l<=mid)add(cur*2,ln,mid,l,min(mid,r)); if(r>mid)add(cur*2+1,mid+1,rn,max(mid+1,l),r); } void update(int cur,int ln,int rn) { if(ln==rn) { if(adn[cur]>=k)size[cur]=1; else size[cur]=0; return ; } pushdown(cur); int mid=(ln+rn)>>1; update(cur*2,ln,mid); update(cur*2+1,mid+1,rn); size[cur]=size[cur*2]+size[cur*2+1]; } void que(int cur,int ln,int rn,int l,int r,int& anss) { if(l<=ln&&r>=rn){anss+=size[cur];return ;} int mid=(ln+rn)>>1; if(l<=mid)que(cur*2,ln,mid,l,min(mid,r),anss); if(r>mid)que(cur*2+1,mid+1,rn,max(l,mid+1),r,anss); } int main() {//freopen("t.txt","r",stdin); scanf("%d%d%d",&n,&k,&q); int l,r; for(int i=0;i<n;i++) { scanf("%d%d",&l,&r); add(1,1,R,l,r); } update(1,1,R); for(int i=0;i<q;i++) { int anss=0; scanf("%d%d",&l,&r); que(1,1,R,l,r,anss); printf("%d\n",anss); } return 0; }
很简单的贪心。。放在C的位置有点过于简单了吧(虽然我被Hack了 不过是打字失误。。mdzz)
提交前还是应该一行一行过一遍。。
#include<bits/stdc++.h> using namespace std; typedef long long int LL; const int N=200; int num[N][N]; int n,m; vector<int>co,ro; int suma,sumb; void row() { for(int i=0;i<n;i++) { int miv=num[i][0]; for(int j=1;j<m;j++) miv=min(miv,num[i][j]); for(int j=0;j<m;j++) num[i][j]-=miv; sumb+=miv*m; for(int k=0;k<miv;k++) ro.push_back(i); } } void col() { for(int i=0;i<m;i++) { int miv=num[0][i]; for(int j=1;j<n;j++) miv=min(miv,num[j][i]); for(int j=0;j<n;j++) num[j][i]-=miv; sumb+=miv*n; for(int k=0;k<miv;k++) co.push_back(i); } } int main() {//freopen("t.txt","r",stdin); scanf("%d%d",&n,&m); for(int i=0;i<n;i++) for(int j=0;j<m;j++) scanf("%d",&num[i][j]),suma+=num[i][j]; if(n>m){col();row();} else {row();col();} if(suma!=sumb){printf("-1\n");return 0;} printf("%d\n",(int)ro.size()+(int)co.size()); for(int i=0;i<ro.size();i++) printf("row %d\n",ro[i]+1); for(int i=0;i<co.size();i++) printf("col %d\n",co[i]+1); return 0; }
技巧性很强的一道计数题 我们考虑计算每个数对答案的贡献 即它的系数
观察发现 某个数的系数c 是它下一行的相邻两个数的系数的线性组合 是不是有点像组合数C(n,m)的递推公式 C(n,m)=C(n-1,m-1)+C(n-1,m)
我们大胆猜测该系数是某种组合数的形式
运用归纳法发现了公式
另外发现了一个特点,当n=4k+1时 第一次进行加法和第一次进行减法的结果是相同的!(也可以用归纳法证明)
所以,对于输入数据,我们首先进行几次暴力运算,将它转化为n=4k+1的形式 之后我们套用系数公式即可。
做计数问题真的是要大胆猜想啊!!! 这道题猜到是组合数就离答案很近了!
#include <bits/stdc++.h> #define MAXN 200010 using namespace std; const int mod=1e9+7; int n,a[MAXN]; long long q_pow(long long x,long long n) { long long res=1,tmp=x; while(n){ if(n&1) res=res*tmp%mod; tmp=tmp*tmp%mod; n>>=1; } return res; } int main() { int i,j,c=1; scanf("%d",&n); for(i=1;i<=n;i++) scanf("%d",&a[i]); while(n%4!=1){ for(i=1;i<n;i++){ if(c) a[i]=(a[i]+a[i+1])%mod; else a[i]=(a[i]-a[i+1]+mod)%mod; c^=1; } n--; } long long p=n/2,C=1,ans=0; for(i=0;i<=p;i++){ if(i) C=C*(p-i+1)%mod*q_pow(i,mod-2)%mod; //cout<<C<<endl; ans+=C*a[i<<1|1]; ans%=mod; } cout<<ans<<endl; return 0; }
补充证明一下:为什么当n=4k+1时有很好的性质呢? 比如n=5的三角形题目也给出了 那么当n=4*2+1=9时 实际上是由5个n=5时的三角形部分重叠构成的!
在这个基础上 所有的归纳证明都好办了。
很有趣的一道树形DP 题目给我们的所有商品显然构成了以商品1为根的树
我们首先转化问题 考虑买j个商品的最小花费,然后枚举判断即可。
那么 我们设函数f[i][j]表示 在子树i中购买j个商品可以使用优惠券的最小花费(即保证购买了商品i且使用了i的优惠券)
g[i][j] 表示在子树i中购买j个商品不可以使用优惠券的最小花费
转移方法是比较简单的,对于当前节点i 每次添加一个它的儿子(子树),枚举在新的子树中购买k个商品,在之前添加的所有子树中购买j-k个商品即可。
__________________________________________________________________________________
那么问题来了 这个转移的复杂度到底是多少?
先说结论,看似总的是三层循环,应该是O(n^3)实际复杂度是O(n^2)
为什么呢? 这里采用反证法证明
假如三层循环的上界都是 关于n的一次函数(不妨设 an,bn,cn) 此时满足总的复杂度O(n^3)
但是,由于最外层循环添加了 an个子树(a是任意正实数 满足an为整数) 最内层循环每次添加了cn个节点
即有an个子树,且存在一个子树有cn个节点,那么总的节点数就成了acn^2 意味着节点数不是O(n)而是O(n^2)出现矛盾
所以三层循环不可能出现每层循环的上界都为n的一次函数的情况 即复杂度是n^3的无穷小。那么其幂函数部分必然是O(n^2)
#include<bits/stdc++.h> #include<vector> #define N 5005 using namespace std; const int oo=(1<<30)-1; vector<int> G[N]; int n,m,sz[N],a[N],b[N],f[N][N],g[N][N],h[N]; void dfs(int t) { int i,j,k,x; sz[t]=1; for(i=1;i<=n;i++) f[t][i]=g[t][i]=oo; f[t][1]=a[t]-b[t]; g[t][0]=0,g[t][1]=a[t]; for(i=0;i<G[t].size();i++){ dfs(x=G[t][i]); for(j=1;j<=sz[t];j++) h[j]=f[t][j]; for(j=1;j<=sz[t];j++) for(k=1;k<=sz[x];k++) f[t][j+k]=min(f[t][j+k],h[j]+min(f[x][k],g[x][k])); for(j=0;j<=sz[t];j++) h[j]=g[t][j]; for(j=0;j<=sz[t];j++) for(k=1;k<=sz[x];k++) g[t][j+k]=min(g[t][j+k],h[j]+g[x][k]); sz[t]+=sz[x]; } } int main() { int i,x; scanf("%d %d",&n,&m); for(i=1;i<=n;i++){ scanf("%d %d",&a[i],&b[i]); if(i>1) scanf("%d",&x),G[x].push_back(i); } dfs(1); for(i=1;i<=n;i++) if(min(f[1][i],g[1][i])>m) break; cout<<i-1; return 0; }
蛤蛤 这个复杂度分析是不是很有趣? 看代码吧