B组:
命运
50分:最小生成树,每两个点连个边
60分(考试分数):当只有1维时,只需对所有点坐标从小到大排序,选相邻的两个点连边即可
100分:发现若答案存在某条边
U−>V
则U和V按某一维排序一定是挨着的,则每次对于这五维排序,对挨着的两个点连边做最小生成树即可
#include<algorithm>
#include<ctype.h>
#include<cstdio>
#define N 100020
using namespace std;
inline int read(){
int x=0,f=1;char c;
do {c=getchar();if(c=='-') f=-1;} while(!isdigit(c));
do x=(x<<3)+(x<<1)+c-'0',c=getchar(); while(isdigit(c));
return x*f;
}
int n,k,top;
long long ans;
int f[N];
struct Point{
int pos[7],bh;
}a[N];
struct Edge{
int x,y,k;
Edge(int _=0,int __=0,int ___=0):x(_),y(__),k(___){}
}q[1000050];
inline void add(int x,int y,int k){
q[++top]=Edge(x,y,k);
}
inline int abs(int a){return a>0?a:-a;}
int find(int k){return f[k]==k?f[k]:f[k]=find(f[k]);}
inline void link(int x,int y){
int fx=find(x),fy=find(y);
f[fx]=fy;
}
inline bool cmp(Edge a,Edge b){return a.k<b.k;}
inline bool cmp1(Point a,Point b){return a.pos[1]<b.pos[1];}
inline bool cmp2(Point a,Point b){return a.pos[2]<b.pos[2];}
inline bool cmp3(Point a,Point b){return a.pos[3]<b.pos[3];}
inline bool cmp4(Point a,Point b){return a.pos[4]<b.pos[4];}
inline bool cmp5(Point a,Point b){return a.pos[5]<b.pos[5];}
inline void solve(){
for(int i=1;i<=k;i++){
if(i==1)sort(a+1,a+n+1,cmp1);
if(i==2)sort(a+1,a+n+1,cmp2);
if(i==3)sort(a+1,a+n+1,cmp3);
if(i==4)sort(a+1,a+n+1,cmp4);
if(i==5)sort(a+1,a+n+1,cmp5);//按照5维排序
for(int j=2;j<=n;j++){//连边,注意编号可能被打乱,要记录一下
add(a[j-1].bh,a[j].bh,abs(a[j].pos[i]-a[j-1].pos[i]));
}
}
sort(q+1,q+top+1,cmp);
for(int i=1;i<=n;i++) f[i]=i;//最小生成树
for(int i=1,cnt=0;i<=top&&cnt<=n-1;i++){
int fx=find(q[i].x),fy=find(q[i].y);
if(fx==fy) continue;
link(fx,fy);
ans+=q[i].k;
cnt++;
}
printf("%lld",ans);
}
int main(){
n=read();k=read();
for(int i=1;i<=n;i++)
for(int j=1;j<=k;j++)
a[i].pos[j]=read(),a[i].bh=i;
solve();
return 0;
}
幻想
spfa, disi,j 表示到点i用了j次更改权值的机会,转移很好想吧…
#include<algorithm>
#include<ctype.h>
#include<cstdio>
#include<queue>
#define N 10020
#define M 2000200
#define int long long
#define INF 214748364700000000ll
using namespace std;
inline int read(){
int x=0,f=1;char c;
do {c=getchar();if(c=='-') f=-1;} while(!isdigit(c));
do x=(x<<3)+(x<<1)+c-'0',c=getchar(); while(isdigit(c));
return x*f;
}
queue<int>q;
int n,m,k,x,y,z,s,t,top;
int Dis[N],fir[N];
struct Edge{
int nex,to,k;
Edge(int _=0,int __=0,int ___=0):to(_),nex(__),k(___){}
}nex[M];
inline void add(int x,int y,int k){
nex[++top]=Edge(y,fir[x],k);
fir[x]=top;
}
bool b[N];
inline bool spfa1(){
while(!q.empty()) q.pop();
for(int i=1;i<=n;i++) Dis[i]=INF,b[i]=false;
Dis[s]=0;q.push(s);b[s]=true;
while(!q.empty()){
int x=q.front();q.pop();b[x]=false;
for(int i=fir[x];i;i=nex[i].nex)
if(Dis[nex[i].to]>Dis[x]+nex[i].k){
Dis[nex[i].to]=Dis[x]+nex[i].k;
if(!b[nex[i].to]){
b[nex[i].to]=true;
q.push(nex[i].to);
}
}
}
return Dis[t]==INF;
}
int dis[N][55],ans;
bool d[N][55];
inline void spfa(){
while(!q.empty()) q.pop();
for(int i=1;i<=n;i++){
for(int j=0;j<=k;j++)
dis[i][j]=INF;
dis[i][0]=Dis[i];
b[i]=false;
}
for(int j=1;j<=k;j++){//从小到大枚举j
dis[s][j]=0;q.push(s);b[s]=true;
while(!q.empty()){
int x=q.front();q.pop();b[x]=false;
for(int i=fir[x];i;i=nex[i].nex){
if(dis[nex[i].to][j]>dis[x][j]+nex[i].k){
dis[nex[i].to][j]=dis[x][j]+nex[i].k;
if(!b[nex[i].to]){
b[nex[i].to]=true;
q.push(nex[i].to);
}
}
if(dis[nex[i].to][j]>dis[x][j-1]+nex[i].k/2){
dis[nex[i].to][j]=dis[x][j-1]+nex[i].k/2;
if(!b[nex[i].to]){
b[nex[i].to]=true;
q.push(nex[i].to);
}
}
}
}
}
}
inline void solve(){
spfa();
ans=INF;
for(int i=0;i<=k;i++)
ans=min(ans,dis[t][i]);
printf("%lld",ans);
}
main(){
n=read();m=read();k=read();
s=read();t=read();
for(int i=1;i<=m;i++){
x=read();y=read();z=read();
add(x,y,z);add(y,x,z);
}
if(spfa1()){
printf("baka");
return 0;
}//判断是否联通
if(k==0){
printf("%lld",Dis[t]);
return 0;
}//水分..
solve();
return 0;
}
读书
Meet-in-Middle…
考虑m为0的情况,则只要让体力消耗小于k即可
在MiM后对左右两段消耗体力值从小到大排序,定义两个指针i,j分别指向s1的开头和s2的结尾
固定i,从右到左枚举j直到合法,此时的j即为可统计的答案,下次查找只需将i右移一位,接着左移j即可
如果m不为0,怎么办?
采用类似的思想,不同的是按照能造成的伤害排序,伤害是否合法按照刚才那样处理就可以,对于统计体力在给定范围内的,开个权值树状数组统计一下就好(见代码)
#include<algorithm>
#include<ctype.h>
#include<cstdio>
#define N 27
using namespace std;
inline int read(){
int x=0,f=1; char c;
do {c=getchar();if(c=='-') f=-1;} while(!isdigit(c));
do x=(x<<3)+(x<<1)+c-'0',c=getchar(); while(isdigit(c));
return x*f;
}
int n,m,k;
long long ans=0;
int top1,top2;
int q[1000020];
inline void add(int x,int k){
for(;x<=1000000;x=x+(x&-x))
q[x]+=k;
}
inline int Sum(int x){
int sum=0;
for(;x;x=x-(x&-x))
sum=sum+q[x];
return sum;
}
struct Round{
int p,def,atk,a,b,c;
}a[N];
struct Data{
int res,hp;
Data(int _=0,int __=0):res(_),hp(__){}
}s1[600500],s2[600500];//res:消耗多少体力 hp:造成多少伤害
void dfs(int x,int res,int hp){
if(x==(n>>1)+1){
s1[++top1]=Data(res,hp);
return;
}
dfs(x+1,res+a[x].p,hp+a[x].a);
dfs(x+1,res+a[x].p-a[x].def,hp+a[x].a-a[x].b);
dfs(x+1,res+a[x].p+a[x].atk,hp+a[x].a+a[x].c);
return;
}//Meet-in-Middle left
void dfs1(int x,int res,int hp){
if(x==n+1){
s2[++top2]=Data(res,hp);
return;
}
dfs1(x+1,res+a[x].p,hp+a[x].a);
dfs1(x+1,res+a[x].p-a[x].def,hp+a[x].a-a[x].b);
dfs1(x+1,res+a[x].p+a[x].atk,hp+a[x].a+a[x].c);
return;
}//Meet-in-Middle right
inline bool cmp(Data a,Data b){
return a.hp>b.hp;
}
inline void solve(){
dfs(1,0,0);
dfs1(n/2+1,0,0);
sort(s1+1,s1+top1+1,cmp);
sort(s2+1,s2+top2+1,cmp);//这次按造成伤害值从小到大排序
int j=top2;
for(int i=1;i<=j;i++) add(s2[i].res,1);
for(int i=1;i<=top1;i++) {
while(s2[j].hp+s1[i].hp<m && j) {add(s2[j].res,-1);j--;}//让造成伤害值始终大于m
if(k-s1[i].res<0) continue;
ans=ans+Sum(k-s1[i].res);//统计此时满足体力限定的方案个数
}
printf("%lld",ans);
return;
}
inline void init();
int main(){
init();
solve();
return 0;
}
inline void init(){//读入...
n=read();m=read();k=read();
for(int i=1;i<=n;i++)a[i].p=read();
for(int i=1;i<=n;i++)a[i].a=read();
for(int i=1;i<=n;i++)a[i].def=read();
for(int i=1;i<=n;i++)a[i].b=read();
for(int i=1;i<=n;i++)a[i].atk=read();
for(int i=1;i<=n;i++)a[i].c=read();
}
A组:
Return
题目描述
他曾经将此作为一种高贵的气质、只留下潇洒的背影,直到他品尝了孤独的滋味、听到黑暗的夜里一个人默默的心跳。他向那个背影追去,却发现又回到原点。
现实中他依然在奔跑,他越跑越快,知道回到最初的时间,于是他又重新开始。然而轮回中、他早已死去,只有在梦魇中他才有一丝气息。
奔跑中的每一秒他都有一个速度,在现实中当然是越来越快的,直到回到最初的速度;在梦里,他同样在奔跑,不过梦,终究只是现实的闪回罢了,逃脱不了无尽的命运。现在你知道他在梦里每一秒的速度,梦里的一秒、即为现实中的一秒。请你统计一下梦里有多少秒在现实中满足 (现实中的上一秒的速度+这一秒的速度)mod(231 -1)=现实中的下一秒的速度。
这是一个长长的梦,所以现实中的每一秒都会在梦里出现的,不必担心会有断裂的时空存在。
输入描述
多组数据。
每组数据第一行一个整数 n,表示梦的长度。
第二行有 n 个整数,第 i 个整数表示梦中第 i 秒他的速度,保证在[0,231 ).
输出描述
对于第 i 组数据输出一行 Case #i: answer;如果现实只有一秒,他以恒定的速度在宇宙中茕茕而行,就像生活那样单调,就像清水一样淡,那么输出-1.
输入样例
3
1 2 3
5
1 2 3 5 7
6
2 3 1 2 7 5
输出样例
Case #1: 1
Case #2: 2
Case #3: 3
数据范围
对于%100 的数据,保证数据组数≤10.
对于%10 的数据,有 n=1.
对于%30 的数据,有 n≤3
对于%60 的数据,有 n≤1000
对于%100 的数据,有 n≤10000
样例3的解释:
现实中的速度是…5 7 1 2 3 5 7 1 2 3 5 7 1 2…
所以在梦里第一秒的速度是2,那么它在现实中前一秒是1,后一秒是3,所以1+2=3,是符合条件的。
梦中第二秒是3,2+3=5,get!
梦中第三秒是1,7+1≠2.
第四秒是2,1+2=3.
第五秒是7,5+7≠1.
第六秒是5,3+5≠7
所以答案就是3啦~
语文题…读懂之后才发现特别水…
sort+unique就行了…
对了-1还要特判一下
#include<algorithm>
#include<ctype.h>
#include<cstdio>
#define N 10050
#define int long long
#define MOD 2147483647
using namespace std;
inline int read(){
int x=0,f=1;char c;
do {c=getchar();if(c=='-') f=-1;} while(!isdigit(c));
do x=(x<<3)+(x<<1)+c-'0',c=getchar(); while(isdigit(c));
return x*f;
}
int n,top,pre,ans,cnt,p,x;
int a[N],b[N];
main(){
while(scanf("%lld",&n)!=EOF){
ans=0ll;
for(int i=1;i<=n;i++) b[i]=a[i]=read();
sort(b+1,b+n+1);
top=unique(b+1,b+n+1)-b-1;
if(top==1){
printf("Case #%lld: %d\n",++p,-1);
continue;
}//特判无解
b[0]=b[top];b[top+1]=b[1];
for(int i=1;i<=n;i++){
x=lower_bound(b+1,b+top+1,a[i])-b;
if((b[x-1]+a[i])%MOD==b[x+1]) ans++;
}
printf("Case #%lld: %lld\n",++p,ans);
}
return 0;
}
One
题目描述
终于都走了。
曾经有 n-1 个人在他身边,然而现在只剩他一个人。
Who are you?Who am I?Why am I here?
走的越来越慢,人越来越少,可终于还是只剩一个了呢。
他们围成一圈,随机了一个人作为 1 号,然后逆时针依次编号。1号开始报数,报到 1,他走了;然后 2 号开始报数,2 号报了 1,3 号报了 2,于是 3 号也走了……每一轮都从上一次出局的下一个人开始报数,第 i 轮从 1 报到 i,报 i 的人出局。
直到只剩他一个人。却早已不记得他自己是谁。
他想知道他最初的号码,那上面承载着最初的记忆和最原始的愿望。
输入描述
第一行一个数 T 表示数据组数。
接下来 T 行,每行一个整数 n。
输出描述
共 T 行,每行一个数表示他原本的编号。
输入样例
2
2
3
输出样例
2
2
数据范围
对于%100 的数据,T≤10
对于%10 的数据,n≤5
对于%30 的数据,n≤3000
对于%60 的数据,n≤10000
对于%100 的数据,n≤10^7
约瑟夫问题
要事先了解一下如何线性推约瑟夫
最后得出的就是F(i)=(F(i-1)+M)%i,F(i)代表报i次数剩下的人是谁,M是每多少个人走一个
不同的是M是变化的,特殊处理一下就可以
关于公式怎么推的,请戳这里
#include<ctype.h>
#include<cstdio>
using namespace std;
inline int read(){
int x=0,f=1;char c;
do {c=getchar();if(c=='-') f=-1;} while(!isdigit(c));
do x=(x<<3)+(x<<1)+c-'0',c=getchar(); while(isdigit(c));
return x*f;
}
int n,x,T;
int main(){
T=read();
while(T--){
n=read();
x=0;
for(int i=2;i<=n;i++) x=(n-i+1+x)%i;
printf("%d\n",x+1);
}
return 0;
}
Phi
数论题,事实上就是个爆搜..
ans=pkii∗pki+1i+1....∗pkjj ,还给出 x=Πipki−1i∗(pi−1) ,我们只要枚举 pi 和相应的 ki 来想方设法拼出一个ans就可以了..
注意大质数的情况
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#define N 100005
#define int long long
using namespace std;
int n,m,cnt,pri[N];
bool b[N];
long long ans;
bool check(int x){
int sz=(int)sqrt(x);
for(int i=1;pri[i]<=sz;i++) if(!(x%pri[i])) return 0;
return 1;
}
void dfs(int k,int now,long long sum){
if(sum>=ans) return;
if(now==1){ans=sum;return;}
if(now>m && check(now+1)) ans=min(ans,sum*(now+1));
for(int i=k+1;pri[i]-1<=m;i++){
if(pri[i]-1>now) break;
if(!(now%(pri[i]-1))){
int x=now/(pri[i]-1);
long long y=sum*pri[i];
dfs(i,x,y);
while(!(x%pri[i])){
x/=pri[i]; y*=pri[i];
dfs(i,x,y);
}
}
}
}
main(){
scanf("%d",&n);m=(int)sqrt(n);
if(n==1){
printf("1");
return 0;
}
for(int i=2;i<=50000;i++){
if(!b[i])pri[++cnt]=i;
for(int j=1;j<=cnt&&i*pri[j]<=50000;j++){
b[i*pri[j]]=true;
if(!(i%pri[j])) break;
}
}
ans=2147483648ll;
dfs(1,n,1);
if(ans<=2147483647) printf("%lld",ans);
else printf("-1");
return 0;
}