您的位置:首页 > 其它

考试 —— NOI2016 山东省队选拔第一轮 Day 1

2016-05-22 14:49 225 查看

NOI2016 山东省队选拔第一轮 Day 1

被虐得好爽。



T1 储能表

【问题描述】

有一个 n 行m 列的表格,行从 0 到 n−1 编号,列从 0 到 m−1 编号。

每个格子都储存着能量。最初,第 i 行第 j 列的格子储存着(i xor j)点能量。

所以,整个表格储存的总能量是,



随着时间的推移,格子中的能量会渐渐减少。

一个时间单位,每个格子中的能量会减少 1。

显然,一个格子的能量减少到 0 之后就不会再减少了。

也就是说,k 个时间单位后,整个表格储存的总能量是,



给出一个表格,求 k 个时间单位后它储存的总能量。

由于总能量可能较大,输出时对 p 取模。

【输入格式】

第一行一个整数 T,表示数据组数。

接下来 T 行,每行四个整数 n、m、k、p。

【输出格式】

共 T 行,每行一个数,表示总能量对 p 取模后的结果。

【样例输入】

3

2 2 0 100

3 3 0 100

3 3 1 100

【样例输出】

2

12

6

【数据规模和约定】



【想法】

这个题目我开始是想找规律,然后确实发现了一些“规律”,结果敲了半个小时发现错了,改进“规律”后又敲了半个小时,结果发现又错了,然后又改进………

然后,然后就没有然后了。

当我幡然醒悟,考试就结束了QWQ

据说题目正解是 “shuwei” DP,反正我是不懂。

T2 数字配对

【问题描述】

有 n 种数字,第 i 种数字是 ai 、有 bi 个,权值是 ci 。

若两个数字 ai 、aj 满足, ai 是 aj 的倍数,且 aiaj 是一个质数,那么这两个数字可以配对,并获得 ci⋅cj 的价值。

一个数字只能参与一次配对,可以不参与配对。

在获得的价值总和不小于 0 的前提下,求最多进行多少次配对。

【输入格式】

第一行一个整数 n。

第二行 n 个整数 a1 、a2 、……、an 。

第三行 n 个整数 b1 、b2 、……、bn 。

第四行 n 个整数 c1 、c2 、……、cn 。

【输出格式】

一行一个数,最多进行多少次配对。

【样例输入】

3

2 4 8

2 200 7

-1 -2 1

【样例输出】

4

【数据规模和约定】



【Solution】

在考场上根本就没有想,只是最开始阅读所有题目时扫了一眼,之后就再也没有看过了。

当时就感觉这道题有二分图的味道,配对是吧,那不就是连边吗???

我们将所有满足条件的几对不同种类的数连起来,然后从源点向这对数的一边连一条容量为 bi,费用为 0 的边,从这对数的另一边向汇点连一条容量为 bj,费用为 0 的边,然后将它们之间连一条容量为 +∞,费用为 −ci⋅cj 的边。

然后不断地找最小费用增广路。

每次增广,我们付出的都是最小的代价。

所以,当 当前最小费用 大于 0 时,我们就可以知道,当前的最大流就为所求解。

注意,最后一次增广时的流量也要计算进去啊(找到能使 当前最小费用 小于等于 0 的最大的流量),具体实现请见代码。

问题又来了,那我们怎么判断从一对数的哪一边连到汇点和源点呢??

根据唯一分解定理,

ai=Pd11⋅Pd22⋅Pd33......Pdll,dk∈N,

aj=Pe11⋅Pe22⋅Pe33……Peqq,ek∈N,

所以,若 ai≡0(mod aj) 且 aiaj 为质数,那么有且只有一个 k∈N∗,使得 dk=ek+1,dm=em,m≠k,l=q。

综上,∑k=1pdk=∑k=1qek+1,所以 ai 与 aj 的质因子个数必定一奇一偶,所以我们可以按照质因子个数的奇偶性将一对数对给分成两边。

问题得解。

【Code】

[cpp] #include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <cmath>

#define LL long long

#define ANOTHER 200
#define Min(x,y) ((x)<(y)?(x):(y))
#define INF 0x3f3f3f3f
#define ss 0
#define tt 500

using namespace std;

LL n,Minx,ans,cnt,tot,Max_flow,sum;
bool flag;
bool lefts[1000];
LL a[1000],b[1000],c[1000];
LL nxt[100100],head[510];
LL data[100100],wei[100100],flow[100100];
LL f[300][300];
LL dis[510],pre[510];
bool in_stack[510];
queue<LL>q;

