NOIP 2014
DAY1
T1 生活大爆炸版石头剪刀布
一道简单的模拟题,给出对应的关系,要打表!打表!打表!
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
//Golbel define
const int N = 210;
const int map[5][5] = {
{ 0,-1, 1, 1,-1},
{ 1, 0,-1, 1,-1},
{-1, 1, 0,-1, 1},
{-1,-1, 1, 0, 1},
{ 1, 1,-1,-1, 0}
};
int afi[N],bfi[N];
int n,lena,lenb,nowa,nowb,scoa,scob;
int main(){
freopen("rps.in","r",stdin);
freopen("rps.out","w",stdout);
scanf("%d",&n);
scanf("%d%d",&lena,&lenb);
for(int i = 0;i < lena;++i)scanf("%d",&afi[i]);
for(int i = 0;i < lenb;++i)scanf("%d",&bfi[i]);
for(int i = 1;i <= n;++i){
if(nowa == lena)nowa = 0;
if(nowb == lenb)nowb = 0;
if(map[afi[nowa]][bfi[nowb]] == 1)scoa++;
else if(map[afi[nowa]][bfi[nowb]] == -1)scob++;
nowa++;
nowb++;
}
printf("%d %d\n",scoa,scob);
return 0;
}
T2 联合权值
求出距离为2的所有点的权值的乘积的和的两倍,枚举中点,直接算所有点的积,显然超时,那么计算所有点(a+b+c+d+.....+n)^2-(a^2-b^2+c^2+d^2+...+n^2)
#include <cstdio>
#include <iostream>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
//Golbel define
const int N = 200010;
const int mod = 10007;
vector <int> edge[N];
int n,u,v,w[N],ans,maxi;
//end Golebel define
int main(){
freopen("link.in","r",stdin);
freopen("link.out","w",stdout);
scanf("%d",&n);
for(int i = 1;i < n;++i){
scanf("%d%d",&u,&v);
edge[u].push_back(v);
edge[v].push_back(u);
}
for(int i = 1;i <= n;++i){
scanf("%d",&w[i]);
}
for(int i = 1;i <= n;++i){
int size = edge[i].size();
int sum = 0,tot = 0,ret = 0;//sum = {a+b+c...+n}^2 tot = {a^2+b^2+c^2...+n^2}
int maxfirst = 0,maxsecond = 0;
for(int j = 0;j < size;++j){
int v = edge[i][j];
if(w[v]>maxfirst){
maxsecond = maxfirst;
maxfirst = w[v];
}
else if(w[v]>maxsecond){
maxsecond = w[v];
}
sum = (sum%mod + w[v]%mod)%mod;
tot = (tot%mod + (w[v]*w[v])%mod)%mod;
}
maxi = max(maxi,(maxfirst*maxsecond));
ret = ( ( (sum*sum)%mod-tot%mod )%mod+mod)%mod;
(ans += ret) %= mod;
}
printf("%d %d\n",maxi,ans);
return 0;
}
T3 飞扬的小鸟
定义状态dp[i][j]为在(i,j)至少按的次数
总共有两种情况{
一.上升:1.上升一步 dp[i][j] = min{dp[i-1][j-x[i-]]+1}
2.上升多步 dp[i][j] = min{dp[i-1][j-k*x[i-1]]+k}这样是会超时的,因为有重叠子问题,那么先考虑飞的情况后考虑坠的情况,那么由下面的点转移而来
min{dp[i][j-x[i-1]]+1}。
3.上升到图的最高点并且只一步dp[i][m] = min{dp[i-1][k]+1}(m-x[i-1]<=k<=m)
4.上升到图的最高点多步的情况dp[i][m] = min(dp[i][k]+1)
二.下降:dp[i][j] = min{dp[i-1][j+y[i-1]]}
}
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
//Golbel define
const int inf = 0x3fffffff;
const int M = 1010;
const int N = 10010;
int n,m,k;
int dp[N][M];
int fly[N],fal[N],up[N],down[N],x[N];
//end Golbel defien
int main(){
freopen("bird.in","r",stdin);
freopen("bird.out","w",stdout);
scanf("%d%d%d",&n,&m,&k);
for(int i = 0;i < n;++i){
scanf("%d%d",&fly[i],&fal[i]);
}
for(int i = 1;i <= n;++i){
up[i] = m;
down[i] = 1;
}
for(int i = 1;i <= k;++i){
int l,h;
scanf("%d%d%d",&x[i],&l,&h);
down[x[i]] = l+1;up[x[i]] = h-1;
}
for(int i = 0;i <= n;++i){
for(int j = 0;j <= m;++j){
dp[i][j] = inf;
}
}
for(int i = 1;i <= m;++i)dp[0][i] = 0;
for(int i = 1;i <= n;++i){
for(int j = 1;j <= m;++j){
if(j > fly[i-1]){
dp[i][j] = min(dp[i-1][j-fly[i-1]]+1,dp[i][j]);
dp[i][j] = min(dp[i][j-fly[i-1]]+1,dp[i][j]);
}
if(j == m){
for(int k = m-fly[i-1];k <= m;++k){
dp[i][j] = min(dp[i][j],dp[i-1][k]+1);
dp[i][j] = min(dp[i][j],dp[i][k]+1);
}
}
}
for(int j = down[i];j <= up[i];++j){
if(j+fal[i-1] <= m){
dp[i][j] = min(dp[i-1][j+fal[i-1]],dp[i][j]);
}
}
for(int j = 0;j < down[i];++j)dp[i][j] = inf;
for(int j = up[i]+1;j <= m;++j)dp[i][j] = inf;
}
int ans = inf;
for(int i = 1;i <= m;++i)ans = min(ans,dp[n][i]);
if(ans != inf){
printf("1\n%d\n",ans);
}
else{
puts("0");
int cnt = 0,flag = false;
for(int i = n-1;i >= 0;--i){
if(flag)break;
for(int j = down[i];j <= up[i];++j){
if(dp[i][j] != inf){
flag = true;
cnt = i;
break;
}
}
}
sort(x+1,x+k+1);
for(int i = 1;i <= k;++i){
if(cnt < x[i]){
printf("%d\n",i-1);
break;
}
}
}
return 0;
}
DAY2
T1 无线网络发射器选址
暴力枚举每一个地址,暴力计算,注意边界,也可以用前缀和优化,也可以套二维树状数组。
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
#define LL long long
const int N = 200;
LL map[N][N],sum[N][N];
int main(){
freopen("wireless.in","r",stdin);
freopen("wireless.out","w",stdout);
int n,d,x,y;
LL k;
scanf("%d%d",&d,&n);
for(int i = 1;i <= n;++i){
scanf("%d%d%I64d",&x,&y,&k);
map[x][y] = k;
}
LL ansi = 0,maxi = -1;
for(int i = 0;i <= 128;++i){
for(int j = 0;j <= 128;++j){
LL cnt = 0;
for(int a = max(0,i-d);a <= min(i+d,128);++a){
for(int b = max(0,j-d);b <= min(j+d,128);++b){
cnt += map[a][b];
}
}
if(cnt > maxi){
maxi = cnt;
ansi = 1;
}
else if(cnt == maxi){
ansi++;
}
}
}
cout<<ansi<<" "<<maxi<<endl;
return 0;
}
T2 寻找道路
问一条特殊的最短路,特殊在于每一点所连的点都能够到达终点。从终点反向一次搜索把能够到达终点的点都标记下来,再正向一次枚举若标记的点可以到达没有标记的点,那么这个点就取消标记,最后一次Dijkstra最短路。
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 10010;
const int M = 800010;
const int inf = 1<<30;
int n,m,x,y,s,t;
struct edge{
int v,w;
edge *nxt;
}*cur,*head[N],*back_head[N],meo[M];
struct node{
int dis,u;
bool operator < (const node &rhs)const{
return dis >rhs.dis;
}
};
int mark[N],done[N],dis[N],vis[N];
void adde(int u,int v,edge *f[]){
cur->v = v;
cur->w = 1;
cur->nxt = f[u];
f[u] = cur++;
}
void dfs(int x){
mark[x] = 1;
for(edge *it = back_head[x];it;it = it->nxt){
int v = it->v;
if(!mark[v])dfs(v);
}
}
void Dijkstra(int s){
priority_queue <node> q;
for(int i = 1;i <= n;++i)dis[i] = inf;
dis[s] = 0;
q.push((node){dis[s],s});
while(!q.empty()){
node p = q.top();q.pop();
int u = p.u;
if(done[u])continue;
done[u] = true;
for(edge *it = head[u];it;it = it->nxt){
int v = it->v;
if(dis[v] > dis[u]+it->w && mark[v] == 1){
dis[v] = dis[u]+it->w;
q.push((node){dis[v],v});
}
}
}
}
int main(){
freopen("road.in","r",stdin);
freopen("road.out","w",stdout);
cur = meo;
scanf("%d%d",&n,&m);
for(int i = 1;i <= m;++i){
scanf("%d%d",&x,&y);
adde(x,y,head);
adde(y,x,back_head);
}
scanf("%d%d",&s,&t);
memset(mark,0,sizeof(mark));
dfs(t);
for(int i = 1;i <= n;++i){
if(mark[i]){
for(edge *it = head[i];it;it = it->nxt){
int v = it->v;
if(!mark[v]){
mark[i] = -1;//不能为0
break;
}
}
}
}
Dijkstra(s);
if(dis[t] == inf)puts("-1");
else printf("%d\n",dis[t]);
return 0;
}
T3 解方程
f(x) = a[0]+a[1]*x+a[2]*x^2+a[3]*x^3+......a[n]*x^n = 0;
既然f(x) = 0,0%p = 0,那么f(x)%p = 0,这里运用hash的思想,这是不对的想法但是错误的概率很小,类似的费马小定理的逆用是相似的,那么很容易想到的是一个O(n)枚举所有在范围里面的解,但是是要超时的,既然f(x)%p = 0,那么很显然f(x+p)%p = 0,那么就先预处理出,x在0~p-1的范围的数,大于其的数直接%p来判断方程是否为0。
类似于Hash要选择素数。
附加神犇vfk的想法:
我们可以选一个好取模的素数比如2^31−1=2147483647。
由于 a⋅2^31+b≡a+b(mod2^31−1),所以 x 与 (x & 0x7fffffff) + (x >> 31) 是同余的!
register unsigned long long y = 0;
for (int i = n; i >= 0; i--)
{
y = y * x + a[i];
y = (y & 0x7fffffff) + (y >> 31);
}
y %= 0x7fffffff;
这样就跑得快了三倍了!(从1.2s变成了0.4s)
在此基础上再多加几个奇怪的素数模一模判一判就好了。只要不是零多项式那么至多只有n个根,所以后面不加什么常数优化也没关系。
献上我的代码:
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <map>
using namespace std;
#define LL long long
const int N = 110;
const int mod1 = 38833;
const int mod2 = 998244353;
const int M = 1000010;
char s[10010];
int n,m,a[M],ans[M],cnt,b[M],first[M],second[M];
int main(){
freopen("equation.in","r",stdin);
freopen("equation.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i = 0;i <= n;++i){
scanf("%s",s);
int x1 = 0,x2 = 0,flag = 1;
if(s[0] == '-')flag = -1;
else {
x1 = s[0]-'0';
x2 = s[0]-'0';
}
for(int i = 1;i < strlen(s);++i){
x1 = (x1*10+s[i]-'0')%mod1;
x2 = ((LL)x2*10+s[i]-'0')%mod2;
}
a[i] = x1,b[i] = x2;
if(~flag){
a[i] = -x1;
b[i] = -x2;
}
}
for(int i = 0;i < mod1;++i){
ans[i] = a[n];
for(int j = n-1;~j;--j)ans[i] = (ans[i]*i+a[j])%mod1;
}
for(int i = 1;i <= m;++i){
if(!ans[i%mod1])first[++(*first)] = i;//f(x+p)%p = 0 -> f(x)%p = 0
}
for(int i = 1;i <= *first;++i){
int x = first[i];ans[i] = b[n];
for(int j = n-1;~j;--j)ans[i] = ((LL)ans[i]*x+b[j])%mod2;
}
for(int i = 1;i <= *first;++i)if(!ans[i])second[++(*second)] = first[i];
printf("%d\n",second[0]);
for (int i = 1; i <= second[0]; ++i) printf("%d\n", second[i]);
return 0;
}

浙公网安备 33010602011771号