Educational Codeforces Round 71 (Rated for Div. 2)


Solutions


A. There Are Two Types Of Burgers

题意:
做一个\(A\)需要两个\(b\)和两个\(p\),能卖\(h\)元;做一个\(B\)需要两个\(b\)和两个\(f\),能卖\(c\)元。给出\(b,p,f\)的数量,求最多卖多少元。
思路:
比赛的时候用循环搞了,判断\(h,c\)的大小关系,决定先做什么。也可以\(b=b/2\),然后判断大小,每次取最小值,利润直接算,然后更新\(b\),再进行一次。

//#define DEBUG
#include
using namespace std;
#define lson (rt<<1)
#define rson (rt<<1|1)
const int N=100010;
const int inf=0X3f3f3f3f;
const long long INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-6;
const double pi = acos(-1.0);
const int mod = 1000000007;
typedef long long ll;
 
int main() {
	#ifdef DEBUG
	freopen("in.txt","r",stdin);
	#endif
    int _,b,p,f;
    double h,c;
    ll ans;
    for(scanf("%d",&_);_;_--) {
        scanf("%d%d%d%lf%lf",&b,&p,&f,&h,&c);
        ans=0;
        if(h>c) {
            while(b>=2&&p) {
                ans+=h;
                b-=2;
                p--;
            }
            while(b>=2&&f) {
                ans+=c;
                b-=2;
                f--;
            }
        } if(h=2&&f) {
                ans+=c;
                b-=2;
                f--;
            }
            while(b>=2&&p) {
                ans+=h;
                b-=2;
                p--;
            }
        } else {
            int zhi=p+f;
            while(b>=2&&zhi) {
                ans+=h;
                b-=2;
                zhi--;
            }
        }
        printf("%lld\n",ans);
    }
}
//#define DEBUG
#include
using namespace std;
const int N=100010;
const int inf=0X3f3f3f3f;
const long long INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-6;
const double pi = acos(-1.0);
const int mod = 1000000007;
typedef long long ll;
 
int _;
int b,p,f;
int h,c;
 
int main() {
	#ifdef DEBUG
	freopen("in.txt","r",stdin);
	#endif
	for (scanf("%d",&_);_;_--) {
		scanf("%d%d%d%d%d",&b,&p,&f,&h,&c);
		ll ans=0;
		b/=2;
		if (h>c) {
			int x=min(b,p);
			ans+=x*h;
			b-=x,p-=x;
			int y=min(b,f);
			ans+=y*c;
		} else {
			int x=min(b,f);
			ans+=x*c;
			b-=x,f-=x;
			int y=min(b,p);
			ans+=y*h;
		}
		printf("%lld\n",ans);
	}
}

B. Square Filling

题意:
给出一个\(01\)矩阵\(A\),让你用一个全\(0\)矩阵\(B\),通过每次选\(2\times2\)的子矩阵设值为\(1\),把\(B\)变为\(A\),可以的话,输出你选的子矩阵的左上角坐标。
思路:
范围不大,直接枚举\(1\)的位置,判断周围是否存在全\(1\)子矩阵。
题解思路是存在子矩阵乘积大于0的,就全部设置为\(2\),然后一直进行,如果最后还有\(1\),说明不行。