void add(LL x,LL y,LL a,LL b){
nxt[cnt]=head[x];data[cnt]=y;wei[cnt]=b;flow[cnt]=a;head[x]=cnt++;
nxt[cnt]=head[y];data[cnt]=x;wei[cnt]=-b;flow[cnt]=0;head[y]=cnt++;
}

bool BFS(){
memset(dis,0x3f,sizeof dis);dis[ss]=0;in_stack[ss]=true;q.push(ss);pre[ss]=pre[tt]=-1;
while(!q.empty()){
LL now=q.front();
q.pop();
in_stack[now]=false;
for(LL i=head[now];i!=-1;i=nxt[i]){
if(flow[i]!=0&&dis[data[i]]>dis[now]+wei[i]){
dis[data[i]]=dis[now]+wei[i];
pre[data[i]]=i^1;
if(!in_stack[data[i]]){
in_stack[data[i]]=true;
q.push(data[i]);
}
}
}
}
return pre[tt]!=-1;
}

bool dfs(){

LL Low=INF;

for(LL i=pre[tt];i!=-1;i=pre[data[i]])Low=Min(Low,flow[i^1]);
for(LL i=pre[tt];i!=-1;i=pre[data[i]])flow[i^1]-=Low,flow[i]+=Low;

LL tmp=ans;

ans+=Low*dis[tt];

if(ans>0){
printf(”%I64d\n”,(0-tmp)/dis[tt]+Max_flow);
flag=true;
return false;
}
Max_flow+=Low;
return true;
}

bool is_prime(LL x){
LL px=(LL)sqrt((double)x);
for(LL i=2;i<=px;i++)if(!(x%i))return false;
return true;
}

int main(){

freopen(”pair.in”,“r”,stdin);
freopen(”pair.out”,“w”,stdout);

memset(head,-1,sizeof head);

scanf(”%I64d”,&n);
for(LL i=1;i<=n;i++)scanf(“%I64d”,&a[i]);
for(LL i=1;i<=n;i++){
scanf(”%I64d”,&b[i]);
sum+=b[i];
}
for(LL i=1;i<=n;i++)scanf(“%I64d”,&c[i]);

for(LL i=1;i<=n;i++){
LL tmp=a[i];
LL tot=0;
for(LL j=2;j<=tmp;j++){
if(tmp%j==0){
while(tmp%j==0){
tmp/=j;
tot++;
}
}
}
if((tot&1))
lefts[i]=true;
}

for(LL i=1;i<=n;i++){
for(LL j=i+1;j<=n;j++)
if((a[i]%a[j]==0&&is_prime(a[i]/a[j]))||(a[j]%a[i]==0&&is_prime(a[j]/a[i]))){
if(lefts[i])
add(i,j,INF,-c[i]*c[j]);
else
add(j,i,INF,-c[i]*c[j]);
}
}

for(LL i=1;i<=n;i++){
if(lefts[i])
add(ss,i,b[i],0);
else
add(i,tt,b[i],0);
}
while(BFS())if(!dfs())break;
if(!flag)printf(“%I64d\n”,Max_flow);

return 0;
}

T3 游戏

【问题描述】

Alice 和 Bob 在玩一个游戏。

游戏在一棵有 n 个点的树上进行。

最初,每个点上都只有一个数字,那个数字是 123456789123456789。

有时,Alice 会选择一条从 s 到 t 的路径,在这条路径上的每一个点上都添加一个数字。

对于路径上的一个点 r,若 r 与 s 的距离是dis,那么 Alice 在点 r 上添加的数字是a⋅dis+b。

有时,Bob 会选择一条从 s 到 t 的路径。

他需要先从这条路径上选择一个点,再从那个点上选择一个数字。

Bob 选择的数字越小越好,但大量的数字让Bob 眼花缭乱。

Bob 需要你帮他找出他能够选择的最小的数字。

【输入格式】

第一行两个数字 n、m,表示树的点数和进行的操作数。

接下来 n−1 行,每行三个数字 u、v、w,表示树上有一条连接 u、v 的边,长度是 w。

接下来 m 行,每行第一个数是 1 或 2。

若第一个数是 1,表示 Alice 进行操作,接下来四个数字 s、t、a、b。

若第一个数是 2,表示 Bob 进行操作,接下来两个数字 s、t。

【输出格式】

每当 Bob 进行操作,输出一行一个数,表示他能够选择的最小的数字。

【样例输入】

3 5

1 2 10

2 3 20

2 1 3

1 2 3 5 6

2 2 3

1 2 3 -5 -6

2 2 3

【样例输出】

123456789123456789

6

-106

【数据规模和约定】



【想法】

据说是搜索?
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: