题目链接:http://acm.hdu.edu.cn/search.php?field=problem&key=2019+Multi-University+Training+Contest+5&source=1&searchmode=source
three arrays(字典树+贪心)
HDU - 6625
参考:https://blog.youkuaiyun.com/mmk27_word/article/details/98611040
#include<bits/stdc++.h>
using namespace std;
const int maxn=100010*30;
const int N=100010;
int tri[2][maxn][2];
int num[2][maxn],pos[2];
void insert1(int cur,int op)
{
int c=0,x;
for(int i=29;i>=0;i--){
if((1<<i)&cur) x=1;
else x=0;
if(tri[op][c][x]==0){
tri[op][c][x]=++pos[op];
// tri[op][c+1][0]=tri[op][c+1][1]=num[op][c+1]=0;
tri[op][pos[op]][0]=tri[op][pos[op]][1]=num[op][pos[op]]=0;
}
c=tri[op][c][x];
num[op][c]++;
}
}
int query()
{
int c0=0,c1=0;
int ans=0;
for(int i=29;i>=0;i--){
if(num[0][tri[0][c0][0]]&&num[1][tri[1][c1][0]]){
c0=tri[0][c0][0];c1=tri[1][c1][0];
num[0][c0]--;num[1][c1]--;
}else if(num[0][tri[0][c0][1]]&&num[1][tri[1][c1][1]]){
c0=tri[0][c0][1];c1=tri[1][c1][1];
num[0][c0]--;num[1][c1]--;
}else if(num[0][tri[0][c0][0]]&&num[1][tri[1][c1][1]]){
c0=tri[0][c0][0];c1=tri[1][c1][1];
num[0][c0]--;num[1][c1]--;
ans+=1<<i;
}else{
c0=tri[0][c0][1];c1=tri[1][c1][0];
num[0][c0]--;num[1][c1]--;
ans+=1<<i;
}
}
return ans;
}
int c[N],n;
int main()
{
int t;scanf("%d",&t);
while(t--){
scanf("%d",&n);
int x;
pos[0]=pos[1]=0;
tri[0][0][0]=tri[0][0][1]=num[0][0]=0;
tri[1][0][0]=tri[1][0][1]=num[1][0]=0;
for(int i=1;i<=n;i++){
scanf("%d",&x);insert1(x,0);
}
for(int i=1;i<=n;i++){
scanf("%d",&x);insert1(x,1);
}
for(int i=1;i<=n;i++){
c[i]=query();
}
sort(c+1,c+n+1);
for(int i=1;i<=n;i++){
printf("%d%c",c[i],i==n?'\n':' ');
}
}
return 0;
}
equation(分段函数/枚举)
HDU - 6627
题解&代码:https://blog.youkuaiyun.com/songziqi98/article/details/98736140
#include <bits/stdc++.h>
using namespace std;
const int SIZE = 1e5+5;
const int INF = 0x3f3f3f3f;
struct Node {
int a, b;
int id;
double aDb;
Node() : a(0), b(0) {}
} arr[SIZE];
int xNum, num;
bool cmp(const Node& a, const Node& b) {
if(a.aDb != b.aDb) return a.aDb < b.aDb;
else return a.id < b.id;
}
struct A {
int a, b;
int gcd(int a, int b) { return b == 0 ? a : gcd(b, a%b); }
void get() {
int num = gcd(a, b);
a /= num;
b /= num;
}
inline bool operator < (const A &tt) const {
double t1 = (double)a/b;
double t2 = (double)tt.a/tt.b;
return t1 < t2;
}
};
set<A> ans;
double l, r;
int main() {
// freopen("RAW/in", "r", stdin);
// freopen("RAW/out", "w", stdout);
int T;
scanf("%d", &T);
while(T--) {
ans.clear();
l = r = -INF;//当前区间
int n, c;
xNum = num = 0;
scanf("%d%d", &n, &c);
for(int i = 0; i < n; i++) {
scanf("%d%d", &arr[i].a, &arr[i].b);
arr[i].id = i;
xNum += (-arr[i].a);
num += (-arr[i].b);
arr[i].aDb = -((double)arr[i].b/arr[i].a);//求出分界点
}
sort(arr, arr + n, cmp);
bool flag = false;
for(int i = 0; i <= n; i++) {
if(i < n) r = arr[i].aDb;
else r = INF;
int a = c-num;
if(xNum == 0 && a == 0) {//无穷解
flag = true;
break;
}
A temp;
if(a == 0) {//零解
temp.a = 0;
temp.b = 1;
if(0 >= l && 0 <= r) ans.insert(temp);
} else if(xNum != 0){//一般解
double tt = (double)a/xNum;
if(tt >= l && tt <= r) {
temp.a = a;
temp.b = xNum;
temp.get();
ans.insert(temp);
}
}//更新 num xNum
num += 2*arr[i].b;
xNum += 2*arr[i].a;
l = arr[i].aDb;
}
if(flag) printf("-1");
else {
int len = ans.size();
printf("%d", len);
for(set<A>::iterator it = ans.begin(); it != ans.end(); it++) {
if(1.0*(*it).a/(*it).b < 0) printf(" -");
else printf(" ");
printf("%d/%d", abs((*it).a), abs((*it).b));
}
}
printf("\n");
}
return 0;
}
permutation 1(思维/排列/搜索)
HDU - 6628
代码:https://blog.youkuaiyun.com/tianwei0822/article/details/98613084
题意:给定n和k,对于n的所有排列,求出字典序第k小的作差序列对应的排列。
对于1 2 4 3作差序列就是1 2 -1
题解:由于n只有20,k只有1e4,那么因为7!<1e4,8!>1e4,所以对于n-2>=8的情况,我们只需考虑固定n,1开头,剩余数做字典序第k小就是原题所求。对于n-2<8的情况,我们暴力处理下即可。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+5;
int n,m,t;
struct p{
int num[11];
char str[11];
}a[11][N];
bool cmp(p aa, p bb){
return strcmp(aa.str,bb.str)<0;
}
int main(){
int b[15]={0,1,2};
for(int k=2;k<=9;k++){
int cnt=1;
do{
for(int i=1;i<=k;i++){
a[k][cnt].num[i]=b[i];
if(i!=1)a[k][cnt].str[i-2]=b[i]-b[i-1]+'A';
}
a[k][cnt].str[k-1]='\0';
cnt++;
}while(next_permutation(b+1,b+k+1));
sort(a[k]+1,a[k]+cnt,cmp);
b[k+1]=k+1;
}
scanf("%d",&t);
while(t--){
int k;
scanf("%d%d",&n,&k);
if(n>9){
int c[30];
c[1]=n;
for(int i=2;i<=n;i++)c[i]=i-1;
for(int i=1;i<k;i++)
next_permutation(c+1,c+n+1);
printf("%d",c[1]);
for(int i=2;i<=n;i++)
printf(" %d",c[i]);
printf("\n");
continue;
}
printf("%d",a[n][k].num[1]);
for(int i=2;i<=n;i++)
printf(" %d",a[n][k].num[i]);
printf("\n");
}
return 0;
}
直接dfs也行:https://blog.youkuaiyun.com/qq_40655981/article/details/98591885
思路:直接dfs枚举差异序列从小往大搜,然后对不合法的情况去掉(差异序列中元素的最大-最小<n-1才合法,重复的也不合法)
代码
#include<bits/stdc++.h>
#define LL long long
#define fi first
#define se second
#define mp make_pair
#define pb push_back
using namespace std;
LL gcd(LL a,LL b){return b?gcd(b,a%b):a;}
LL lcm(LL a,LL b){return a/gcd(a,b)*b;}
LL powmod(LL a,LL b,LL MOD){LL ans=1;while(b){if(b%2)ans=ans*a%MOD;a=a*a%MOD;b/=2;}return ans;}
const int N = 503;
int t,n,k;
int vis[N],p[N];
bool dfs(int now,int pre,int l,int r){
if(now==n){
if(k==1){
for(int i=0;i<n;i++){
cout<<p[i]-l+1;
if(i<n-1)cout<<' ';
else cout<<'\n';
}
return 1;
}
k--;
return 0;
}
for(int i=1-n;i<=n-1;i++){//枚举差异序列字典序最小
if(!vis[i+pre]){
vis[i+pre]=1;
if(max(i+pre,r)-min(l,i+pre)<=n-1){
p[now]=i+pre;
if(dfs(now+1,i+pre,min(i+pre,l),max(i+pre,r))){
vis[i+pre]=0;
return 1;
}
}
vis[i+pre]=0;
}
}
return 0;
}
int main(){
ios::sync_with_stdio(false);
for(cin>>t;t;t--){
cin>>n>>k;int sta=0;
vis[n]=1;
p[0]=n;
dfs(1,n,n,n);//相对大小
vis[n]=0;
}
return 0;
}
string matching(扩展KMP)
HDU 6629
基本题意就大概是求s的所有后缀子串和s的最长匹配前缀串长度之和。
#include<bits/stdc++.h>
using namespace std;
int const maxn=1000100;
//扩展kmp:求t每一个后缀字符串匹配s最长前缀长度
/*
extend[i]:S[i]...S[n-1]与 T 的最长相同前缀的长度
next[i]: T[i]...T[m - 1]与 T 的最长相同前缀长度;
*/
int lent,nex[maxn];
char t[maxn];
long long ans;
void getnext(){
lent=strlen(t);
int a=0,p=0;//p是最长的匹配到的位置,a是它开始匹的位置
nex[0]=lent;
for(int i=1;i<lent;i++){
if(i>=p||i+nex[i-a]>=p){
if(i>=p)p=i;
while(p<lent&&t[p]==t[p-i])p++;
nex[i]=p-i;a=i;
}
else nex[i]=nex[i-a];
if(i+nex[i]==lent)ans+=nex[i];
else ans+=nex[i]+1;
}
}
int main(){
int Y;
scanf("%d",&Y);
while(Y--){
scanf("%s",t);
ans=0;
getnext();
cout<<ans<<endl;
}
return 0;
}
permutation 2(思维/递推)
HDU - 6630
题意:给定义一个x,y求以x为第一元素,y为最后一个元素,且任意相邻元素差值绝对值不大于2的排列个数
题解:转化理解,有n个点,起点为x,终点为y,每一步只能跳1格和2格,每个点都要访问且访问一次,求所有走法。这里我们假设x<y,x>y的情况是类似的,由于要走完所有的点,对于x左边的点,我们要确保全部走完,且能返回到x右边来,那么走法唯一,即从x,x-2,x-4到1/2,再返回走,最后回到x+1;对于y右边的点,由于y要最后访问,需要把y右边的点,全部访问后再回到y,其起点是y-1;那么问题就变成了求[x+1,y-1]的走法了,递推即可。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=100010;
const int mod=998244353;
ll a[maxn];
int n;
void init()
{
a[1]=a[2]=a[3]=1;
for(int i=4;i<maxn;i++){
a[i]=(a[i-1]+a[i-3])%mod;
}
}
int main()
{
init();
int t;scanf("%d",&t);
int x,y;
while(t--){
scanf("%d%d%d",&n,&x,&y);
if(x>y) swap(x,y);
if(y==x+1){
if(x==1||y==n) printf("1\n");
else printf("0\n");
continue;//
}
if(x!=1) x++;
if(y!=n) y--;
int cur=y-x+1;
printf("%lld\n",a[cur]);
}
return 0;
}