Problem A Chessboard
补题地址:http://acm.hdu.edu.cn/showproblem.php?pid=6532
题意:有n个点,其价值为i;分别对某一行、某一列以下的行、列有限制,求选择棋子的价值和最大
题解:费用流
离散化坐标,每行用一个点表示,每列也用一个点表示。表示第i-1行的点向表示第i行的点连边,容量为第i行及以后能拿的棋子数的上限,费用为0,同理表示相邻列的点两两连边。若第i行第j列上有棋子,则表示第i行的点向表示第j列的点连边,容量为1,费用为该棋子的价值。可以定义源点表示第0行,汇点表示第0列,源点到汇点的最大费用流即为答案。
C++版本一
题解:最小费用最大流
Dinic+SPFA+二分+离散化
1、离散化坐标,每行用一个点表示,每列也用一个点表示。
2、从0行(0列)递推到每一行(每一列)可以拿到的棋子,表示第i-1行的点向表示第i行的点连边,容量为第i行及以后能拿的棋子数的上限,费用为0,同理表示相邻列的点两两连边。
3、若第i行第j列上有棋子,则表示第i行的点向表示第j列的点连边,容量为1,费用为该棋子的价值。
4、可以定义源点表示第0行,汇点表示第0列,源点到汇点的最大费用流即为答案。
5、即假设有n行m列,拆分每个点,分为横坐标和纵坐标,从0行到n行,再从m列到0列。
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=1000+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 s,t,n,m,p,l,r,u,v;
ll k;
ll ans,cntX,cntY,flag,temp,sum,minz,maxflow;
ll dis[N];
bool vis[N];
int X[N],Y[N],x[N],y[N];
ll wc[N],wr[N];
char str[5];
struct Node{
int x,y;
ll w;
Node(){};
Node(int X,int Y,ll W):x(X),y(Y),w(W){}
}e[N];
struct node{
int u,v;
ll c,w;
node(){};
node(int form,int to,ll cap,ll w):u(form),v(to),c(cap),w(w){}
};
vector<node>edge;
vector<int> G[N];
void Addedge(int u,int v,ll cap,ll w){
edge.push_back({u,v,cap,w});
edge.push_back({v,u,0,-w});
//cout<<u<<" "<<v<<" "<<cap<<endl;
int sz=edge.size();
G[u].push_back(sz-2);
G[v].push_back(sz-1);
}
bool bfs(int u){
//memset(dis,-1,sizeof(dis));
for(int i=0;i<=t;i++)dis[i]=INF,vis[i]=0;
dis[u]=0;
queue<int>q;
q.push(u);
vis[u]=1;
while(!q.empty()){
int u=q.front();
q.pop();
vis[u]=0;
for(int i=0;i<G[u].size();i++){
node e=edge[G[u][i]];
//cout<<u<<" "<<e.v<<endl;
if(dis[e.v]>dis[u]+e.w&&e.c>0){
dis[e.v]=dis[u]+e.w;
if(!vis[e.v]){
vis[e.v]=1;
q.push(e.v);
}
}
}
}
return dis[t]!=INF;
}
int dfs(int u,ll flow){
vis[u]=1;
if(u==t)
return flow;
int now;
for(int i=0;i<G[u].size();i++){
node e=edge[G[u][i]];//
if((!vis[e.v]||e.v==t)&&e.c>0&&dis[u]+e.w==dis[e.v]&&(now=dfs(e.v,min(flow,e.c)))){
ans+=e.w*now;
//cout<<now<<endl;
edge[G[u][i]].c-=now;
edge[G[u][i]^1].c+=now;
return now;
}
}
return 0;
}
void dinic(){
while(bfs(s)){//cout<<ans<<endl;
int res=0;
//memset(vis,0,sizeof(vis));
while((res=dfs(s,INF))){
maxflow+=res;
memset(vis,0,sizeof(vis));
}
}
}
void init(){
for(int i=0;i<=t;i++)G[i].clear();
edge.clear();
ans=0;
maxflow=0;
}
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%d",&x[i],&y[i]);
e[i]=Node(x[i],y[i],i);
}
sort(x+1,x+n+1);
sort(y+1,y+n+1);
cntX=cntY=0;
X[++cntX]=x[1];
Y[++cntY]=y[1];
for(int i=2;i<=n;i++)if(x[i-1]!=x[i])X[++cntX]=x[i];
for(int i=2;i<=n;i++)if(y[i-1]!=y[i])Y[++cntY]=y[i];
s=0;
t=cntX+cntY+1;
//cout<<s<<" "<<t<<endl;
init();
scanf("%d",&m);
memset(wc,0x3f,sizeof(wc));
memset(wr,0x3f,sizeof(wr));
for(int i=1;i<=m;i++){
scanf("%s%d%lld",str,&p,&k);
if(str[0]=='R'){
int row=lower_bound(X+1,X+cntX+1,p)-X;
//if(X[row]!=p)row--;
wr[row]=min(k,wr[row]);
}else{
int col=lower_bound(Y+1,Y+cntY+1,p)-Y;
//if(Y[col]!=p)col--;
wc[col]=min(k,wc[col]);
}
}
for(int i=1;i<=cntX;i++){
wr[i]=min(wr[i],wr[i-1]);
Addedge(i-1,i,wr[i],0);
}
for(int i=1;i<=cntY;i++){
wc[i]=min(wc[i],wc[i-1]);
Addedge(t-i,t-i+1,wc[i],0);
}
for(int i=1;i<=n;i++){
int u=lower_bound(X+1,X+cntX+1,e[i].x)-X;
int v=lower_bound(Y+1,Y+cntY+1,e[i].y)-Y;
Addedge(u,t-v,1,e[i].w);
}
dinic();
cout<<ans<<endl;
}
#ifdef DEBUG
printf("Time cost : %lf s\n",(double)clock()/CLOCKS_PER_SEC);
#endif
//cout << "Hello world!" << endl;
return 0;
}
Problem B Build Tree
补题地址:http://acm.hdu.edu.cn/showproblem.php?pid=6533
题意:一颗m层的满n叉树,从k数组中不重复的选择一些数作为树的边,使得所有节点到根节点的距离之和最小
题解:对于一颗m层的满n叉树而言,某条边重复计算的次数是该边连接的子树的结点的数量,因为是满n叉树,所以这个结点数量是可以O(1)求出的,求min时,只要贪心选择小的权值为边赋值,O(E)复杂度内即可算出。而排序复杂度是O(NlogN),所以总复杂度是O(NlogN+E)。
C++版本一
/*
*@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=200000+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;
ll t,n,m,k,p,l,r,u,v;
ll ans,cnt,flag,temp,sum;
ll a[N];
char str;
struct node{};
ll POW(ll a,ll b,ll c){
ll res=1;
ll base=a%c;
while(b){
if(b&1)res=(res*base)%c;
base=(base*base)%c;
b>>=1;
}
return res;
}
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("%lld%lld%lld%lld",&k,&m,&n,&p)){
for(int i=1;i<=k;i++)scanf("%lld",&a[i]);
if(m==0||n==0){
cout<<0<<endl;
continue;
}
sort(a+1,a+k+1);
int pos=n;
int fac=m-1;
ans=0;
for(int i=1;i<=k;i++){
if(!fac)break;
ans=(ans+((a[i]%p)*(n==1?fac:(POW(n,fac,p)-1)/(n-1)))%p)%p;
//cout<<(ll)(POW(n,fac,p)-1)/(n-1)<<endl;
if(i==pos){
//cout<<pos<<endl;
fac--;
pos=pos+POW(n,m-fac,p);
}
}
cout<<ans<<endl;
}
//}
#ifdef DEBUG
printf("Time cost : %lf s\n",(double)clock()/CLOCKS_PER_SEC);
#endif
//cout << "Hello world!" << endl;
return 0;
}
Problem C Chika and Friendly Pairs
补题地址:http://acm.hdu.edu.cn/showproblem.php?pid=6534
题意:给你一个序列,多次询问,每次让你回答一个区间中差的绝对值不超过一个给定常数K的元素对数。
题解:对序列中的所有元素以及这些元素+K,-K后的值进行离散化。 然后使用莫队算法,在莫队算法的端点移动过程中,新加入一个元素x的过程中,将其插入树状数组,查询[x-K,x+K]范围的元素数即可。删除一个元素的过程与其类似。
C++版本一
题解:莫队+树状数组+离散化
/*
*@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[N],r[N],u,v;
int ans[N],cnt,flag,temp,tot;
int a[N],b[N],c[N],be[N];
char str;
struct node{
int l,r,id;
bool operator <(const node &S)const{
return (be[l] ^ be[S.l]) ? be[l] < be[S.l] : ((be[l] & 1) ? r < S.r : r > S.r);
}
}e[N];
int tree[N];
void add(int x,int C){
for(int i=x;i<=tot;i+=i&-i){
tree[i]+=C;
}
}
int sum(int x){
int res=0;
for(int i=x;i>0;i-=i&-i){
res+=tree[i];
}
return res;
}
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%d%d",&n,&m,&k);
int sz = sqrt(n);
int bnum = ceil((double)n / sz);
for (int i = 1; i <= bnum; i++){
for (int j = (i - 1) * sz + 1; j <= i * sz; j++)
be[j] = i;
}
for(int i=1;i<=n;i++)scanf("%d",&a[i]),b[i]=a[i];
sort(b+1,b+n+1);
c[1]=b[1];
tot=1;
for(int i=2;i<=n;i++)if(b[i-1]!=b[i])c[++tot]=b[i];
for(int i=1;i<=m;i++){
scanf("%d%d",&e[i].l,&e[i].r);
e[i].id=i;
}
sort(e+1,e+m+1);
for (int i = 1; i <= n; i++){
l[i] = lower_bound(c+1,c+tot+1, a[i] - k) - c ;
r[i] = upper_bound(c+1,c+tot+1, a[i] + k) - c - 1;
a[i] = lower_bound(c+1,c+tot+1, a[i]) - c ;
}
int L = 1, R = 0, temp = 0;
for (int i = 1; i <= m; i++){
int ql = e[i].l, qr = e[i].r;
while(L < ql){
add(a[L], -1);
temp -= sum(r[L]) - sum(l[L] - 1);
L++;
}
while(L > ql){
L--;
temp += sum(r[L]) - sum(l[L] - 1);
add(a[L], 1);
}
while(R < qr){
R++;
temp += sum(r[R]) - sum(l[R] - 1);
add(a[R], 1);
}
while(R > qr){
add(a[R], -1);
temp -= sum(r[R]) - sum(l[R] - 1);
R--;
}
ans[e[i].id] = temp;
//cout<<L<<" "<<R<<endl;
//cout<<temp<<endl;
}
for(int i=1;i<=m;i++){
cout<<ans[i]<<endl;
}
//}
#ifdef DEBUG
printf("Time cost : %lf s\n",(double)clock()/CLOCKS_PER_SEC);
#endif
//cout << "Hello world!" << endl;
return 0;
}
/**/
/*
5 5 3
2 5 7 1 3
6 6
1 3
2 4
1 5
2 3
*/
Problem D Chika and Solid Geometry
补题地址:http://acm.hdu.edu.cn/showproblem.php?pid=6535
题意:求底面均在z=0平面上,顶点的z坐标大于零的斜圆锥和棱锥的体积并。
题解:
将每个z轴方向的横截面看做多边形和圆的面积并,然后对z轴做辛普森积分即可。
多边形和圆的面积并:面积和-面积交。
多边形和圆的面积交:将圆心移动到原点,以原点为基准对多边形进行三角剖分,转化成计算圆心在原点的圆和有一个顶点在原点的三角形的面积交问题。这个进行大力分类讨论即可,细节比较多。
C++版本一
Problem E Hello XTCPC
补题地址:http://acm.hdu.edu.cn/showproblem.php?pid=6536
题意:不重复元素,求子串为xtCpc的数量最大
题解:这是一道很简单的贪心题目,因为xtCpc这5个字母各不相同,所以直接采取贪心的策略,对于所有的“x”,“t”,“C”,”p”,”c”按照字母不同分别建立一个队列,每次贪心的从队列中找到最前面的没有使用过的字母,直到有一个队列为空。
C++版本一
/*
*@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=200000+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;
string 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)){
ans=0;
cin>>str;
queue<int>q[5];
for(int i=0;i<n;i++){
if(str[i]=='x'){
q[0].push(i);
}else if(str[i]=='t'){
q[1].push(i);
}else if(str[i]=='C'){
q[2].push(i);
}else if(str[i]=='p'){
q[3].push(i);
}else if(str[i]=='c'){
q[4].push(i);
}
}
while(!q[0].empty()){
int pos=q[0].front();
q[0].pop();
flag=1;
for(int i=1;i<5;i++){
while(!q[i].empty()&&pos>q[i].front())q[i].pop();
if(q[i].empty()){
flag=0;
break;
}
pos=q[i].front();
q[i].pop();
}
if(!flag)break;
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 F Neko and function
补题地址:http://acm.hdu.edu.cn/showproblem.php?pid=6537
题意:
题解:
设g(n,k)为k个>=1的数的乘积为n的方案数
很容易推出容斥f(n,k)= sum (-1)^i * g(n,k-i) * C(k,i)
而g(n,k)=prod C(ai+k-1,k-1) 其中n=prod pi^ai pi是n的第i个质因子
可以发现g(n,k)是积性函数,套min-25筛即可
C++版本一
Problem G Neko and quadrilateral
补题地址:http://acm.hdu.edu.cn/showproblem.php?pid=6538
题意:
题解:
求面积最大和最小四边形,通过对角线变换为求面积最大和最小三角形
求出任意两点组成的向量,然后排序
枚举向量,基于此进行贪心,维护向量左侧的点到向量的距离单调,右侧的点到向量的距离单调
然后选择最远和最近点更新答案
复杂度n^2logn
C++版本一
Problem H Neko and sequence
补题地址:http://acm.hdu.edu.cn/showproblem.php?pid=6539
题意:
题解:
考虑如果两种括号都存在,那么移动一定步数后,都会在()上来回移动,可以预处理出每个位置移动多少步后会进入这个循环,或者永远不进入循环
在进入循环之前一定都是朝一个方向一直移动
那么在进入循环之前,起点为i的位置走j步到达的点就是(i+kj)%n
化简为(i+kj%n)%n,设d=kj%n,把区间询问以n-d为间隔分成两部分处理即可,可以用树状数组维护出来
进入循环后的部分也可以按j的奇偶用树状数组轻松维护出来
总体复杂度nlogn
C++版本一
Problem I Neko and tree
补题地址:http://acm.hdu.edu.cn/showproblem.php?pid=6540
题意:
题解:
树形dp
dp[i][j]表示以i为根的子树中到i最远的关键点距离为j的方案数
当u向fa [u]更新时,进行更新即可
dp[u][i]∗dp[fa[u]][j]∗[i+j≤k]→new_dp[fa[u]][max(i+1,j)]
可以用前缀和分两段优化
求答案时考虑反过来求,用总方案数减去不合法方案数
不合法方案数在可在更新的时候用类似的方法同时求出
C++版本一
Problem J Neko and triangle
补题地址:http://acm.hdu.edu.cn/showproblem.php?pid=6541
题意:
题解:
本质上是给你x,y,找到一个区间[l,r]使得
sum_x(l,r)*y-sum_y(l,r)*x最大
如果把所有sum_x(l,r)和sum_y(l,r)映射成二维平面上的点集的话,我们可以发现答案一定在点集的凸壳上
如果求出这个凸壳,就可以二分答案了
这个凸壳可以用分治+闵可夫斯基和来求
C++版本一
Problem K SSY and JLBD
补题地址:http://acm.hdu.edu.cn/showproblem.php?pid=6542
题意:判断是否满足“shisanyao”或者“jiulianbaodeng”的要求
题解:只要根据题意进行简单的模拟即可,唯一略有麻烦的地方应该就是麻将牌的读入了。
C++版本一
/*
*@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;
int a[4][10];
int b[2][14]={{0,0,1,1,2,2,3,3,3,3,3,3,3},
{1,9,1,9,1,9,1,2,3,4,5,6,7}};
int c[10]={0,3,1,1,1,1,1,1,1,3};
string str;
string word[8]={"","dong","nan","xi","bei","zhong","fa","bai"};
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);
for(int i=1;i<=14;i++){
cin>>str;
if(isdigit(str[0])){
if(str[1]=='s'){
a[0][str[0]-'0']++;a[0][0]++;
}else if(str[1]=='p'){
a[1][str[0]-'0']++;a[1][0]++;
}else if(str[1]=='w'){
a[2][str[0]-'0']++;a[2][0]++;
}
}else{
for(int j=1;j<=7;j++){
if(str==word[j]){
a[3][j]++;a[3][0]++;
break;
}
}
}
}
flag = 1;
for(int i=0;i<13;i++)if(!a[b[0][i]][b[1][i]]){flag=0;break;}
if(flag){cout<<"shisanyao!"<<endl;return 0;}
for(int i=0;i<3;i++){
if(a[i][0]==14){
flag=1;
for(int j=1;j<=9;j++){
if(a[i][j]<c[j]){
flag=0;
}
}
if(flag){cout<<"jiulianbaodeng!"<<endl;return 0;}
}
}
cout<<"I dont know!"<<endl;
//}
#ifdef DEBUG
printf("Time cost : %lf s\n",(double)clock()/CLOCKS_PER_SEC);
#endif
//cout << "Hello world!" << endl;
return 0;
}
Problem L Can you raed it croretcly?
补题地址:http://acm.hdu.edu.cn/showproblem.php?pid=6543
题意:如果单词的首字母和最后一个字母是正确的,只是交换其他字母,人类可以自动纠正单词的拼写。当两个字符串相同时输出“Equal”; 输出“是”时可以更正,否则输出“否”。
题解:输入两个字符串,如果两者一样则输出Equal,不一样则判断字符串首尾字母是否相同,中间字母对应个数是否相同,直接输出结果。
C++版本一
/*
*@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=300+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;
int a[N];
string str1,str2;
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(cin>>str1){
cin>>str2;
memset(a,0,sizeof(a));
if(str1==str2){
cout<<"Equal"<<endl;
}else{
flag=1;
for(int i=0;i<str1.size();i++)
a[str1[i]-97]++;
for(int i=0;i<str2.size();i++)
a[str2[i]-97]--;
for(int i=0;i<26;i++)
if(a[i])flag=0;
cout<<(flag&&str1[0]==str2[0]&&str1[str1.size()-1]==str2[str2.size()-1]?"Yes":"No")<<endl;
}
//scanf("%d",&n);
}
#ifdef DEBUG
printf("Time cost : %lf s\n",(double)clock()/CLOCKS_PER_SEC);
#endif
//cout << "Hello world!" << endl;
return 0;
}