目录
糖果传递(排序、中位数、环形均分纸牌,牛客)
题解:
1、关于模型:环状均分纸牌
显然最后每个人都剩下sum/n张纸牌,p[i]表示这个人给下一个人多少张纸牌
显然p[i]=a[i]+p[i-1]-sum/n
p[i]-p[i-1]=a[i]-sum/n,所以p[i]-p[i-1]+p[i-1]-p[i-2]+…-p[1] = sigma(i)(a[i]-sum/n)
即p[i]=sigma(i)(a[i]-sum/n)+p[1]
显然sigma(i)(a[i]-sum/n)是定值,所以p[1]是所有sigma(i)(a[i]-sum/n)的中位数就好了
……
2、我们用xi表示第i个小朋友给第i−1个小朋友的糖果数,其中x1表示第1个小朋友给第n个朋友的糖果数,那么最终答案即为|x1|+|x2|+…|xn|。
我们假设最后每个人剩avg个糖果,那么可以得到:
对于第一个小朋友:a1+x2−x1=avg
对于第二个小朋友:a2+x3−x2=avg
…
对于最后一个小朋友:an+x1−xn=avg
整理一下即可得到:
x2=avg−a1+x1
x3=avg−a2+x2=2avg+x1−a2−a1
…
xn=avg−an−1+xn−1=(n−1)avg+x1−∑n−1i=1ai
我们设c[1]=a[1]-ave
c[2]=c[1]+a[2]-ave
则有c[i]=c[i-1]+a[i]-ave
上述式子即可转化为求解|x1|+|x1−c1|+|x1−c2|…+|x1−cn−1|的最小值,那么直接令x1等于c的中位数即可。
3、货仓选址:给定数轴上的n个点,找出一个到他们的距离之和尽量小的点,而这个点就是这些数中的中位数,证明(随便找一个点,若是左边的点比右边多,那么往左移距离和就会减少,反之右移距离减少,一定是中位数最优)。
4、数据范围有毒。
1、至少1e6
2、记得开long long
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1000010;
int a[N],c[N];
int main(){
int n;
cin>>n;
ll ans=0;
ll avg=0;
for(int i=0;i<n;i++){
cin>>a[i];
avg+=a[i];
}
avg/=n;
for(int i=1;i<n;i++)
c[i]=c[i-1]+a[i]-avg;
sort(c,c+n);
/*到这里就是一个货仓选址问题。 */
for(int i=0;i<n;i++){
ans+=abs(c[i]-c[n/2]);
}
printf("%lld\n",ans);
return 0;
}
城市扩建(无限包含自身的分形,牛客)
/*
std::pair主要的作用是将两个数据组合成一个数据,两个数据可以是同一类型或者不同类型。
例如std::pair<int,float> 或者 std::pair<double,double>等。
pair实质上是一个结构体,其主要的两个成员变量是first和second,这两个变量可以直接使用。
初始化一个pair可以使用构造函数,也可以使用std::make_pair函数,make_pair函数的定义如下:
template pair make_pair(T1 a, T2 b) { return pair(a, b); }
一般make_pair都使用在需要pair做参数的位置,可以直接调用make_pair生成pair对象。
另一个使用的方面就是pair可以接受隐式的类型转换,这样可以获得更高的灵活度。但是这样会出现如下问题:
例如有如下两个定义:
std::pair<int, float>(1, 1.1);
std::make_pair(1, 1.1);
其中第一个的second变量是float类型,而make_pair函数会将second变量都转换成double类型。
这个问题在编程是需要引起注意。
*/
#include<bits/stdc++.h>
#include<iostream>
#define ll long long
#define pll pair<ll,ll>
using namespace std;
pll calc(ll n,ll m){
if(n==0)return make_pair(0,0);
ll len=1ll<<(n-1),cnt=1ll<<(2*n-2);
auto pos=calc(n-1,m%cnt);
auto x=pos.first,y=pos.second;
auto z=m/cnt;
if(z==0)return make_pair(y,x);
if(z==1)return make_pair(x,y+len);
if(z==2)return make_pair(x+len,y+len);
if(z==3)return make_pair(2*len-y-1,len-x-1);
}
int main(){
int t;
cin>>t;
while(t--){
ll n,a,b;
cin>>n>>a>>b;
auto ac=calc(n,a-1);
auto bc=calc(n,b-1);
double x=ac.first-bc.first,y=ac.second-bc.second;
printf("%.0lf\n", sqrt(x * x + y * y) * 10);
}
return 0;
}
round corridor(公约数、思考题,CF)
题目http://codeforces.com/contest/1200/problem/C
两层转盘分格数的最大公约数n,是它们的分隔数,未分隔开的两层转盘可达。可求未分隔开的两层转盘各自的分块数nn(x/n),只需根据x坐标判断内层还是外层,据y坐标除以其所在转盘的分块数mm,得到编号(<mm)。比较内外转盘的编号是否相等即可。
#include <map>
#include <set>
#include <cmath>
#include <queue>
#include <string>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define ll long long
ll gcd(ll a,ll b)
{
return b==0?a:gcd(b,a%b);
}
int main()
{
ll n,m,t;
scanf("%lld%lld%lld",&n,&m,&t);
ll g=gcd(n,m);
ll nn=n/g;
ll mm=m/g;
for(int i=0;i<t;i++)
{
ll a,b,c,d,nb=0,nd=0;
scanf("%lld%lld%lld%lld",&a,&b,&c,&d);
if(a==1) nb=(b-1)/nn;
if(a==2) nb=(b-1)/mm;
if(c==1) nd=(d-1)/nn;
if(c==2) nd=(d-1)/mm;
if(nb==nd)
printf("YES\n");
else
printf("NO\n");
}
return 0;
}
Compress Words
(暴力单词去重,CF)
题目:http://codeforces.com/contest/1200/problem/E
暴力方法,运行998ms,差一点点就T了。会有更简洁的方法的。
#include <map>
#include <set>
#include <cmath>
#include <queue>
#include <string>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
string a,ans,t;
int main(){
ios::sync_with_stdio(false);
int n;
cin>>n;
cin>>ans;
for(int i=1;i<n;i++)
{
int len=(int)ans.size();
cin>>a;
while(!a.empty())
{
//cout<<a<<' '<<t<<' '<<ans<<endl;
if(~ int(ans.find(a,len-a.size())))
{
break;
}
else
{
t.push_back(a.back());
a.pop_back();
}
}
while(!t.empty())
{
ans.push_back(t.back());
t.pop_back();
}
//cout<<ans<<endl;
}
cout<<ans<<endl;
return 0;
}
(巧妙单词去重,CF)
看了别人的题解,好巧妙…不过还是暴力好理解
#include<bits/stdc++.h>
using namespace std;
int net[1000000];
string s;
string ans;
void get_next(int len,string w)
{
net[0]=-1;
int i=0,j=-1;
while(i<len)
{
if(j==-1||w[i]==w[j])
{
i++;
j++;
net[i]=j;
}
else
j=net[j];
}
}//net[len]时重复的字母数
int n;
int main()
{
std::ios::sync_with_stdio(false);
cin.tie(NULL);
cin>>n;
cin>>ans;
int cnt=ans.length();
for(int i=2;i<=n;i++)
{
cin>>s;
int tt=s.length();
cnt=min(cnt,tt);//只需要拿出ans中跟输入的长度相等的字串比较即可
s+="#";
//cout<<cnt<<'@'<<endl;
s+=ans.substr(ans.length()-cnt);//s加上了同等长度的ans中的字串,中间#隔开,s=s#ans
//cout<<s<<'$'<<endl;
get_next(s.length(),s );
int ttt=min(net[s.length()],tt);
//cout<<net[s.length()]<<'^'<<tt<<endl;
ans+=s.substr(ttt,tt-ttt);//ttt是两个单词重复的数量,此步将重复的去掉,以更新ans
//cout<<ans<<'%'<<endl;
cnt=ans.length();
}
cout<<ans<<endl;
return 0;
}
To The Max(矩阵前缀和,CF)
巧妙处理矩阵,for循环看的我有点晕。。
题目:https://ac.nowcoder.com/acm/contest/1004/K
/*a[i][j]代表了前i行的前j列的所有元素的和,注意DP题目数组下标从1开始,以避免一些麻烦.
*/
#include<bits/stdc++.h>
using namespace std;
int a[105][105];
int main(){
std::ios::sync_with_stdio(false);
std::cin.tie(NULL);
int n,i,j,k,t,sum,max;
while(cin>>n){
memset(a,0,sizeof(a));
for(i=1;i<=n;i++){
for(j=1;j<=n;j++){
cin>>t;
a[i][j]=a[i-1][j]+t;//输入的数加上上一行同列的数 ,列的前缀和
//cout<<a[i][j]<<"#";
}
}
max=0;
for(i=1;i<=n;i++){//第一行
for(j=i;j<=n;j++){//第二行
sum=0;
for(k=1;k<=n;k++){
t=a[j][k]-a[i-1][k];//前j行前k列的和-前i-1行前k列的和
sum+=t;
if(sum<0)sum=0;
if(sum>max)max=sum;
cout<<sum<<"*";
cout<<" ";
}
}
}
cout<<max<<endl;
}
return 0;
}
Go to School【数组排序并存下标】
https://vjudge.net/contest/341088#problem/D
/*对数组升序排序并输出其下标,使用结构体*/
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=100010;
struct node{
int a;
int b;
};
bool cmp(struct node m, struct node n) //结构体排序
{
return m.a<n.a;
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(NULL);
std::cout.tie(NULL);
int n,m;
node node[N];
int ans=0;
cin>>n;
for(int i=0;i<n;i++){
cin>>m;
node[i].a=m;
node[i].b=i+1;
}
sort(node,node+n,cmp);
for(int i=0;i<n;i++){
cout<<node[i].b<<" ";
}
cout<<endl;
return 0;
}
Disjoint Set of Common Divisors【找出A\B互质公因数】
https://vjudge.net/contest/341088#problem/E
/*我们知道对gcd(a,b)进行因数分解就能得到a,b的所有因数。但是这里需要互质的因数,所以我们这里需要对gcd(a,b)作质因数分解。
质因数分解的代码:
if(g%i!=0)ans++; //得到一个因数后,我们ans++
while(g%i==0)g/=i //后面有这句话
if(g>2)ans++ //最后还有一个质因子的话需要再++
*/
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn =1e5+10;
long long gcd(long long x,long long y)
{
if(y==0)return x;
return gcd(y,x%y);
}//最大公约数
int main()
{
long long x,y;
cin >> x >> y;
long long g=gcd(x,y);
long long ans=0;
for(long long i=2;i<=sqrt(g); i++) //对gcd(a,b)作质因数分解
{
if(g%i==0)
{
ans++;
while(g%i==0)g/=i;
}
}
if(g>2) ans++;
cout << ans +1 <<endl;
return 0;
}