不知不觉,这场已经是很久以前了,一直想补这场,倒不是这场多么特殊,而是d题的随机化,确实是一个比较特殊的算法
A. Diversity
思路:水题,统计字母种类
/*
Author Owen_Q
*/
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1010;
char s[maxn];
bool in[maxn];
int main()
{
int k;
while(scanf("%s%d",s,&k)!=EOF)
{
memset(in,false,sizeof(in));
int num = 0;
int len = strlen(s);
for(int i=0;i<len;i++)
{
if(!in[s[i]-'a'])
{
in[s[i]-'a'] = true;
//cout << "*" << s[i] << "*" << endl;
num++;
}
}
//cout << num <<" "<< k << " "<<len << endl;
if(k>len)
{
printf("impossible\n");
}
else if(k<=num)
{
printf("0\n");
}
else
{
printf("%d\n",k-num);
}
}
return 0;
}
B. Rectangles
思路:简单排列组合,按行列运用乘法原理,最后做个容斥就好了
然而,这题的细节就在于运算符的优先级,按位运算符的优先级相当低,因此对于数字按位操作前,将常数先转成long long才可防溢出
/*
Author Owen_Q
*/
#include <bits/stdc++.h>
using namespace std;
const int maxn = 55;
long long row[maxn][2];
long long line[maxn][2];
long long min2[60];
int main()
{
//cout << (1L<<1L) << " " << (1L<<31L) << " " << (1L<<32L) << " " << (1L<<33L) << endl;
long long be = 1;
for(int i=0;i<55;i++)
{
min2[i] = be;
be *= 2L;
//cout << min2[i] << endl;
}
int n,m;
while(scanf("%d%d",&n,&m)!=EOF)
{
memset(row,0,sizeof(row));
memset(line,0,sizeof(line));
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
{
int temp;
scanf("%d",&temp);
if(temp==1)
{
row[i][1]++;
line[j][1]++;
}
else
{
row[i][0]++;
line[j][0]++;
}
}
}
long long sum = -1L * m * n;
for(int i=0;i<n;i++)
{
sum += /*(1L << row[i][0])*/min2[row[i][0]];
sum --;
sum += /*(1L << row[i][1])*/min2[row[i][1]];
sum --;
}
for(int j=0;j<m;j++)
{
sum += /*(1L << line[j][0])*/min2[line[j][0]];
sum --;
sum += /*(1L << line[j][1])*/min2[line[j][1]];
sum --;
}
printf("%I64d\n",sum);
}
return 0;
}
C. Sorting by Subsequences
总结:找规律,数列排序好后寻找最少交换策略,即并查集合并交换元素,即可得到结果
/*
Author Owen_Q
*/
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+10;
int pre[maxn];
int finds(int x)
{
int r = x;
while(pre[r]!=r)
{
r = pre[r];
}
int i=x ,j;
while(pre[i]!=r)
{
j = pre[i];
pre[i] = r;
i = j;
}
return r;
}
void join(int x,int y)
{
int fx = finds(x);
int fy = finds(y);
if(fx > fy)
{
int temp = fx;
fx = fy;
fy = temp;
}
if(fx!=fy)
{
pre[fy] = fx;
}
}
map <int,int> dic;
int a[maxn];
int orda[maxn];
int num[maxn];
vector <int> re[maxn];
int main()
{
int n;
while(scanf("%d",&n)!=EOF)
{
for(int i=1;i<=n;i++)
{
pre[i] = i;
re[i].clear();
}
dic.clear();
memset(num,0,sizeof(num));
for(int i=0;i<n;i++)
{
scanf("%d",&a[i]);
orda[i] = a[i];
dic[a[i]] = i+1;
}
sort(orda,orda+n);
int t = n;
for(int i=0;i<n;i++)
{
if(finds(dic[a[i]]) != finds(dic[orda[i]]))
{
join(dic[a[i]],dic[orda[i]]);
t--;
}
}
for(int i=1;i<=n;i++)
{
num[finds(i)]++;
re[finds(i)].push_back(i);
}
printf("%d\n",t);
for(int i=1;i<=n&&t>0;i++)
{
if(num[i]>0)
{
t--;
printf("%d ",num[i]);
for(int j=0;j<num[i]-1;j++)
{
printf("%d ",re[i][j]);
}
printf("%d\n",re[i][num[i]-1]);
}
}
}
return 0;
}
D. Interactive LowerBound
思路:这场比赛的精华之处就在这题了吧
链表查找题,交互,每次可以访问链表的一个元素以及其下一个元素所在位置,2e3步内找出长达5e4的链表内特定元素
由于链表未知,内部结构也未知,因此实在想不到什么O(n)以内的算法,眼看着就会tle,于是随机化大法的神奇之处就出现了
先随机寻找1e3个点,在选择最近的点往后延伸1e3个点,最优可以在2e3的时间内完成1e6的覆盖,这就是随机化的神奇之处了
/*
Author Owen_Q
*/
#include <bits/stdc++.h>
using namespace std;
const int maxn = 5e4+10;
typedef struct NODE
{
int val;
int nex;
}Node;
vector<int> ran;
bool vis[maxn];
Node askre[maxn];
Node ask(int k)
{
Node n;
printf("? %d\n",k);
fflush(stdout);
scanf("%d%d",&n.val,&n.nex);
return n;
}
int main()
{
int n,start,x;
srand(time(0));
/*for(int i=0;i<5;i++)
{
if(i%2==0)
{
continue;
}
}*/
scanf("%d%d%d",&n,&start,&x);
ran.clear();
memset(vis,false,sizeof(vis));
for(int i=1;i<=n;i++)
{
ran.push_back(i);
}
random_shuffle(ran.begin(),ran.end());
vis[start] = true;
Node p,stp;
stp = ask(start);
if(x>(stp.val)&&stp.nex==-1)
{
printf("! -1\n");
fflush(stdout);
}
else if(x<stp.val)
{
printf("! %d\n",stp.val);
fflush(stdout);
}
else
{
vis[start] = true;
for(int i=0,t=0;t<1000-1&&i<n;i++,t++)
{
if(vis[ran[i]])
{
t--;
continue;
}
vis[ran[i]] = true;
p = ask(ran[i]);
if((p.nex) == -1 && x>(p.val))
{
printf("! -1\n");
fflush(stdout);
exit(0);
}
else if(p.val<=x&&p.val>stp.val)
{
stp = p;
}
}
if(stp.val == x)
{
printf("! %d\n",stp.val);
fflush(stdout);
}
else
{
for(int i=0;i<1000-1;i++)
{
if((stp.nex) == -1&&stp.val < x)
{
printf("! -1\n");
fflush(stdout);
break;
}
stp = ask(stp.nex);
if(stp.val>=x)
{
printf("! %d\n",stp.val);
fflush(stdout);
break;
}
}
}
}
return 0;
}
最后,补充两个知识点
(1)fflush(stdout)清空缓存区
(2)random_shuffe(a.begin(),a.end())随机生成一个1~n的全排列,n为数组长度(n = a.end() - a.begin())别忘了播种srand(time(0)),否则生成的仅仅是伪随机数