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
d,取n次最多可以区分出
d
n
−
1
d^n-1
dn−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!!!
直接爆搜就好辣。
代码如下:
#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
<
j
)
\sum-earning[i][j](i<j)
∑−earning[i][j](i<j)
然后考虑对于每一对数(x,y),有四种选法:
1.都不选:
+
0
+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]
+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]
+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])
+(earning[x][y]−value[x])+(earning[x][y]−value[y])
这样对于某一个x,如果选了它,答案必定加上
(
∑
1
n
e
a
r
n
i
n
g
[
x
]
[
i
]
)
−
v
a
l
u
e
[
x
]
(\sum_1^n earning[x][i] )-value[x]
(∑1nearning[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";
}