A - simple counting problem HDU - 6056
待补
B - Kanade’s convolution HDU - 6057
待补
C - Kanade’s sum HDU - 6058 (模拟队列/链表)
题意:给n个数是(1-n),然后问所有序列中第k大的数之和是多少。
当时和队友先找规律找半天,然后觉得不对。之后就怎么想都超时。
赛后看到有人模拟做的,自己先模拟了一下,还是超时,直接模拟太暴力了。从第一个数开始考虑,当这个数是第k大的时候,先找左边它大的,找到k-1个,然后往左边找。注意一下边界。。
我的是左边来一下,右边来一下,肯定超时啦。。
第二种方法是用链表,从小到大扫,然后扫完之后删除节点就行了。直接记录位置,扫的当前点,与它相连的都是比它大的,不用扫比它小的了,因为比它小的已经删除了。
//直接模拟(TIME:1934ms)差一点就超时了//用c++交会tle ,用G++交
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdio>
using namespace std;
#define LL long long
const int maxn = 1000020;
LL a[maxn];
int b[maxn];
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
int n,k;
scanf("%d %d",&n,&k);
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]);
LL ans=0;
for(int i=1;i<=n;i++)
{
int h=0;
b[++h]=i;
int j;
for(j=i+1;j<=n&&h<k;j++)
{
if(a[j]>a[i]) b[++h]=j;
}
if(h>=k)
{
LL temp=1,t=0;
for(;j<=n;j++)
{
if(a[j]>a[i]) break;
else temp++;
}
t+=temp;
for(j=i-1;j>=1;j--)
{
if(a[j]>a[i]&&h<=1) break;
else if(a[j]>a[i])
{
temp=b[h]-b[h-1];
h--;
t+=temp;
}
else t+=temp;
}
ans+=t*a[i];
}
else {
int g=h;
for(j=i-1;j>=1;j--)
{
if(a[j]>a[i]) b[++g]=j;
if(g==k) break;
}
if(g<k) continue;
int temp=n-b[h]+1,t=temp;
for(j=j-1;j>=1;j--)//这里为j-1//因为这个WA了好多次
{
if(a[j]>a[i])
{
if(h<=1) break;
temp=b[h]-b[h-1];
h--;
t+=temp;
}
else t+=temp;
}
ans+=t*a[i];
}
}
printf("%lld\n",ans);
}
return 0;
}
//链表//(TIME:546ms)
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdio>
using namespace std;
#define LL long long
const int maxn = 5e5+10;
int pre[maxn],nxt[maxn],pos[maxn];
int r[maxn],l[maxn];
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
int n,k;
scanf("%d %d",&n,&k);
for(int i=1;i<=n;i++)
{
int x;
scanf("%d",&x);
pos[x]=i;
pre[pos[x]]=i-1;
nxt[pos[x]]=i+1;
}
LL ans=0;
for(int i=1;i<=n;i++)
{
int now=pos[i],right=0,left=0;
for(int j=now;j<=n&&right<k;j=nxt[j])
{
r[right++]=nxt[j]-j;
}
LL num=0;
for(int j=now;j>=1&&left<k;j=pre[j])
{
if(left+right>=k){
l[left]=j-pre[j];
num+=l[left]*r[k-left-1];
left++;
}
else
{
left++;
continue;
}
}
ans+=(LL)num*i;
nxt[pre[pos[i]]]=nxt[pos[i]];
pre[nxt[pos[i]]]=pre[pos[i]];
}
printf("%lld\n",ans);
}
return 0;
}
D - Kanade’s trio HDU - 6059 (01字典树)
题意:给你一个数组A[n]下标从1开始到n,问有多少个三元组(i,j,k),i < j < k,使得(A[i]^A[j])<(A[j]^A[k])
第一反应是枚举j,然后j的前面都放在一个字典树Trie1中,j的后面后放在一个字典树Trie2中。然后发现没有办法去统计个数。了别人题解之后知道了~~ 满足条件的三元组,如果A[i],A[k]的前10位都相同,第11位不同了
第11位,A[i]=0,A[k]=1,那么A[j]=0,之后的值都可以任意取了。
A[i]=1,A[k]=0,那么A[j]=1,之后的值也可以任意取了。
所以我们用找A[i],A[k]第一个不同的位置,然后统计有多少那个j就行了。
dalao的思路:先从小到大枚举k,把A[k]一个一个插入字典树中,然后相应位统计有多少个j。这样就保证了j < k
然后从小到大枚举i,将以i作为k的对应位数有多少个j减去,这样就保证了i < k.(相当于k的范围在(i,k] ),现在j的范围是[1,k),对于当前i,相应位的j我们要减去i出现的次数就可以保证i
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
#define LL long long
const int maxn = 500005;
int a[maxn];
int node[maxn*10][2];
LL val[maxn*10][2];
LL num[32][2];
LL coun[maxn*10][2];
int tot=1;
void sert(int x,int d)
{
int u=0;
for(int i=30;i>=0;i--)
{
int g=(x>>i)&1;
if(!node[u][g])
{
node[u][g]=tot++;
}
coun[u][g]+=d*num[i][1-g];//如果这里是num[u][1-g]的话,会TLE,因为这样的话num的空间就比较大了,memset就比较耗时.
val[u][g]+=d;
num[i][g]++;
u=node[u][g];
}
}
LL lookf(int x)
{
LL sum=0;
int u=0;
for(int i=30;i>=0;i--)
{
int g1=(x>>i)&1;
if(node[u][1-g1])
sum+=coun[u][1-g1]-(LL)val[u][1-g1]*num[i][g1];//这里要防止爆int
u=node[u][g1];
}
return sum;
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
tot=1;
memset(val,0,sizeof(val));
memset(node,0,sizeof(node));
memset(num,0,sizeof(num));
memset(coun,0,sizeof(coun));
memset(a,0,sizeof(a));
int n;
scanf("%d",&n);
for(int i=0;i<n;i++){
scanf("%d",&a[i]);
sert(a[i],1);
}
LL ans=0;
memset(num,0,sizeof(num));
for(int i=0;i<n;i++)
{
sert(a[i],-1);
ans+=lookf(a[i]);
}
printf("%I64d\n",ans);
}
return 0;
}
思考:num[i][g]表示第i位上为g的个数,那么我计算j的时候应该会有重复的值。之后好像又减去了。。这一点还没怎么明白== (望路过的dalao们指点指点
感觉还是用指针写比较清楚点
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
#define LL long long
const int maxn = 500005;
int a[maxn];
struct Node
{
Node *son[2];
int coun;
int val;
node(){
coun=0,val=0;son[0]=son[1]=NULL;
}
};
Node *root;
int node[maxn*10][2];
LL val[maxn*10][2];
LL num[32][2];
LL coun[maxn*10][2];
int tot=1;
void sert(int x,int d)
{
Node *u=root;
for(int i=30;i>=0;i--)
{
int g=(x>>i)&1;
if(u->son[g]==NULL)
{
u->son[g]=new Node();
}
u=u->son[g];
u->coun+=d*num[i][1-g];
u->val+=d;
num[i][g]++;
}
}
LL lookf(int x)
{
LL sum=0;
Node *u=root;
for(int i=30;i>=0;i--)
{
int g1=(x>>i)&1;
Node *p=u->son[1-g1];
//if(!val[u][g1]) break;
if(p)
sum+=p->coun-(LL)p->val*num[i][g1];
u=u->son[g1];
if(u==NULL) break;
}
return sum;
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
root=new Node();
int n;
memset(num,0,sizeof(num));
scanf("%d",&n);
for(int i=0;i<n;i++){
scanf("%d",&a[i]);
sert(a[i],1);
}
LL ans=0;
memset(num,0,sizeof(num));
for(int i=0;i<n-2;i++)
{
sert(a[i],-1);
ans+=lookf(a[i]);
}
printf("%lld\n",ans);
}
return 0;
}
E - RXD and dividing (贪心+dfs)
题意:给你一棵树,将除1之外的点分成k份,每份使之连通(会加一些边),边权之和最大为多少。
很巧妙。这个就是要算每条边的最大贡献。假 设x是根,有y个孩子(所有孩子,孩子的孩子也算),这个时候上面的那条边v的贡献为v*min(y+1,k)因为自己也要算
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <vector>
using namespace std;
#define LL long long
const int maxn = 1e6+10;
struct node
{
int to;
LL v;
node(int a=0,LL b=0){
to=a,v=b;
}
};
vector<node> vec[maxn];
int vis[maxn];
void addedge(int from,int to,LL v)
{
vec[from].push_back(node(to,v));
vec[to].push_back(node(from,v));
}
LL ans=0;
int n,k;
int dfs(int pos,LL zz)
{
int hz=0;
for(int i=0;i<vec[pos].size();i++)
{
node e=vec[pos][i];
if(!vis[e.to])
{
hz++;
vis[e.to]=1;
hz=hz+dfs(e.to,e.v);
}
if(i==vec[pos].size()-1){
int b=hz+1;
LL g=min(k,b);
ans+=g*zz;
}
}
return hz;
}
void init()
{
memset(vis,0,sizeof(vis));
for(int i=0;i<maxn;i++)
vec[i].clear();
}
int main()
{
while(scanf("%d %d",&n,&k)!=EOF)
{
init();
for(int i=1;i<n;i++)
{
int from,to;LL v;
scanf("%d %d %I64d",&from,&to,&v);
addedge(from,to,v);
}
ans=0;
vis[1]=1;
dfs(1,0);
printf("%I64d\n",ans);
}
return 0;
}
F - RXD and functions HDU - 6061 (快速数论变换(NTT))
待补
G - RXD and logic gates HDU - 6062
待补
H - RXD and math HDU - 6063 (思维+快速幂)
开始还以为和莫比乌斯反演有关,然后开始推,推半天。然后想打个表看看,结果发现了规律,答案就是n^k%mod,快速幂搞一下就行了。