TC srm 题解

版权声明:原创文章,转载要注明作者哦 https://blog.csdn.net/DYT_B/article/details/81585959

SRM 616 div.2 T3:
暴力枚举,大力分类讨论。
枚举一下两个L型相交的的情况,一共4种。
代码如下:

void doit(int x,int y,int x1,int y1){
	if (x>x1) {swap(x,x1); swap(y,y1);}
	if (x==x1) {if (y>y1) swap(y,y1); ans+=1ll*min(y1-y-1,sum_l[x][y])*sum_u[x][y]*sum_u[x1][y1]*sum_l[x1][y1]; return;}
	if (y==y1) {if (x>x1) swap(x,x1); ans+=1ll*min(x1-x-1,sum_u[x1][y1])*sum_l[x1][y1]*sum_u[x][y]*sum_l[x][y]; return;}
	ans+=1ll*sum_l[x][y]*sum_u[x][y]*sum_l[x1][y1]*sum_u[x1][y1];
	if (y1<=y) return;
	if (x>=x1-sum_u[x1][y1]&&y+sum_l[x][y]>=y1) ans-=1ll*sum_u[x][y]*sum_l[x1][y1]*(y+sum_l[x][y]-y1+1)*(x-x1+sum_u[x1][y1]+1);
}
long long TwoLLogo::countWays(vector <string> grid) {
	n=grid.size(),m=grid[1].length(),ans=0;
	memset(sum_l,0,sizeof(sum_l));
	memset(sum_u,0,sizeof(sum_u));
    for (int i=1;i<=n;i++)
    for (int j=1;j<=m;j++)
    if (grid[i-1][j-1]=='.') sum_l[i][j]=sum_u[i][j]=1;
    else sum_l[i][j]=sum_u[i][j]=0;

    for (int i=1;i<=n;i++)
    for (int j=m;j>=1;j--)
    if (grid[i-1][j-1]=='.') sum_l[i][j]+=sum_l[i][j+1],sum_u[i][j]+=sum_u[i-1][j];

    for (int i=1;i<=n;i++)
    for (int j=1;j<=m;j++)
    if (grid[i-1][j-1]=='.') sum_l[i][j]--,sum_u[i][j]--;

	for (int i=1;i<=n;i++)
	for (int j=1;j<=m;j++)
	if (grid[i-1][j-1]=='.')
	for (int i1=1;i1<=n;i1++)
	for (int j1=1;j1<=m;j1++)
	if (grid[i1-1][j1-1]=='.'&&(i!=i1||j!=j1)) doit(i,j,i1,j1);
	return ans/2;
}

SRM 616 Div 1 T1:
我们发现题目中给的所有的量都比较小,那么考虑到volume的加和有循环节,而且这个循环节一定很小。那么只要枚举从1到1e6的所有时间节点,然后看一看最小的S就好了。
最后还有一个-1的情况没有判断,我们只要发现ans在前一些循环节每次都变大,那么肯定就有一个时刻,使任意s,都能在某一时刻醒来。

代码如下:

int WakingUp::maxSleepiness(vector <int> period, vector <int> start, vector <int> volume, int D) {
	ans=0; int sumt=0;
   	n=period.size();
   	memset(sum,0,sizeof(sum));
   	for (int j=1;j<=n;j++){
   		for (int i=start[j-1];i<=maxn;i+=period[j-1])
   			sum[i]+=1ll*volume[j-1];
  	}
  	for (int i=1;i<=maxn;i++) sum[i]+=sum[i-1];
  	for (int i=1;i<=maxn;i++)
 	if (sum[i]-1ll*D*i>ans) ans=sum[i]-1ll*D*i,sumt++;
  	if (sumt<30) return ans; return -1;
}

SRM 616 Div 1 T2:
这题重点在于找到一个最优的方案。区分两种硬币,可以通过比较硬币的数量,因为可以取多次,我们可以做到每次取某种硬币的数量不同。对于某种硬币,我们可以比较几次取它的数量所构成的数量序列来确定它的种类。
如果每种硬币与比它币值大的硬币的最小币值比为 d d ,取n次最多可以区分出 d n 1 d^n-1 种硬币。

代码如下:

int ColorfulCoins::minQueries( vector<long long> values ) {
	int n=values.size(),ans=0;
	if (n==1) return 1;
	hsh.clear();
	for (int i=0;i<n-1;i++) hsh[values[i+1]/values[i]]++;
	map<long long,long long>::iterator it;
	long long y=0;
	for (it=hsh.begin();it!=hsh.end();it++){
		int sum=0; y+=it->second;
		long long x=1ll,w=it->first;
		while (x<=y) x*=w,sum++;
		ans=max(ans,sum);
	}
	return ans;
}

SRM 617 Div.2 T3:
我们发现这题就是先按每一种人数x切成相应的n/x块,然后再去掉重复的部分。
那么哪些是重复的部分呢,必然是某一些的lcm。
那么这就可以想到容斥专题中的一题目,容斥一下。
我们先求出n的因子i,然后求出每一个因子的质因子的个数,如果是奇数就加上n/i,否则减去n/i,但是如果有平方因子的话就不加也不减。这就相当于莫比乌斯函数的手动版= =。然后要特判一下,如果人数是n要跳过。