#include 
#define lowbit(x) (x)&(-x)
#define srand() srand(time(0))
#define pi acos(-1.0)
#define printftime() printf("Time used = %.2f\n",(double)clock()/CLOCKS_PER_SEC)
using namespace std;
typedef long long ll;
int a[100][100];
int flag[100][100];
struct len {
	int l,r;
}ans[100000];
int main()
{
	int n,m,cnt=1;int f=0;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)scanf("%d",&a[i][j]);
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			if(a[i][j]){
				if(a[i-1][j-1]&&a[i-1][j]&&a[i][j-1]){
					ans[cnt].l=i-1;
					ans[cnt++].r=j-1;
					continue ;
				}
				if(a[i][j-1]&&a[i+1][j]&&a[i+1][j-1]){
					ans[cnt].l=i;
					ans[cnt++].r=j-1;
					continue ;
				}
				if(a[i+1][j+1]&&a[i+1][j]&&a[i][j+1]){
					ans[cnt].l=i;
					ans[cnt++].r=j;
					continue ;
				}
				if(a[i-1][j]&&a[i-1][j+1]&&a[i][j+1]){
					ans[cnt].l=i-1;
					ans[cnt++].r=j;
					continue ;
				}
				f=1;
			}
		}
	}
	if(f)puts("-1");
	else {
		printf("%d\n",cnt-1);
		for(int i=1;i
//#define DEBUG
#include
using namespace std;
const int N=100010;
const int inf=0X3f3f3f3f;
const long long INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-6;
const double pi = acos(-1.0);
const int mod = 1000000007;
typedef long long ll;
 
int n, m;
int a[100][100];
 
int main() {
	#ifdef DEBUG
	freopen("in.txt","r",stdin);
	#endif
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= m; j++) 
			scanf("%d", &a[i][j]);
	}	
	vector > ans;
	for (int i = 1; i < n; i++) {
		for (int j = 1; j < m; j++) {
			if (a[i][j] * a[i][j + 1] * a[i + 1][j] * a[i + 1][j + 1] > 0 ) {
				a[i][j] = a[i][j + 1] = a[i + 1][j] = a[i + 1][j + 1] = 2;
				ans.push_back({i, j});
			}
		}
	}
	bool ok=true;
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= m; j++) {
			if (a[i][j] == 1) {
				ok = false; break;
			}
		}
	}
	if (!ok) puts("-1");
	else {
		printf("%d\n", (int)ans.size());
		for (int i = 0; i < (int)ans.size(); i++) {
			printf("%d %d\n", ans[i].first, ans[i].second);
		}
	}
	return 0;
}

C. Gas Pipeline

题意:
给一条公路用\(01\)字符串表示,有十字路口用\(1\)表示,现在要架管道,十字路口的时候,管道必须在高度为\(2\)的位置,其他位置高度可以为\(1\),管道同时需要支架,现给出\(01\)字符串,让你给公路架管道,保证开始和结束都是高度为\(1\)的管道。给出单位长度管道的花费和单位高度支架的花费,求架完公路的最小花费。

思路:
很容易想到贪心,处理出连续\(0\)序列的长度,\(0\)的位置可能不下降,所以判断一下下降与不下降的花费,选择较小的。
\(dalao\)好像都是动态规划写的。\(dp[i][0/1]\)表示\(i\)位置右边支架为\(0/1\)的最小花费,转移的话就,

\[dp[i][0/1]= \begin{cases} dp[i][0]=min(dp[i-1][0]+a,\ dp[i-1][1]+2{\ast}a)+b, &\text{if s[i]=='0'}\\ dp[i][1]=min(dp[i-1][0]+2{\ast}a,\ dp[i-1][1]+a)+2{\ast}b, &\text{if s[i]=='0'}\\ dp[i][1]=dp[i-1][1]+a+2{\ast}b, &\text{if s[i]=='1'}\\ dp[i][0]=INF,& \text{if s[i]=='1'} \end{cases} \]

\(s[i]=1\)的时候,右边支架高度只能为2,所以\(dp[i][0]=INF\),并且只能由\(d[i-1][1]\)转移得到,因为左边支架高度也只能为\(2\)。初始\(dp[0][0]=b,dp[0][1]=INF\),因为左边由高度为\(1\)开始的。答案即为\(dp[n][0]\)

//#define DEBUG
#include
using namespace std;
#define lson (rt<<1)
#define rson (rt<<1|1)
const int N=100010;
const int inf=0X3f3f3f3f;
const long long INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-6;
const double pi = acos(-1.0);
const int mod = 1000000007;
typedef long long ll;
 
char s[2*N];
ll d[2*N];
 
