Codeforces Round #786 (Div. 3)(A-G)

第一次被hack有点难受,不过看到本来e题2400,变成570左右就好受多了(不是)

A. Number Transformation

题意:给定x,y,问是否存在a,b,使得x*b^a=y,输出任意a,b;

思路:由于1<=x,y<=100,于是只需要暴力即可,暴力枚举b,a,由于b是在1-100,b如果为2,a最多也只能为7,于是复杂度为t*100*7,1<=t<=1e4,暴力即可

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
int t; 
int main(){
	cin>>t;
	while(t--){
	   ll x,y;
	   cin>>x>>y;
	   int flag=0;
	   ll ansx=0,ansy=0;
	   for(ll i=1;i<=100;i++){
	   	 for(ll j=1;j<=8;j++){
			ll po=pow(i,j);
			if(po*x==y){
				flag=1;
				ansx=j;
				ansy=i;
				break;
			} 	
		 }
		 if(flag==1)break;
	   }
	   cout<<ansx<<" "<<ansy<<endl; 
	}	
} 

B. Dictionary

题意:ab=1,ac=2,ad=3........az=25,ba=26;于是只需要一个用一个map映射关系即可;

#include<bits/stdc++.h>
using namespace std;
#define ll long long
int t;
map<string,int> mp; 
int main(){
	cin>>t;
	int cnt=1;
	for(int i=0;i<26;i++){
		for(int j=0;j<26;j++){
			string a;
			a+=i+'a';
			a+=j+'a';
		    if(i!=j){
		    	mp[a]=cnt++;
			}
		}
	}
	while(t--){
	   string s;
	   cin>>s;
	   cout<<mp[s]<<endl;
	}	
} 

C. Infinite Replacement

题意:给定一个只由a的s字符串,一个q字符串,每次可以将t字符串的替换s字符串中的一个a,问最多能产生多少种字符串,若答案为无穷大则输出-1;

思路:可以发现只要q字符串中出现了a且q字符串个数大于1,则答案一定为-1,其余的情况为若q字符串只有一个1,则答案为1,剩下的答案为2的cnt(s)次方

#include<bits/stdc++.h>
using namespace std;
#define ll long long
int t; 
int main(){
	cin>>t;
	while(t--){
       string s,q;
       cin>>s>>q;
       int flag=1;
       for(int i=0;i<q.size();i++){
       	  if(q[i]=='a')flag=0;
	   }
       if(q.size()>1&&!flag){
       	  cout<<-1<<endl;
       	  continue;
	   }
	   ll ans=1;
       if(q.size()==1&&q[0]=='a')ans=1;
       else {
       	   ans=1;
       	   for(int i=1;i<=s.size();i++)ans*=2;
	   }
	   cout<<ans<<endl;
	}	
} 

D-A-B-C Sort

题意:给的那个数组a,还有两个空数组b,c,操作1:先将a数组的最后一个数放到b数组的中间,

直到a数组为空。操作2:将b数组的中间一个放到c数组的末尾,直到b数组为空。

思路:不难发现从a数组的末尾开始,每两个数被配对起来,这两个数最终一定会在c数组中被连续存放,其所有对都是按顺序存放的,于是只需要处理出来排序判断后面的对的最小值是否大于前面的对的最大值即可,若大于则不可能存在

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
int t,a[200010],v[200010][2];
int main(){
	cin>>t;
	while(t--){
       int n;
       cin>>n;
       for(int i=1;i<=n;i++)cin>>a[i];
       int cnt=1;
       for(int i=n;i>=1;i-=2,cnt++){
       	   if(i>1){
       	     int mi=min(a[i],a[i-1]);
       	     int ma=max(a[i],a[i-1]);
       	     v[cnt][0]=mi;
       	     v[cnt][1]=ma;
       	   }
       	   else {
       	     v[cnt][0]=a[i];
			 v[cnt][1]=a[i];   	
		  }
	   }
	   cnt--;
	   int flag=1;
	   for(int i=cnt;i>=2;i--){
	   	 if(v[i][1]>v[i-1][0]){ 
	   	   flag=0;break; 	
		}
	   }
	   if(flag)cout<<"YES"<<endl;
	   else cout<<"NO"<<endl;
	}	
} 

E. Breaking the Wall

被hack了QWQ

题意:给定一个数组a,每次可以选择一个元素减2,同时他的左边和右边的元素会-1,问使得最终至少有两个元素小于等于0的最小操作次数

思路:题目给的样例提示很多,照着样例的思路基本上就可以将所有情况考虑到了。

样例1:20 10 30 10 20 输出:10

不难看出这种情况下就是将两个10都选择5次

