SDUT 2018 Winter Individual Contest - 1
https://cn.vjudge.net/contest/207904#problem/A
A 尺取法经典入门
题意:找一段最长的区间,该区间内的元素种类个数小于等于k
#include
using namespace std;
const int maxn= 1e6+7;
int a[maxn];
int book[maxn];
int main()
{
int n,k;
cin>>n>>k;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
memset(book,false,sizeof(book));
book[a[1]] = 1;
int sl = 1;
int cnt =1;
int rel = 1,rell = 1,relr = 1;
for(int i=2;i<=n;i++)
{
book[a[i]]++;
if(book[a[i]]==1)cnt++;
while(cnt>k)
{
book[a[sl]]--;
if(book[a[sl]]==0)cnt--;
sl++;
}
if(i-sl+1>rel)
{
rel = (i-sl+1);
rell = sl;
relr = i;
}
}
printf("%d %d\n",rell,relr);
}
C 类似于哈夫曼堆
题意:有n(1<=n<=10^5)堆石子,每堆有ai(1<=ai<=10^9)个石子,每次将一堆石子a合并到另一堆b花费为a,求把所有石子合并一起的最小花费。
不过没有那么简单,现在有m(1<=m<=10^5)个询问ki(1<=ki<=10^5),问每堆石子至多被合并ki次,求把所有石子合并在一起的最小花费。
题解:
首先想ki = 1的情形,不难想到,一堆石子被合并一次,一堆石子被合并一次…一堆石子被合并一次,这显然是让最少的石子去合并别的石子n-1次。
考虑ki = 2,一堆石子被合并二次,二堆石子被合并二次,四堆石子被合并二次…即每次*2。
很好理解,每次可以合并的堆的个数增加了,被合并的堆数也在增加。所以排序后,从大到小贪心即可。同时记录答案。
#include
using namespace std;
const int maxn = 1e6+7;
long long n,a[maxn],sum[maxn];
int i;
int main()
{
while (cin>>n)
{
for(i=1; i<=n; i++)
scanf("%I64d",&a[i]);
sort(a+1,a+1+n);
int m;
cin>>m;
sum[0] = 0;
for(i=1; i<=n; i++)
{
sum[i] = sum[i-1]+a[i];
}
long long k;
long long data = 0;
for(i=1; iwhile(m--)
{
long long ans = 0;
cin>>i;
if(i==1)
cout<else
{
long long tn = n;
long long p = 1;
k = i;
while(tn-k>=0)
{
ans+=p*(sum[tn]-sum[tn-k]);
tn-=k;
k*=i;//该层结点的个数K^(2,4,8,16)
p++;//形成的k叉树的深度
}
if(tn>=0)//不满时加上剩下的
ans+=p*sum[tn];
cout<return 0;
}
D 二分答案经典
(注意该题卡常)
在栗树的脚下有n只松鼠。第i棵树的第一个栗子在T i秒后立即落下,而后一个P i秒后再落下一个。松鼠的“大妈妈”希望他们尽可能快的带上自己的巢穴,以免大风暴来临!所以他们正在讨论在最短的时间里等待哪些树下采取足够的栗子。移动到位的时间是零,松鼠在那之后没有任何动作。
请求
计算最短的时间(多少秒)松鼠可以采取足够的栗子。
输入
第一行包含一个整数t,即每个测试用例的数量:
第一行分别包含整数m,n,k。
第二行分别包含整数T i(i = 1..m)。
第三行分别包含整数P i(i = 1..m)。
(同一行上的每个整数由至少一个空格字符分隔,测试用例之间没有添加的行)
产量
对于每个测试用例,在一行中输出一个以最短时间计算的整数。
#include
#include
using namespace std;
const int maxn = 1e6+10;
long long a[maxn],b[maxn];
long long sum[maxn];
int n,m;
long long k;
int cmp(long long a,long long b)
{
return a>b;
}
int pan(long long t)
{
vector<long long>o;
for(int i=1;i<=m;i++)
{
if(tcontinue;
o.push_back( 1+(t-a[i])/b[i]);
}
long long Sum = 0;
sort(o.begin(),o.end(),cmp);
for(int i=0;i<=min(n,(int )o.size())-1;i++)
{
Sum+=o[i];
}
return Sum>=k;
}
int main(){
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d%d%lld",&m,&n,&k);
for(int i=1;i<=m;i++)
{
scanf("%lld",&a[i]);
}
for(int i=1;i<=m;i++)
{
scanf("%lld",&b[i]);
}
long long l = 1,r = 1e9,ans = r;
while(l<=r)
{
long long mid = (l+r)/2;
//cout<
int f = pan(mid);
if(f){
ans = mid;
r = mid-1;
}
else l=mid+1;
}
cout<return 0;
}
G
题意:小象喜欢玩彩卡。
他有n张牌,每张都有两种颜色(正面的颜色和背面的颜色)。最初,所有牌都在正面朝上放在桌子上。小象可以把任何一张牌转到另一边。小象认为,如果至少有一半的牌具有相同的颜色(每张牌的上边的颜色被考虑),桌上的一套牌是有趣的。
帮助小象找到所需的最少的动作,使这一套n卡搞笑。
输入
第一行包含一个整数Ñ (1≤ ñ ≤10 5) -的卡的数量。以下n行包含所有卡的描述,每行一张卡。这些卡片是由一对不超过10 9 - 双方颜色的正整数描述的。一行中的第一个数字是卡片正面的颜色,第二个是背面的数字。卡片正面的颜色可能与卡片背面的颜色一致。
行中的数字由单个空格分隔。
产量
在一行上打印一个整数 - 寻求的最小移动次数。如果不可能让这个设置变得有趣,请打印-1。
离散化一下然后暴力求最大值即可
#include
using namespace std;
const int maxn = 1100000;
int Twos[maxn] ={0} ;
int Front[maxn] ={0};
int a[maxn],b[maxn],c[maxn];
int main() {
int n,req;
cin>>n;
req = (n+1)/2;
int top =0;
for(int i=1;i<=n;i++)
{
scanf("%d%d",&a[i],&b[i]);
c[top++] = a[i];
c[top++] = b[i];
}
sort(c,c+top);
top = unique(c,c+top)-c;
for(int i=1;i<=n;i++)
{
int x =lower_bound(c,c+top,a[i])-c;
int y = lower_bound(c,c+top,b[i])-c;
Twos[x]++;
Twos[y]++;
Front[x]++;
if(x==y)Twos[x]--;
}
int ans = 1e6+7;
for(int i=0;iif(Twos[i]>=req) {
ans = min(ans,max(req-Front[i],0));
}
}
if(ans==(1e6+7))printf("-1\n");
else cout<return 0;
}