int main() {
	#ifdef DEBUG
	freopen("in.txt","r",stdin);
	#endif
    int _,len;
    ll a,b;
    for(scanf("%d",&_);_;_--) {
        scanf("%d%lld%lld",&len,&a,&b);
        scanf("%s",s+1);
        d[len+1]=0;
        for(int i=len;i>=1;i--) {
            if(s[i]=='1') d[i]=0;
            else d[i]=d[i+1]+1;
        }
        //for(int i=1;i<=len;i++) printf("%d",d[i]);
        int op=0; //下降态
        ll sum=0;
        ll cnt=0; // 支柱:0但不下降
        for(int i=1;i<=len;i++) {
            if(s[i]=='0') {
                if(op) {
                    if(2*a-d[i]*b+b<=0||i+d[i]-1==len) { //下降
                        if(i+d[i]-1==len) { //最后下
                            sum+=(d[i]+2)*a;
                            cnt+=2*b;
                            cnt+=d[i]*b;
                        } else { //中间下
                            sum+=(d[i]+1)*a;
                            cnt+=(d[i]-1)*b;
                            cnt+=2*b;
                        }
                        op=!op;
                    } else { // 不下降
                        sum+=d[i]*a;
                        cnt+=d[i]*2*b;
                    }
                } else {
                    if(i+d[i]-1!=len) {
                        sum+=(d[i]-1)*a;
                        cnt+=d[i]*b;
                    } else {
                        sum+=d[i]*a;
                        cnt+=(d[i]+1)*b;
                    }
                }
                i=i+d[i]-1;
            } else {
                if(op) {
                    sum+=a;
                } else {
                    sum+=2*a;
                    op=!op;
                }
                cnt+=2*b;
            }
            //printf("i=%d   %lld  **** %lld\n",i,sum,cnt);
        }
        sum=sum+cnt;
        printf("%lld\n",sum);
    }
}
//#define DEBUG
#include
using namespace std;
const int N = 100010;
const int inf = 0X3f3f3f3f;
const long long INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-6;
const double pi = acos(-1.0);
const int mod = 1000000007;
typedef long long ll;
 
char s[2*N];
ll dp[2*N][2];
int _;
int n;
ll a, b;
 
int main() {
    #ifdef DEBUG    
    freopen("in.txt", "r", stdin);
    #endif
    for (scanf("%d", &_); _; _--) {
        scanf("%d %lld %lld", &n, &a, &b);
        scanf("%s", s+1);
        dp[0][0] = b; dp[0][1] = INF;
        for (int i = 1; i <= n; i++) {
            if (s[i] == '0') {
                dp[i][1] = min(dp[i - 1][1] + a + 2 * b, dp[i - 1][0] + 2 * a + 2 * b);
                dp[i][0] = min(dp[i - 1][1] + 2 * a + b, dp[i - 1][0] + a + b);
            } else {
                dp[i][0] = INF;
                dp[i][1] = dp[i - 1][1] + a + 2 * b;
            }
        }
        printf("%lld\n", dp[n][0]);
    }
    return 0;    
}

D. Number Of Permutations

题意:
给出\(n\)个有序对\((x_i,y_i)\),如果一组有序对,按\(x_i\)排序非递减或按\(y_i\)排序非递减的话定义为坏的。然后问\(n\)的全排列生成的每组有序对有多少个好的。比如\(n=3\),给出\((1,2),(3,2),(3,1)\),那么按全排列生成的即为:
\(123 :(1,2),(3,2),(3,1)\)
\(132:(1,3),(3,1),(3,2)\)
\(\vdots\)
思路:
逆向思维,一共有\(n!\)种结果,定义\(cnt_1\)为按\(x_i\)排序非递减的,定义\(cnt_2\)为按\(y_i\)排序非递减的,定义\(cnt_{12}\)\(x_i和y_i\)同时非递减的。那么答案就是\(n!-cnt_1-cnt_2+cnt_{12}\)
\(cnt_1\)的求法:假设这么一个序列:\(1,2,1,3\),全排列后只有两个非递减的,即\(2!\)。再比如:\(1,2,3,2,1\),全排列后有\(2!\ {\ast}\ 2!\)个非递减的。因为\(1、2\)重复。所以\(cnt_1\)就出来了。\(cnt_2\)同理。
\(cnt_{12}\)的求法:我们可以先按\(x_i\)排序再按\(y_i\)排序,同样也是统计重复的个数。但最后要检查是否满足\(y_i\)非递减。

//#define DEBUG
#include
using namespace std;
const int N = 100010;
const int inf = 0X3f3f3f3f;
const long long INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-6;
const double pi = acos(-1.0);
const int mod = 998244353;
typedef long long ll;
 