于是我们可以得到一种情况,即先选择一次最小的那个,将他变为0,然后标记为选择过,将他左右两边的数减掉cnt后,再找一次最小值即可。

样例2:1 8 1 输出:1

这种情况下,我们发现他选择的是将两个距离为2的1减掉。

于是我们再得到一种情况,即找到两个差距为2的数,将他们都减到0即可,但是减掉他们的做法是什么呢,设这两个数为a1,a2,(a1<=a2)答案是不是a2呢?(即将这两个数都减掉最大的那一个值)

这样就会出错了,如果为1 10 5呢,显然我们不是减五次。

那我们应该怎样减呢,肯定是先将小的变为0后再对大的进行减2操作会是最好的选择,

比如1 10 5,我们先减掉一次得到 0 9 4,然后对4进行减2操作,答案为3;

样例3:7 6 6 8 5 8 输出:4

这种情况下,是选择最终将两个6变成<=0.,那他是怎么操作的呢?

设这两个数为a1 a2(a1<=a2)相邻的两个数,如果我们要尽快得达到我们想要的答案,我们就需要贪心达到最优。我们进行每次操作时肯定是对两个数中大的那一个进行减二的操作才会得到最优解。显然当a1>=2*a2,答案就为a1/2+(a1%2!=0)了,因为在后面的操作中a1始终为较大的值。

那我们就可以将情况变为什么时候a1>=2*a2了,若达不到这种情况则是一直对a2进行操作,答案为此时答案为a2/2+(a2%2!=0);

用cnt来表示a1>=2*a2的时候;

即a1-cnt>=2(a2-2cnt)

解得cnt>=(2a2-a1)/3,此时cnt应该向上取整

当达到a1>=2*a2时,剩余得操作都是对a1进行,则答案为cnt1=a1/2+(a1%2!=0);

所以这种情况下ans=cnt+cnt1.

上面得情况中可能会有重叠得部分,但是答案最终还是取最小值。

将提交的代码整理干净一点。标程超级短。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
int n,a[200010],flag[200010]={0};
int cal(int x){
	return x/2+(x%2!=0);
}//向上取整 
int solve1(){//第一种情况
	 int res=0,mi=1e9,idx; 
	 for(int i=1;i<=n;i++)if(a[i]<mi)idx=i,mi=a[i];
	 int cnt=cal(a[idx]);//找到最小的那一个将他减到0 
	 res+=cnt;
	 a[idx]=0;
	 a[idx-1]-=cnt;
	 a[idx+1]-=cnt;
	 flag[idx]=1;//标记为选择过 
	 mi=1e9; 
	 for(int i=1;i<=n;i++)if(a[i]<mi&&!flag[i])mi=a[i];
	 if(mi>0)res+=cal(mi);
	 return res;
}
int solve2(){//第二种情况 
	int res=1e9;
	for(int i=3;i<=n;i++){
	   	 int a1=min(a[i-2],a[i]);
	   	 int a2=max(a[i-2],a[i]);
	   	 res=min(res,a1+cal(a2-a1));
   }
   return res;
}
int solve3(){//第三种情况 
	 int res=1e9;
	 for(int i=2;i<=n;i++){
	   	  int a1=min(a[i-1],a[i]);
	   	  int a2=max(a[i-1],a[i]);
	   	  int cnt=(2*a2-a1)/3+((2*a2-a1)%3!=0);
	   	  if(a1-cnt<=0)res=min(res,cal(a2));
	   	  else {
			 a1-=cnt;
	   	     int cnt1=cal(a1);
	   	     res=min(res,cnt1+cnt);
	   	 }
	}
	return res;
}
int main(){
	int t=1;
	while(t--){
       cin>>n;
       for(int i=1;i<=n;i++)cin>>a[i];
       int ans=min(solve2(),solve3());
       ans=min(ans,solve1());
       cout<<ans;
	}	
} 

F. Desktop Rearrangement

题意:给定一个只有*和.的矩阵,可以进行操作为将任意一个*移动到另一个位置,问最终达到一个形状为*全部放到左靠上的位置的矩阵需要的最小操作次数,同时给出q个询问每次可以将x,y处的*改为.,.改为*,每次询问完矩阵就会永久性改变,并输出改变完后的最小操作次数

思路:这道题的话也是分类讨论,不过这个讨论是比E简单我感觉。

首先我们知道每次询问只能改变一个方格,于是我们只需要先跑出原始的答案,然后每次询问完后对那个选定的方格进行条件判断即可。

为了方便理解给出一个矩阵

 ...*

 *...

 .*.*(4*4)