代码如下:

int make(int x){
	int sq=sqrt(x),sum=0;
	for (int i=2;i<=sq;i++)
	if (x%i==0) {
		int s=0;
		while (x%i==0) {x/=i; s++;}
		if (s>1) return 0;
		sum++;
	}
	if(x != 1) sum ++;
	if (sum%2==1) return 1; else return -1;
}
int MyVeryLongCake::cut( int n ) {
	int sq=sqrt(n),ans=0;
	for (int i=1;i<=sq;i++)
	if (n%i==0){
		if (i!=1) ans+=make(i)*n/i;
		if (n/i!=i) ans+=make(n/i)*i;
	}
	return ans;
}

SRM 617 Div.1 T1:
和Div.2 T3是一样的。。。
代码如下:

int make(int x){
	int sq=sqrt(x),sum=0;
	for (int i=2;i<=sq;i++)
	if (x%i==0) {
		int s=0;
		while (x%i==0) {x/=i; s++;}
		if (s>1) return 0;
		sum++;
	}
	if(x != 1) sum ++;
	if (sum%2==1) return 1; else return -1;
}
int MyVeryLongCake::cut( int n ) {
	int sq=sqrt(n),ans=0;
	for (int i=1;i<=sq;i++)
	if (n%i==0){
		if (i!=1) ans+=make(i)*n/i;
		if (n/i!=i) ans+=make(n/i)*i;
	}
	return ans;
}

SRM 617 Div.1 T2:
这题首先要抽象出一个模型来,其实可以把人看作点,其实就是一个无向图中,把所有无向边都变成有向边,使得 \sum 每个点的入度-出度的绝对值 最小。
然而到这里就容易卡壳。
其实入度和出度的差值,这让我们想到了欧拉回路。我们可以这样思考,每一次到一个点必定是从某条边进入,某一条边出来,一个入度一个出度相抵消,不会对答案产生影响,会对答案产生影响的只有最后剩下的一条边或者没有。那么其实最后的答案也确定了,就是度数为1的点的数量。
那么怎样求出一个方案呢,只要把这个图先“补全”,变成欧拉回路,也就是给两个度数为奇数的点连一条边。然后直接按照任意顺序遍历每一条边,定一个方向就ok了。

代码如下:

#include <bits/stdc++.h>
using namespace std;
const int maxm=10005,maxn=55;
int n,tot,lnk[maxn],son[2*maxm],nxt[2*maxm],w[2*maxm],id[2*maxm],inn[maxn];
vector<int> ans;
bool vis[2*maxm];
class PieOrDolphin {
	public:vector <int> Distribute( vector <int> choice1, vector <int> choice2 );
};
void clr(){
	tot=0;
	memset(lnk,0,sizeof(lnk));
	memset(nxt,0,sizeof(nxt));
	memset(vis,0,sizeof(vis));
	memset(inn,0,sizeof(inn));
}
void add(int x,int y,int z,int z1){
	inn[y]++,son[++tot]=y,w[tot]=z,id[tot]=z1,nxt[tot]=lnk[x],lnk[x]=tot;
}
void dfs(int x){
	for (int j=lnk[x];j;j=nxt[j])
	if (!vis[id[j]]){
		vis[id[j]]=1; ans[id[j]]=w[j];
		dfs(son[j]);
	}
}
vector <int> PieOrDolphin::Distribute( vector <int> choice1, vector <int> choice2 ) {
	ans.clear();
	n=choice1.size(); int n1=n;
	clr();
	for (int i=0;i<n;i++){
		add(choice1[i],choice2[i],1,i);
		add(choice2[i],choice1[i],2,i);
	}
	int las=0;
	for (int i=0;i<50;i++)
	if (inn[i]%2){
		if (las) {
			add(las,i,1,n1);
			add(i,las,2,n1);
			n1++,las=0;
		} else las=i;
	}
	for (int i=0;i<n1;i++) ans.push_back(0);
	for (int i=0;i<50;i++) dfs(i);
	//printf("%d %d\n",n,n1);
	for (int i=n;i<n1;i++) ans.pop_back();
	return ans;
}

SRM 618 Div.2 T3:
还以为是什么高端数学题。。。
n=8!!!
直接爆搜就好辣。
代码如下:

扫描二维码关注公众号,回复: 4680704 查看本文章
#include <bits/stdc++.h>
using namespace std;
const int maxn=15;
int n;
map <vector<int>,bool> vis;
bool check;
class MovingRooksDiv2 {
	public:string move( vector <int> Y1, vector <int> Y2 );
};
void dfs(vector<int> a,vector<int> b){
	if (a==b) {check=1; return;}
	if (check==1||vis[a]) return;
	vis[a]=1;
	for (int i=0;i<n-1;i++)
	for (int j=i+1;j<n;j++)
	if (a[i]>a[j]){
		swap(a[i],a[j]);
		dfs(a,b);
		swap(a[i],a[j]);
	}
}
string MovingRooksDiv2::move( vector <int> Y1, vector <int> Y2 ) {
	check=0; n=Y1.size();
	dfs(Y1,Y2);
	if (check) return "Possible"; else return "Impossible";
}