pair d[3 * N];
map a, b;
map, int> ab;
int n;
int fac[3 * N];
 
 
int main() {
    #ifdef DEBUG
    freopen("in.txt", "r", stdin);
    #endif
    scanf("%d", &n);
    fac[0] = 1;
    for (int i = 1; i <= n; i++) {
        fac[i] = 1ll * fac[i - 1] * i % mod;
        scanf("%d %d", &d[i].first, &d[i].second);
        a[d[i].first]++; b[d[i].second]++;
        ab[d[i]]++;       
    }
    sort(d + 1, d + n + 1);
    int sum = fac[n];
    int temp = 1;
    for (auto i : a) {
        temp = 1ll * temp * fac[i.second] % mod;
    }
    sum = (sum - temp + mod) % mod; 
    temp = 1;
    for (auto i : b) {
        temp = 1ll * temp * fac[i.second] % mod;
    }
    sum = (sum - temp + mod) % mod; 
    bool ok = true;
    temp = 1;
    for (auto i : ab) {
        temp = 1ll * temp * fac[i.second] % mod;
    }
    for (int i = 1; i < n; i++) {
        if(d[i].second > d[i + 1].second) {
            ok = false; break;
        }
    }
    if(ok) sum = (sum + temp) % mod;
    printf("%d\n", sum);
}

E. XOR Guessing

题意:
猜数字\(w\),你每次询问\(100\)个数,会给你一个\(w\)与其中一个的异或结果\(x\),再次询问\(100\)个数,又给你一个\(w\)与其中一个的异或结果\(y\),最多询问两次,且你询问的所有数字不能重复,求\(w\)
思路:
很神奇的思路,\(w\)范围最多\(14\)位二进制,我们可以先用\(100\)个低\(7\)的数字询问,那么\(x\)的高\(7\)位就是\(w\)的高\(7\)位,再用高\(7\)位的数字询问,那么\(y\)的低\(7\)位就是\(w\)的低\(7\)位。\(1\sim100\)刚好能用低\(7\)位表示,然后可以把\(1{\sim}100\)的数字左移\(7\)位,就生成高\(7\)位的数字了。

//#define DEBUG
#include
using namespace std;
const int N = 100010;
const int inf = 0X3f3f3f3f;
const long long INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-6;
const double pi = acos(-1.0);
const int mod = 1000000007;
typedef long long ll;
 
int main() {
    #ifdef DEBUG
    freopen("in.txt", "r", stdin);
    #endif
    printf("?");
    for (int i = 0; i < 100; i++) printf(" %d", i + 1);
    puts("");
    fflush(stdout);
    int x;
    scanf("%d", &x);
    printf("?");
    for (int i = 0; i < 100; i++) printf(" %d", i + 1 << 7);
    puts("");
    fflush(stdout);
    int y;
    scanf("%d", &y);
    printf("! %d", (x & ~0x7f) | (y & 0x7f));
}

F. Remainder Problem

题意:
给出\(a_1,a_2,{\dots},a_{500000}\),且全为\(0\),然后有\(q\)次询问,

  • 1 \(x \ y\) :\(a_x + y\)
  • 2 \(x\ y\)\(\sum_{i{\in}R(x,y)}a_i\)\(R(x,y)\)\(1{\sim}500000\)的下标\(i\%x=y\)的集合

思路:
看题解是根号分治,题目大多是\(q\)\(N\)同一数量级 ,每次修改在原数组上修改,同时用数组预处理出小于\(\sqrt(N)\)的查询\(O(sqrt(N))\),大于\(\sqrt(N)\)的查询就累加原数组的值\(O({\frac{N}{X}})\),其中\(X>{\sqrt(N)}\),所以总复杂度\(O(N{\sqrt(N)})\)
见代码

//#define DEBUG
#include
using namespace std;
const int N = 100010;
const int inf = 0X3f3f3f3f;
const long long INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-6;
const double pi = acos(-1.0);
const int mod = 1000000007;
typedef long long ll;
 
int q;
int a[5*N];
int d[710][710];
 
int main() {
    #ifdef DEBUG
    freopen("in.txt", "r", stdin);
    #endif
    scanf("%d", &q);
    int op, x, y;
    for (int i = 1; i <= q; i++) {
    	scanf("%d %d %d", &op, &x, &y);
    	if (op == 1) {
    		a[x] += y;
    		for (int j = 1; j < 710; j++) {
    			d[j][x % j] += y; //d[j][模j的余数]
    		}
    	} else {
    		int ans = 0;
    		if (x < 710) ans = d[x][y]; // d[x][模x的余数=y]
    		else {
    			for (int j = y; j <= 500000; j += x) {
    				ans += a[j];
    			}
    		}
    		printf("%d\n", ans);
    	}
    }
}