首先记录总共的*的个数cnt,ans=cnt,再跑一遍矩阵,如果(j-i)*n+i<-cnt,并且该位置为*,则ans--(即减掉在左靠上的位置上的*因为他们不需要移动);

好了现在开始讨论吧

每次我们选择一个x,y时,答案矩阵就会增加或者减少一个,增加或减少的位置为posi,posj。

得到posi,posj的方法为

int posj=cnt/n+(cnt%n!=0),posi;
if(cnt%n!=0)posi=cnt%n;
else posi=n;

一.当选择的位置为.时,答案矩阵增加一个

1.当我们选定的posi==x&&posj==y,此时增加的位置就是我们选择的位置,那他增加完后就恰好时在我们想要增加的位置上,不用再进行操作了。

我们将最终答案的矩阵为*的位置划为想要的位置,.的位置划为不想要的位置

排除上面的情况

2.当我们选定的a[posi][posj]='*'时

我们在原本的基础上多出一个*,它是a[posi][posj]贡献的,不会对答案产生影响

(1).如果我们改变的位置为在我们想要的位置时,我们在想要的位置上多了一个*,那我们就少了一个需要移动的位点了,所以整体上ans--;

 (2).如果我们改变的位置为在我们不想要的位置时,那我们多了一个点需要去移动到想要的位置上,但是我们知道恰好a[posi][posj]那里的点被固定在原地不用移动了,所以两者抵消,答案不变。

3.当我们选定的a[posi][posj]='.'时

    我们在原本的基础上多出一个需要到那里的‘*’,它是别的位置的*贡献的。

(1)如果我们改变的位置在我们想要的位置上时

  我们少了一个需要移动的点,加上原本基础上需要多出的一个移动的点,抵消后答案不变

(2)如果我们改变的位置在我们不想要的位置上时

   我们多了的这个点恰好既可以移动到原本基础上多出的那个点。ans++;

这样就肯定把所有情况涵盖了。

对于减少的情况我们可以同样得讨论一遍,知道了一种情况很容易就能推出所有情况,主要时分类讨论的思想。

才发现有暴力的解法,o(q*n)。

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
char a[1010][1010];
int main(){
	int n,m,q;
	cin>>n>>m>>q;
	int cnt=0;
	for(int i=1;i<=n;i++){
		cin>>(a[i]+1);
		for(int j=1;j<=m;j++)
		  if(a[i][j]=='*')cnt++;
	}
	int ans=cnt;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			if((j-1)*n+i<=cnt&&a[i][j]=='*')ans--;
		}
	}
	for(int i=1;i<=q;i++){
		int x,y;
		cin>>x>>y;
		if(a[x][y]=='.'){
			a[x][y]='*';
			cnt++;
			int posj=cnt/n+(cnt%n!=0),posi;
	   	    if(cnt%n!=0)posi=cnt%n;
	   	    else posi=n;
	   	    if(posi==x&&posj==y);
		    else if(a[posi][posj]=='*'){
				if((y-1)*n+x<=cnt)ans--;
			}
			else {
				if((y-1)*n+x>cnt)ans++;
			}
		}
	    else {
	   	    a[x][y]='.';
	   	    int posj=cnt/n+(cnt%n!=0),posi;
	   	    if(cnt%n!=0)posi=cnt%n;
	   	    else posi=n;
	   	    if(posi==x&&posj==y);
	   	    else if(a[posi][posj]=='*'){
	   	        if((y-1)*n+x<=cnt)ans++;	
			}
			else {
				if((y-1)*n+x>cnt)ans--;
			}
			cnt--;
	    }
	    cout<<ans<<endl;
	}
} 

G. Remove Directed Edges

题意:给定一个有向无环图,要求你找到最长的一条路径,且这条路径上的所有节点的出边和入边都至少为2(除了出发点入边可以等于1或者0,结束点出边可以等于1或者0)

思路:直接树形dp即可,找出最长的链。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=2e5+10;
int dp[N]={0},in[N],out[N];
vector<int> v[N];
void dfs(int u){
   if(dp[u])return;
   dp[u]=1;
   if(out[u]<=1)return;
   for(int i=0;i<v[u].size();i++){
   	  int to=v[u][i];
   	  dfs(to);
   	  if(in[to]>=2)dp[u]=max(dp[u],dp[to]+1);
   }
}
int main(){
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		int a,b;
		cin>>a>>b;
		v[a].push_back(b);
		in[b]++;
		out[a]++;
	}
	for(int u=1;u<=n;u++){
		dfs(u);
	}
	int ans=-1;
    for(int i=1;i<=n;i++)ans=max(ans,dp[i]);
    cout<<ans<<endl;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值