Codeforces Round #786 (Div. 3)

A. Number Transformation

又不仔细看题目!!A题都wa了三次……

#include<bits/stdc++.h>
using namespace std;
int gcd(int x,int y)
{
	return y==0?x:gcd(y,x%y);
}
int main()
{
	int i,j;
	int t;cin>>t;
	while(t--)
	{
		int x,y;cin>>x>>y;
		double ex = (double)y/x;
		int cc1 = ex;
		if(cc1== ex){
			cout<<1<<" "<<ex<<endl;
		}else{
			puts("0 0");
		}
	}
	return 0; 
}

B. Dictionary

#include<bits/stdc++.h>
using namespace std;
int v[30][30];
void in()
{
	int idx = 1;
	for(int i=0;i<26;i++){
		for(int j = 0;j <26;j++){
			if(i == j) continue;
			v[i][j] = idx++;
		}
	}
}
int main()
{
	int t;cin>>t;
	in();
	while(t--)
	{
		char a,b;cin>>a>>b;
		cout<<v[a-'a'][b-'a']<<endl;
	

C. Infinite Replacement

这题看了大佬的解法后突然意识到写快速幂干嘛啊;

ll quick(int x,ll base)
{
	ll ans = 1;
	while(x)
	{
		if(x&1) ans=ans*base;
		x>>=1;
		base*=base;
	}
	return ans ;
}
//当base等于2的时候不就是位运算做嘛quick(x,2)  = 1ll<<x;
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int main()
{
	int t;cin>>t;
	
	while(t--)
	{
		string s;cin>>s;
		string t;cin>>t;
		int f = 0;//看t中是否有'a'
		int f2 = 0;//看是否除了a还有别的字母 
		for(int i=0;i<t.length();i++){
			if(t[i]=='a'){
				f = 1;
			}
			if(t[i]!='a'){
				f2 = 1;
			}
		}
		if(f == 1 &&t.length()!=1){
			puts("-1");
		}else{//t中无'a'或者t的长度不为1 
			ll ans = 0;//主要2^50会爆int
			if(f2 == 1 && f == 0){//有别的字母并且不含a 
                ans  = 1ll<<s.length();
			}else{//无别的字母 
				ans = 1;
			}
			cout<<ans<<'\n';
		}
	}
}

大佬的代码:

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int t;scanf("%d",&t);
    while(t--)
    {
        string s,t;
        cin>>s>>t;
        if(t == "a"){
            puts("1");
        }else{
            bool f = 0;
            for(int i = 0;i < t.length(); i++){
                if(t[i] == 'a'){
                    f = 1;
                    break;
                }
            }
            if(f){
                puts("-1");
            }else{
                printf("%lld\n",1ll<<s.length());
            }
            
        }
    }
    return 0;
}

D. A-B-C Sort

​ 如果A数组有奇数个元素,则 A 1 A_1 A1一定被放在B数组最中间然后又被拿出来放在C数组的第一个位置,所以如果A数组元素个数为奇数然后A中的第一个元素又不是A中最小的话,那边不能使C升序;当 A 1 A_1 A1是min时,那便不用考虑它,直接看剩下的偶数个元素,剩下偶数个元素中 A 2 A_2 A2 A 3 A_3 A3最终一定被放在B的中间两个位置( A 1 A_1 A1左右两边),之后又被拿出来放到C中,当然 ,偶数个的时候A2和A3谁在A1左侧谁在右侧随意选择,之后拿出来放到C中的时候选了A1之后再从A3和A2选较小值便可。所以A2和A3至少有一个必定是剩下元素中的最小值,但是又要小于A1,于是我们便不断重复奇数长度、偶数长度的过程,在此过程中维护一个当前C的最大值就行。

#include<bits/stdc++.h>
using namespace std;
const int N = 1e6+5;
int main()
{
    int t;scanf("%d",&t);
    while(t--)
    {
        int n;scanf("%d",&n);
        vector<int>q; 
        for(int i = 1;i <= n; i++){
        	int x;scanf("%d",&x);
        	q.push_back(x);
		}
        
        int maxt = -1e9;
       	int f = 0;
       	while(!q.empty())
       	{
       		if(q.size()%2==1){
				if(q[0] < maxt){
					f = 1;
					break;
				}else{
					maxt = q[0];
					q.erase(q.begin());
				}	
			}else{
				int mint = min(q[0],q[1]);
				if(mint < maxt){
					f = 1;
					break;
				}else{
					maxt = mint ;
					auto it = q.begin();
					if(q[0]>q[1]) it = q.begin()+1;
					else it = q.begin();
					q.erase(it);
				}
			}	
		}
        puts(f?"NO":"YES");
    }
    return 0;
}

E. Breaking the Wall

​ 花费代价1的价格对 A i A_i Ai攻击一次造成2点伤害,并且对 A i − 1 A_{i-1} Ai1 A i + 1 A_{i+1} Ai+1造成1点溅射伤害;

情况分析:

  • 1:两个攻击点相隔2距离or more,则互相不产生溅射伤害,总攻击次数为 ⌈ A i 2 ⌉ \lceil{\frac{A_i}{2}\rceil} 2Ai+ ⌈ A j 2 ⌉ \lceil{\frac{A_j}{2}\rceil} 2Aj;

  • 2:两个攻击点相距1距离,也不产生溅射伤害,但是如果 A i A_i Ai A j A_j Aj都为奇数的时候,我们可以对他们中间位置攻击一次,然后对i和j产生一点溅射伤害,让他两变成偶数,则总攻击次数为1+ ⌊ A i 2 ⌋ \lfloor{\frac{A_i}{2}\rfloor} 2Ai+ ⌊ A j 2 ⌋ \lfloor{\frac{A_j}{2}\rfloor} 2Aj;

  • 3:两个攻击点相邻;每次让较大的数减少2,较少的减少1,每次总体减少3;

    • 如果较大的数是较小的数的两倍即以上,则答案为 ⌈ m a x 2 ⌉ \lceil{\frac{max}{2}\rceil} 2max;

    • 否则:若干步后两步数达到相同,然后在接下来的步骤里至多相差1,当前花费了 ⌊ A i + A j 2 ⌋ \lfloor{\frac{A_i+A_j}{2}\rfloor} 2Ai+Aj步数,达到(0,0)(0,1)或者(1,0)的状态,后两种则需要再攻击一次,则取上整, ⌈ A i + A j 2 ⌉ \lceil{\frac{A_i+A_j}{2}\rceil} 2Ai+Aj

#include<bits/stdc++.h>
using namespace std;

const int N = 1e6+5;
int a[N],b[N];

int n;

int main()
{
	int i,j;
	cin>>n;
	for(i = 1;i <= n; i++){
		scanf("%d",&a[i]);
		b[i] = a[i];
	}
	sort(b+1,b+1+n);
	int mint = ceil((double)b[1]/2) + ceil((double)b[2]/2);//情况1 
	for(i = 1;i <= n; i++){
		
		if(i < n &&i > 1&& a[i-1]%2 == 1&&a[i+1]%2==1 ){//情况2那种特殊情况 
			int cc1 = 1+a[i-1]/2 + a[i+1]/2;
			mint = min (mint,cc1);
		}
		if(i < n){//情况3 
			int max_ex = max(a[i],a[i+1]);
			int min_ex = min(a[i],a[i+1]);
			
			if(max_ex>=min_ex*2){
				int cc2 = ceil((double)max_ex/2);
				mint = min(mint,cc2);
			}else{
				int cc2 = ceil((double)(a[i]+a[i+1])/3);
				mint = min(mint,cc2);
			}
		} 
	}
	cout<<mint<<endl;
	return 0;
}

F. Desktop Rearrangement

这句话"The next q lines describe queries. The i-th of them contains two integers xi and yi (1≤xi≤n;1≤yi≤m) — the position of the cell which changes its state (if this cell contained the icon before, then this icon is removed, otherwise an icon appears in this cell)."

的意思是:接下来的 q 行描述查询。其中的第 i 个包含两个整数 xi 和 yi (1≤xi≤n;1≤yi≤m) — 更改其状态的单元格的位置(如果此单元格之前包含图标,则此图标将被删除,否则此单元格中会出现一个图标)。也就是转换它。

然后输出修改后使得桌面“变好”的最小操作次数;

暴力做法:

#include<bits/stdc++.h>
using namespace std;

const int N = 1010;
char s[N][N];
int col[N];//记录每一列"*"的数量 
int n,m,q;

int main()
{
	int i,j;
	scanf("%d%d%d",&n,&m,&q);
	for(i = 1;i <= n; i++) cin>>s[i]+1;
	
	int sum = 0;//记录总的"*"得数量
	for(i = 1;i <= n; i++){
		for(j = 1;j <= m; j++){
			//cout<<s[i][j];
			if(s[i][j] == '*'){
				sum++;
				col[j]++;
			}
		}//cout<<'\n'; 
	} 
	while(q--)
	{
		int x,y;scanf("%d%d",&x,&y);
		if(s[x][y] == '*'){
			sum--;
			s[x][y] = '.';
			col[y]--;//第y列 
		}else{
			sum++;
			s[x][y] = '*';
			col[y]++;
		}
		
		int cc1 = sum/n;//都被填充满有几列 
		int cc2 = sum - cc1*n;//剩多少颗是不能填充满的
		//然后我们去看前cc1列有多少棵"*",他们是本就不需要移动的
		int cnt = 0;
		for(i = 1;i <= cc1; i++){
			cnt += col[i];
		}	
		//然后再去看第cc1+1那列前cc2个中有多少个"*",他们也不需要额外移动 
		for(i = 1;i <= cc2; i++){
			cnt+=(s[i][cc1+1]=='*');
		} 
		cout<<sum-cnt<<endl;
	}
	return 0;
}

树状数组做法:

​ 看了某乎上才知道可以转换成树状数组做法,昨天刚学树状数组,qwq;

首先将二维坐标转为一维坐标**(x,y) = ((y-1)*n+x)**;

然后每修改一个点就判断,如果是“*”,就add(pos,-1),pos表示当前点的一维x轴坐标,否则就add(pos,1);

修改完后统计前cnt个位置上有多少个“*”即可,然后与cnt作差;

#include<bits/stdc++.h>
using namespace std;
const int N = 1010,M = 1000010;
int tr[M];
char a[N][N];
int n,m,q;

int lowbit(int x)
{
    return x&(-x);
}

void add(int u,int v)
{
    for(int i = u;i <= n*m;i += lowbit(i)) tr[i]+=v;
}

int query(int x)
{
    int res = 0;
    for(int i = x;i >= 1;i -= lowbit(i)) res+=tr[i];
    return res;
}

int get(int x,int y)
{
    return (y-1)*n+x;
}
int main()
{
    int i,j;
    cin>>n>>m>>q;
    int cnt = 0;//记录*的个数
    for(i = 1;i <= n; i++){
        cin>>a[i]+1;
    }
    for(i = 1;i <= n; i++){
        for(j = 1;j <= m; j++){
            if(a[i][j] == '*') add(get(i,j),1),cnt++;
        }
    }
    while(q--)
    {
        int x,y;
        cin>>x>>y;
        int idx = get(x,y);
        if(a[x][y] == '*'){
            a[x][y] = '.';
            add(idx,-1);
            cnt--;
        }else{
            a[x][y] = '*';
            add(idx,1);
            cnt++;
        }
        cout<<cnt - query(cnt)<<'\n';
    }
    return 0;
}

G. Remove Directed Edges

还得多复习几次这个题;

要好好学英语啊!

​ 给你n个顶点,m条边,顶点从1~n编号,保证没有多重边和自循环环;

i n v in_v inv表示顶点v的入度, o u t v out_v outv表示顶点v的出度;

​ 要你从图中删除若干条边,然后v点新的入度为 i n v ′ {in_v}^ \prime inv,新的出度为 o u t v ′ {out_v}^ \prime outv,如果原本就是0就不需要删除;

需满足:

  • i n v ′ {in_v}^ \prime inv< i n v ′ {in_v}^ \prime inv或者 i n v ′ {in_v}^ \prime inv = i n v {in_v} inv = 0;
  • o u t v ′ {out_v}^ \prime outv< o u t v ′ {out_v}^ \prime outv或者 o u t v ′ {out_v}^ \prime outv = o u t v {out_v} outv = 0;

我们将一个点集S称之为cute定义为:对于S中的任意点v和u(v!=u),在未删除的路径上存在一条从u到v的路径;

请问:从图中删除一些边并且所有顶点的入度和出度都减小或保持等于 0 后,可爱集 S 的最大可能大小是多少?

分析:就是找出删除若干条边使得点集S“cute”后,从u到v的那条链的长度最长是多少?

用dp求最长链:由可达到的点来更新当前点的值;

#include<bits/stdc++.h>
using namespace std;
const int N = 1e6+5;

int h[N],e[N],ne[N],idx;
int n,m;
int in[N],out[N];//入度,出度 
int dp[N];

void add(int a,int b)
{
	e[idx] = b,ne[idx] = h[a],h[a] = idx++;
}

void dfs(int u)
{
	if(dp[u]) return;//已经遍历过查找过最远能到达的地方
	dp[u] = 1;
	if(out[u] <= 1) return;//当前点已经不能再删了
	for(int i = h[u];i != -1;i = ne[i]){
		int j = e[i];
		dfs(j);
		if(in[j] >= 2) dp[u] = max(dp[u],dp[j]+1);
        //若u点能到达j点,则也能到达j能到达的点,取大值;
	} 
}

int main()
{
	int i,j;
	cin>>n>>m;
	memset(h,-1,sizeof(h));//初始化 
	for(i = 1;i <= m; i++){
		int u,v;//u->v的一条边
		cin>>u>>v;
		add(u,v);
		in[v]++,out[u]++; 
	}
	
	for(i = 1;i <= n; i++) dfs(i);
	
	int maxt = 0;
	for(i = 1;i <= n; i++){
		maxt = max(dp[i],maxt); 
	}
	cout<<maxt<<endl;
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值