Problem A
题意:有一个长度为n的随机排列以及m个min、max操作。问最后一个操作的结果的期望 * n! 的结果。
题解:
枚举k,考虑计算结果 >= k 的排列有几个。
此时数字本质上只有两类,>= k 的以及 < k的。可以2^n的枚举每个位置的数是>=k还是<k,然后对于每一个状态模拟一遍所有m个操作,如果结果为1,则说明该状态是可行的。
接下来考虑每种状态对应几个排列,若该状态中有x个1,则对应的排列个数为 x! * (n-x)! 个。现在已经计算出结果 >= k 的排列有 f[k] 个,那么答案也就是所有 f[k] 的和。整体复杂度为 O((2^n) * m)
Problem B Triangle
http://acm.hdu.edu.cn/contests/contest_showproblem.php?pid=1002&cid=846
题意:给你𝑛根棍子,问是否有其中三根能组成三角形
题解:斐波那契数列
定理:给定一个非空的由正整数构成的多重集合𝐴A,若𝐴A中元素不能组成三角形三边,则𝐴A中的最大元素max𝐴≥𝐹𝐴,maxA≥F_|A| ,其中𝐹𝐴是F_|A| 是斐波那契数列的第𝑛n项
/*
*@Author: STZG
*@Language: C++
*/
#include <bits/stdc++.h>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<string>
#include<vector>
#include<bitset>
#include<queue>
#include<deque>
#include<stack>
#include<cmath>
#include<list>
#include<map>
#include<set>
//#define DEBUG
#define RI register int
using namespace std;
typedef long long ll;
//typedef __int128 lll;
const int N=5000000+10;
const int M=100000+10;
const int MOD=1e9+7;
const double PI = acos(-1.0);
const double EXP = 1E-8;
const int INF = 0x3f3f3f3f;
int t,n,m,k,q;
int ans,cnt,flag,temp,sum;
int a[N];
char str;
struct node{};
int main()
{
#ifdef DEBUG
freopen("input.in", "r", stdin);
//freopen("output.out", "w", stdout);
#endif
//ios::sync_with_stdio(false);
//cin.tie(0);
//cout.tie(0);
//scanf("%d",&t);
//while(t--){
while(~scanf("%d",&n)){
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
if(n<3){
cout<<"NO"<<"\n";
continue;
}
if(n>60){
cout<<"YES"<<"\n";
continue;
}
sort(a+1,a+n+1);
flag=0;
for(int i=1;i<n-1;i++){
if(a[i]-a[i+2]+a[i+1]>0){
cout<<"YES"<<"\n";
flag=1;
break;
}
}
if(!flag)
cout<<"NO"<<"\n";
}
//}
#ifdef DEBUG
printf("Time cost : %lf s\n",(double)clock()/CLOCKS_PER_SEC);
#endif
//cout << "Hello world!" << endl;
return 0;
}
Problem C
题意:涛涛有一个n行m列的卡牌阵,每个位置是0或1,0代表卡牌背面朝上,1代表卡牌正面朝下,涛涛每次可以选择一个小矩阵,把这个矩阵内所有卡牌翻转,问最后可以得到多少本质不同的卡牌阵。
题解:
先计算出取一个矩形的方案是one=
再计算出取两个不同矩形的方案是two=
再减去重复的方案
考虑4种会重复的形状,算出相应系数
Problem D Monitor
http://acm.hdu.edu.cn/contests/contest_showproblem.php?pid=1004&cid=846
题意:在一个面积不超过n*m的矩形上,有p个矩形A,问之后的q个矩形B能否被之前的A全部覆盖(每个B的点都在至少一个A中)
题解:树状数组(区间更新+区间查询)
由于n*m,p,q的范围过大,于是考虑O(n*m+p+q)的做法。
对于A类矩形(x1,y1,x2,y2),我们只需要在(x1,y1),(x2+1,y2+1)处+1,在(x1,y2+1)(x2+1,y1)处-1
之后对整个面积求一个前缀和。则大于0的地方就是被A类矩形覆盖的点。
把值大于0的地方变成1,再一次求一次前缀和,处理好后即可在O(1)的时间算出一个矩形内被覆盖的点的数量。
/*
*@Author: STZG
*@Language: C++
*/
#include <bits/stdc++.h>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<string>
#include<vector>
#include<bitset>
#include<queue>
#include<deque>
#include<stack>
#include<cmath>
#include<list>
#include<map>
#include<set>
//#define DEBUG
#define RI register int
using namespace std;
typedef long long ll;
//typedef __int128 lll;
const int N=20000000+10;
const int MOD=1e9+7;
const double PI = acos(-1.0);
const double EXP = 1E-8;
const int INF = 0x3f3f3f3f;
int t,n,m,k,q;
int ans,cnt,flag,temp;
int x,y,x2,y2;
int lowbit(int x){
return x&(-x);
}
int a[N];
void updata(int x,int y,int k){
if(x>n||y>m)return;
a[(x-1)*m+y]+=k;
}
ll query(int x,int y){
if(x==0||y==0)return 0;
return a[(x-1)*m+y];
}
int main()
{
#ifdef DEBUG
freopen("input.in", "r", stdin);
//freopen("output.out", "w", stdout);
#endif
while(~scanf("%d%d",&n,&m)){
memset(a,0,sizeof(a));
scanf("%d",&k);
while(k--){
scanf("%d%d%d%d",&x,&y,&x2,&y2);
updata(x2+1,y2+1,1);
updata(x,y,1);
updata(x,y2+1,-1);
updata(x2+1,y,-1);
}
for(int i = 1 ; i <=n ; i++)
for(int j = 1 ; j <=m ; j++)
a[(i-1)*m+j]=query(i,j-1)+query(i-1,j)-query(i-1,j-1)+a[(i-1)*m+j];
for(int i = 1 ; i <=n ; i++)
for(int j = 1 ; j <=m ; j++)
a[(i-1)*m+j]=a[(i-1)*m+j]!=0;
for(int i = 1 ; i <=n ; i++)
for(int j = 1 ; j <=m ; j++)
a[(i-1)*m+j]=query(i,j-1)+query(i-1,j)-query(i-1,j-1)+a[(i-1)*m+j];
scanf("%d",&q);
while(q--){
scanf("%d%d%d%d",&x,&y,&x2,&y2);
int res=query(x-1,y-1)-query(x-1,y2)-query(x2,y-1)+query(x2,y2);
//cout<<res<<endl;
printf("%s\n",(res==(x2-x+1)*(y2-y+1))?"YES":"NO");
}
}
//cout << "Hello world!" << endl;
return 0;
}
Problem E Coding Problem
http://acm.hdu.edu.cn/contests/contest_showproblem.php?cid=846&pid=1005
题意:将字符串每个字符ASCII的二进制翻转码组成的字符串以每6个一组输出。
题解:模拟
/*
*@Author: STZG
*@Language: C++
*/
#include <bits/stdc++.h>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<string>
#include<vector>
#include<bitset>
#include<queue>
#include<deque>
#include<stack>
#include<cmath>
#include<list>
#include<map>
#include<set>
//#define DEBUG
#define RI register int
#define endl "\n"
using namespace std;
typedef long long ll;
//typedef __int128 lll;
const int N=2400000+10;
const int M=100000+10;
const int MOD=1e9+7;
const double PI = acos(-1.0);
const double EXP = 1E-8;
const int INF = 0x3f3f3f3f;
int t,n,m,k,p,l,r,u,v;
int ans,cnt,flag,temp,sum;
char str[N];
struct node{};
int main()
{
#ifdef DEBUG
freopen("input.in", "r", stdin);
//freopen("output.out", "w", stdout);
#endif
//ios::sync_with_stdio(false);
//cin.tie(0);
//cout.tie(0);
//scanf("%d",&t);
//while(t--){
//scanf("%d",&n);
cin>>str;
int len=strlen(str);
cnt=0;
temp=0;
for(int i=0;i<len;i++){
for(int j=0;j<8;j++){
sum=(str[i]&(1<<j))>0;
temp=temp*2+sum;
if((i*8+j+1)%6==0){
printf("%d%c",temp,' ');
temp=0;
}
}
}
//}
#ifdef DEBUG
printf("Time cost : %lf s\n",(double)clock()/CLOCKS_PER_SEC);
#endif
//cout << "Hello world!" << endl;
return 0;
}
Problem F
题意:给一棵边带权的树,定义两个点之间的距离为路径上边权的 max – min。一开始树上有一些黑点,其余的为白点,对于每个白点计算出距离它最远的黑点到它的距离 dis[u],现在要求再添加一个黑点,使得dis数组的最小值最大。
题解:首先需要计算出距离每个白点最远的黑点到它的距离max_dis。将所有点按照这个距离从小到大排序得到一个有序序列p[1..n]。接下来枚举一个将要变成黑点的白点,并且从小到大枚举答案Ans,并加入所有max_dis <= Ans的白点,查询当前点到所有已经加入白点的最小距离,若该值大于 Ans 则说明Ans可以扩大。由于Ans 只会从1到n扩大一次,所以整体上的复杂度是线性的。
求最大距离和最小距离是经典问题,可以直接利用点分治解决。
复杂度:Ο(n log^2n )
Problem G
题意:求至多对给定串修改一个字符情况下所有的中点不重复的对称的回文的子串的平方的和最大是多少。
题解:计算出把第i个字符变成字符j的收益contr[i][j]。以及把第i个字符更改导致的损失del[i]。
字符串是对称的,每次倍增考虑f[i][j],即第j字符是否是长为2𝑖 – 1特殊串的中心。如果是则f[i][j]为1,不是为0.
现在分类讨论这个以j为中心,长度为2^i-1的字符串。
定义左串是其左边(2^(i-1)-1)的串,右串同理。
Problem H Clumsy Keke
http://acm.hdu.edu.cn/contests/contest_showproblem.php?pid=1008&cid=846
题意:给你一个立方块堆的三视图,求出它的体积(若有多个满足,则求最大的那个)。
题解:枚举+思维
/*
*@Author: STZG
*@Language: C++
*/
#include <bits/stdc++.h>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<string>
#include<vector>
#include<bitset>
#include<queue>
#include<deque>
#include<stack>
#include<cmath>
#include<list>
#include<map>
#include<set>
//#define DEBUG
#define RI register int
#define endl "\n"
using namespace std;
typedef long long ll;
//typedef __int128 lll;
const int N=100+10;
const int M=100000+10;
const int MOD=1e9+7;
const double PI = acos(-1.0);
const double EXP = 1E-8;
const int INF = 0x3f3f3f3f;
int t,n,m,k,p,l,r,u,v,h;
int ans,cnt,flag,temp,sum;
int a[N][N],b[N][N],c[N][N];
char str;
struct node{};
int main()
{
#ifdef DEBUG
freopen("input.in", "r", stdin);
//freopen("output.out", "w", stdout);
#endif
//ios::sync_with_stdio(false);
//cin.tie(0);
//cout.tie(0);
//scanf("%d",&t);
while(~scanf("%d%d%d",&n,&m,&h)){
ans=0;
for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)scanf("%d",&a[i][j]);
for(int i=1;i<=m;i++)for(int j=1;j<=h;j++)scanf("%d",&b[i][j]);
for(int i=1;i<=h;i++)for(int j=1;j<=n;j++)scanf("%d",&c[i][j]);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
for(int k=1;k<=h;k++)
if(a[i][j]&&b[j][k]&&c[k][i])
ans++;
cout<<ans<<endl;
}
#ifdef DEBUG
printf("Time cost : %lf s\n",(double)clock()/CLOCKS_PER_SEC);
#endif
//cout << "Hello world!" << endl;
return 0;
}
Problem I Enlarge it
http://acm.hdu.edu.cn/contests/contest_showproblem.php?pid=1009&cid=846
题意:输入一个字符矩阵,把它放大k倍输出。u题意:输入一个字符矩阵,把它放大k倍输出。
题解:模拟
/*
*@Author: STZG
*@Language: C++
*/
#include <bits/stdc++.h>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<string>
#include<vector>
#include<bitset>
#include<queue>
#include<deque>
#include<stack>
#include<cmath>
#include<list>
#include<map>
#include<set>
//#define DEBUG
#define RI register int
#define endl "\n"
using namespace std;
typedef long long ll;
//typedef __int128 lll;
const int N=100000+10;
const int M=100000+10;
const int MOD=1e9+7;
const double PI = acos(-1.0);
const double EXP = 1E-8;
const int INF = 0x3f3f3f3f;
int t,n,m,k,p,l,r,u,v;
int ans,cnt,flag,temp,sum;
char ditu[106][106];
int main()
{
#ifdef DEBUG
freopen("input.in", "r", stdin);
//freopen("output.out", "w", stdout);
#endif
//ios::sync_with_stdio(false);
//cin.tie(0);
//cout.tie(0);
//scanf("%d",&t);
//while(t--){
while(cin>>n>>m>>k){
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
cin>>ditu[i][j];
for(int i=0;i<n*k;i++){
for(int j=0;j<m;j++)
for(int t=0;t<k;t++)
cout<<ditu[i/k][j];
cout<<endl;
}
}
//}
#ifdef DEBUG
printf("Time cost : %lf s\n",(double)clock()/CLOCKS_PER_SEC);
#endif
//cout << "Hello world!" << endl;
return 0;
}
Problem J
题意:给你一个零件,它是由两个无限长的凸直棱柱A和B相交部分组成的,A的母线与B的母线垂直,求该零件的体积
Problem K
题意:有n个人,开m次派对,每次派对每对人会相互认识对方。对于每一次派对,我们要求出新认识的pair的数量。
题解:只允许O(n log(n)+mlog(n))通过
因为是算数量,所以可以把相互认识当作有向边,每个边当作从小连向大的有向边即可
对于每一个i,我们只用记录它在每次派对数量的数量认识的最大值f[i]即可。因为一旦i认识了f[i],那么说明它认识了i到f[i]的全部人。
用线段树来维护一段区间上f值的max和sum即可
我们可以在线段树上二分来用logn的效率来计算不需要再连边的人。