G似乎有点难,估计鸽掉了。。
Contest:http://codeforces.com/contest/1207
A-There Are Two Types Of Burgers(简单贪心)
题目链接:http://codeforces.com/contest/1207/problem/A
题目大意:给出b个皮,p个牛肉,f个鸡肉,让你组装成利润最多的组合。牛肉包需要2皮+1牛肉,鸡肉堡需要2皮+1鸡肉。h是牛肉煲单价,c是鸡肉堡单价。
思路:贪心,[b/2]个汉堡,加和即可。
ACCode:
int n,p,f;
int h,c;
int main(){
int T;scanf("%d",&T);
while(T--){
scanf("%d%d%d",&n,&p,&f);
scanf("%d%d",&h,&c);
n>>=1;
int Ans=0;
if(c>=h){//chichen is batter
if(n>=f){//all chicken
Ans+=f*c;
n-=f;
}
else{//all bun
Ans+=n*c;
n=0;
}
if(n>=p){//all beef
Ans+=p*h;
n-=p;
}
else{//all bnn
Ans+=n*h;
n=0;
}
}
else{
if(n>=p){
Ans+=p*h;
n-=p;
}
else{
Ans+=n*h;
n=0;
}
if(n>=f){
n-=f;
Ans+=f*c;
}
else{
Ans+=n*c;
n=0;
}
}printf("%d\n",Ans);
}
}
B-Square Filling(简单模拟)
题目链接:http://codeforces.com/contest/1207/problem/B
题目大意:给出一个(n*m)的0,1矩阵A。让你经过k次修改将一个全0的(n*m)矩阵B修改为A。每次修改一个2*2的范围。不能修改输出-1
思路:从左上角枚举即可,最后比较。
ACCode:
int A[MAXN][MAXN],B[MAXN][MAXN];
PII Ans[MAXN*MAXN];int tot;
int n,m;
int main(){
while(~scanf("%d%d",&n,&m)){
clean(B,0);
for(int i=1;i<=n;++i){
for(int j=1;j<=m;++j){
scanf("%d",&A[i][j]);
}
}
tot=0;
for(int i=1;i<=n;++i){
for(int j=1;j<=m;++j){
if(A[i][j]&&A[i+1][j]&&A[i][j+1]&&A[i+1][j+1]){
Ans[++tot]=make_pair(i,j);
B[i][j]=B[i+1][j]=B[i][j+1]=B[i+1][j+1]=1;
}
}
}
int flag=1;
for(int i=1;i<=n;++i){
for(int j=1;j<=m;++j){
if(A[i][j]!=B[i][j]){
flag=0;break;
}
}
}
if(flag){
printf("%d\n",tot);
for(int i=1;i<=tot;++i) printf("%d %d\n",Ans[i].first,Ans[i].second);
}
else printf("-1\n");
}
}
C-Gas Pipeline(简单DP)
题目链接:http://codeforces.com/contest/1207/problem/C
题目大意:给出一个01串,0代表该位置是路,1代表该位置是路口,是路的地方可以假设高为1的管子,路口的地方只能假设高为2的管子,问你将这个路线架设完管子的最小花费。架子b花费每单位,管子a花费每单位。
思路:只有两种状态,该点架设高为1||2的,在路口只能架设高为2的。很容易推出状态转移方程:
DP[i][1]=min(DP[i-1][1]+a+b,DP[i-1][2]+2*a+b);//i表示第i个路线,1表示高为1,
DP[i][2]=min(DP[i-1][1]+2*a+2*b,DP[i-1][2]+a+2*b);
最后约束一下路口时只能建造单位2的就好了就好了。
ACCode:
char S[MAXN];
ll DP[MAXN][3];
ll n,a,b;
int main(){
int T;scanf("%d",&T);
while(T--){
scanf("%lld%lld%lld",&n,&a,&b);
scanf("%s",&S);
// for(int i=0;i<n;++i) printf("%c",S[i]);printf("\n");
DP[0][1]=b;DP[0][2]=INF64;//
for(int i=1;i<n;++i){
if(S[i-1]=='0'&&S[i]=='0'){//可以设置单位1的高度
DP[i][1]=min(DP[i-1][1]+a+b,DP[i-1][2]+2*a+b);
DP[i][2]=min(DP[i-1][1]+2*a+2*b,DP[i-1][2]+a+2*b);
}
else{//只能是单位2
DP[i][1]=INF64;
DP[i][2]=min(DP[i-1][1]+2*a+2*b,DP[i-1][2]+a+2*b);
}
// printf("1=%lld 2=%lld\n",DP[i][1],DP[i][2]);
}
DP[n][1]=min(DP[n-1][1]+a+b,DP[n-1][2]+2*a+b);
// DP[i][2]=min(DP[i-1][1]+2*a+2*b,DP[i-1][2]+a+2*b);
printf("%lld\n",DP[n][1]);
}
}
D- Number Of Permutations(组合数+容斥)
题目链接:http://codeforces.com/contest/1207/problem/D
题目大意:给出n对数(x,y)找出所有的不是坏序列的个数。坏序列的定义是:一个排列中,x不递减的是坏序列。y不递减的是坏序列,(x,y)都不递减的是坏序列。
思路:很明显的组合数+容斥,首先得到全排列S。A第一关键字的重复排列之积,B第二关键字的重复排列之积,AB双关键字的重复排列之积。最后判断一下是否存在BC。因此就是Ans=S-A-B+AB;
ACCode:
/*
共n!种组合 S
第一关键字排序,第一关键字相同的得到组合数相加 B
第二关键字排序,相同的组合数相加 C
双关键字,相同的 BC
Ans=S-A-B+C
*/
PII A[MAXN];
ll Fac[MAXN];
int n;
int Cmp1(PII a,PII b){
return a.first<b.first;
}
int Cmp2(PII a,PII b){
return a.second<b.second;
}
int Cmp3(PII a,PII b){
if(a.first==b.first) return a.second<b.second;
return a.first<b.first;
}
void Intt(){
Fac[0]=Fac[1]=1;
for(ll i=2;i<MAXN;++i){
Fac[i]=(i*Fac[i-1])%MOD;
}
}
int main(){
Intt();
while(~scanf("%d",&n)){
for(int i=1;i<=n;++i){
scanf("%d%d",&A[i].first,&A[i].second);
}sort(A+1,A+1+n,Cmp1);
ll ans=Fac[n],B=1,C=1,BC=1;
for(int i=1,j=1;j<=n;j=i){
while(i<=n&&A[i].first==A[j].first) ++i;
B=(B*Fac[i-j])%MOD;
}sort(A+1,A+1+n,Cmp2);
for(int i=1,j=1;j<=n;j=i){
while(i<=n&&A[i].second==A[j].second) ++i;
C=(C*Fac[i-j])%MOD;
}sort(A+1,A+1+n);
for(int i=1,j=1;j<=n;j=i){
while(i<=n&&A[i].second==A[j].second&&A[i].first==A[j].first) ++i;
BC=(BC*Fac[i-j])%MOD;
}
for(int i=2;i<=n;++i){
if(A[i-1].second>A[i].second) BC=0;
}
// printf("ans=%lld B=%lld C=%lld BC=%lld\n",ans,B,C,BC);
ans=(ans-B-C+BC+2ll*MOD)%MOD;
printf("%lld\n",ans);
}
}
E-XOR Guessing(交互问题,分治)
题目链接:http://codeforces.com/contest/1207/problem/E
题目大意:猜一个数x在[0,2^14-1]。有两次机会,每次询问100个数,随机挑一个回答x^a[i],让你得出x是多少
思路:将两次询问分成[0,2^7-1]和[2^7,2^14-1]区间,这样,第一次确定的前七位,第二次确定后六位。拼起来就好了。注意交互问题的输入输出形式。。Wa了好几发,缓冲区没清空。。。
ACCode:
int main(){
int ans1,ans2,ans=0;
cout<<"? 1";//printf("? 1");
for(int i=2;i<=100;++i) cout<<" "<<i;cout<<endl;//printf(" %d",i);printf("\n");
cin>>ans1;//scanf("%d",&ans1);//得到前六位的值
for(int i=(1<<14);i>=(1<<7);i>>=1){
if(i&ans1){//这一位存在
ans|=i;
}
}
cout<<"? "<<(1<<7);//printf("? %d",(1<<7));
for(int i=(1<<8),j=2;j<=100;i+=(1<<7),++j) cout<<" "<<i;cout<<endl;//printf(" %d",i);printf("\n");
cin>>ans2;//scanf("%d",&ans2);
for(int i=(1<<6);i>0;i>>=1){
if(ans2&i){
ans|=i;
}
}cout<<"! "<<ans<<endl;//printf("! %d\n",ans);
fflush(stdout);
}
F-Remainder Problem(数据结构,分块)
题目链接:http://codeforces.com/contest/1207/problem/F
题目大意:给出一个[1,5e5]的数组,初始元素都为0,两种操作。1:x y a[x]+=y 2:x,y 求在[1,5e5]中找到所有a[i]的i%x==y的a[i]之和。
思路:分成两部分,sqrt(5e5),小的块维护一个Sum,大的块直接暴力求和即可。
对于Sum的维护,每次修改一个x位置的元素后,对于小部分,每个i只会对x%i产生影响(其他的取不到)。这里的i就是query里面的x.
ACCode:
/*
修改A[x]+=y
查询A[[1,5e5]%x==y]之和。
每次修改,对于我们只用处理出 sqrt(5e5)的所有影响就好了
x对Sum的影响,每次修改一个x位置的y后直接对应的Sum[i][i%x]+=y
查询x>sqrt(5e5)的时候,直接就可以暴力相加。否则直接输出答案即可
*/
int A[MAXN];
int Sum[750][750];
int q;
int main(){
while(~scanf("%d",&q)){
clean(Sum,0);
while(q--){
int opt;scanf("%d",&opt);
if(opt==1){//修改x的值+y
int x,y;scanf("%d%d",&x,&y);
A[x]+=y;
for(int i=1;i<750;++i) Sum[i][x%i]+=y;
}
else{//查询下标为[[1,5e5]%x]==y的数之和
int x,y;scanf("%d%d",&x,&y);
if(x>=750){//直接加就好了
int ans=0;
for(int i=y;i<=5e5;i+=x) ans+=A[i];
printf("%d\n",ans);
}
else printf("%d\n",Sum[x][y]);
}
}
}
}