SRM 618 Div.1 T1:
我们可以将每一对父母连边, 那么就变成了求一个无向图中的染色问题。
因为图有可能不连通,数据范围又小,直接暴力枚举每一个点,刷DFS。看是否一个点会同时染两种颜色就ok了。
代码如下:

#include <bits/stdc++.h>
using namespace std;
const int maxn=105;
int n,tot,lnk[maxn],son[2*maxn],nxt[2*maxn],col[maxn];
bool vis[2*maxn],check;
class Family {
	public:string isFamily( vector <int> parent1, vector <int> parent2 );
};
void add(int x,int y){
	son[++tot]=y,nxt[tot]=lnk[x],lnk[x]=tot;
	son[++tot]=x,nxt[tot]=lnk[y],lnk[y]=tot;
}
void dfs(int x){
	for (int j=lnk[x];j;j=nxt[j])
		if (!vis[j]){
			vis[j]=1;
			if (col[son[j]]!=-1&&col[son[j]]!=(col[x]^1)) {
				check=1; break;
			} else col[son[j]]=col[x]^1;
			dfs(son[j]);
		}
}

SRM 618 Div.1 T2:
这题意思大概就是:
不能有两个连续的相同数字,相同序列(长度>=2)不能出现两次及以上,同一种数字不能出现4次及以上,并且要最长。
这是典型的“放数”问题。
我们先不考虑是那种数字放在一个位置(也就是最后答案×n!),这样能方便递推。
然后考虑放到第i种数时,也就是f[i]。
因为同一种数字不能出现4次,所以必定是三次最优秀。
因为不考虑哪种数字放在那些位置,所以默认当前数字放在最外面
方案数就是:f[1]*f[i-2]+f[2]*f[i-3]+…+f[i-2]*f[1],枚举插空方法。
当然也可以是:接在前3×(i-1)的后面,也就是f[i-1]
累计一下,最后×n!即可。
代码如下:

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int tt=1e9+7,maxn=5005;
ll f[maxn];
class LongWordsDiv1 {
	public:int count( int n );
};
int LongWordsDiv1::count( int n ) {
	f[1]=1;
	for (int i=2;i<=n;i++){
		f[i]=f[i-1];
		for (int j=0;j<i;j++)
			f[i]=(f[i]+f[j]*f[i-j-1])%tt;
	}
	for (int i=1;i<=n;i++) f[n]=f[n]*i%tt;
	return f[n];
}

SRM 619 Div.2 T3:
这题是一个trick题。。。
我们假设都不选,那么答案是 e a r n i n g [ i ] [ j ] ( i &lt; j ) \sum-earning[i][j](i&lt;j)
然后考虑对于每一对数(x,y),有四种选法:
1.都不选: + 0 +0
2.选1个(假设选x): + e a r n i n g [ x ] [ y ] v a l u e [ x ] +earning[x][y]-value[x]
3.选2个: + 2 e a r n i n g [ x ] [ y ] v a l u e [ x ] v a l u e [ y ] +2*earning[x][y]-value[x]-value[y]
也可以写作: + ( e a r n i n g [ x ] [ y ] v a l u e [ x ] ) + ( e a r n i n g [ x ] [ y ] v a l u e [ y ] ) +(earning[x][y]-value[x])+(earning[x][y]-value[y])
这样对于某一个x,如果选了它,答案必定加上$(\sum_1^n earning[x][i])-value[x] $
这样就可以贪心的选择辣。
代码如下:

#include <bits/stdc++.h>
using namespace std;
int n;
class EmployManager {
	public:int maximumEarnings( vector <int> value, vector <string> earning );
};
int EmployManager::maximumEarnings( vector <int> value, vector <string> earning ) {
	n=value.size(); int ans=0;
	for (int i=0;i<n-1;i++)
	for (int j=i+1;j<n;j++)
		ans-=earning[i][j]-'0';
	for (int i=0;i<n;i++){
		int sum=0;
		for (int j=0;j<n;j++)
		sum+=earning[i][j]-'0';
		if (sum>value[i]) ans+=sum-value[i];
	}
	return ans;
}

SRM 619 Div.1 T1:
这不是SB题吗。。。
直接判断奇偶性就好了啊。
特判一下特殊情况。
代码如下:

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

class SplitStoneGame {
	public:string winOrLose( vector <int> number );
};
string SplitStoneGame::winOrLose( vector <int> number ) {
	int n=number.size(),sum=0;
	if (n<3) return "LOSE";
	for (int i=0;i<n;i++) sum+=number[i];
	if (sum==n) return "LOSE";
	if (n%2==1) return "WIN"; return "LOSE";
}

猜你喜欢

转载自blog.csdn.net/DYT_B/article/details/81585959
tc