没来的及写上一次的就水掉了
%%%%%考场AK的肥佬
day1
T1 玩具谜题
1e5的复杂度直接取模跳就好了没有那么多技巧性
但是有一点需要注意啊
要么就从0开始输入
要么要注意Mod n之后是否为0,要不会死的不明不白,输出0(但是似乎没有这个东西)
AC代码
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=1e5+5;
int n,m;
struct point
{
int loc,id;
char name[15];
}p[maxn];
int main()
{
scanf("%d%d",&n,&m);
for (int i=0;i<n;i++)
{
scanf("%d",&p[i].loc);
scanf("\n");
cin>>p[i].name;
p[i].id=i;
}
int pos=p[0].id;
for (int i=1;i<=m;i++)
{
int pd,len;
scanf("%d%d",&pd,&len);
if (pd==0)
{
if (p[pos].loc==1)
{
pos=(pos+len)%n;
}
else
{
pos=(pos+n-len)%n;
}
}
else
{
if (p[pos].loc==0)
{
pos=(pos+len)%n;
}
else
{
pos=(pos+n-len)%n;
}
}
}
cout<<p[pos].name;
return 0;
}
T2 天天爱跑步
Noip近几年最难的题 没有之一,据说sc当年只有6人AC
我到现在还不是很会做,但是大概思路可以理一下
我们先考虑 u ---> LCA(u,v) 这条路径,这是一条向“上”跑的路径。
对与这条路径上的点i来说,当且仅当deep[i]+w[i] = deep[u]时,u节点对i节点是有贡献的。
那么也就是说,只要符合deep[i]+w[i]的全部是玩家起点的点,就能对i点产生贡献。
对于向下走的路径,我们也思考,在什么条件下,这条路径上的点会获得贡献呢?
很明显的,当 dis(u,v)-deep[v] = w[i]-deep[i] 等式成立的时候,这条路径将会对i点有贡献。
所以就可以用树上差分的思想然后就可以开心的贴别人的代码了
#include<cstdio>
#include<cstring>
#include<cmath>
#include<vector>
#include<algorithm>
#define N 300009
#define M 600009
using namespace std;
int en,n,m;
int w[N],spn[M],bucket[N+M],ans[N];
vector<int> v1[M],v2[M],v3[M];
struct nod{
int u,v,dis,lca;
}p[N];
struct edge{
int e;
edge *next;
}*v[N],ed[M];
inline void add_edge(int s,int e){
en++;
ed[en].next = v[s],v[s] = ed+en,v[s]->e =e;
}
int read(){
int x = 0;
char ch = getchar();
while(ch < '0' || ch > '9')ch = getchar();
while(ch >= '0' && ch <= '9'){
x = x * 10 + ch - '0';
ch = getchar();
}
return x;
}
int deep[N],f[N][25],dist[N];
bool use[N];
void dfs(int now,int dep){
use[now] = true;
deep[now] = dep;
for(int k = 1;k <= 22; k++){
int j = f[now][k-1];
f[now][k] = f[j][k-1];
}
for(edge *e = v[now];e;e=e->next)
if(!use[e->e]){
f[e->e][0] = now;
dist[e->e] = dist[now]+1;
dfs(e->e,dep+1);
}
use[now] = false;
}
inline int jump(int u,int step){
for(int k = 0; k <= 22; k++)
if((step & (1<<k)))u = f[u][k];
return u;
}
inline int qlca(int u,int v){
if(deep[u] < deep[v])swap(u,v);
u = jump(u,deep[u]-deep[v]);
for(int k = 22; k >= 0; k--)
if(f[u][k] != f[v][k])u = f[u][k],v = f[v][k];
return u == v ? u : f[u][0];
}
void LCA(){ //关于LCA的组件
f[1][0] = 1;
dfs(1,0);
}
inline void dfs1(int now){
use[now] = true;
int prev = bucket[deep[now]+w[now]+N];
for(edge *e = v[now];e;e=e->next)
if(!use[e->e])dfs1(e->e);
bucket[deep[now]+N] += spn[now];
ans[now] += bucket[deep[now]+w[now]+N]-prev;
int len = v1[now].size();
for(int k = 0; k < len;k++)
--bucket[deep[v1[now][k]]+N];
use[now] = false;
}
inline void dfs2(int now){
use[now] = true;
int prev = bucket[w[now]-deep[now]+N];
for(edge *e = v[now];e;e=e->next)
if(!use[e->e])dfs2(e->e);
int len = v2[now].size();
for(int k = 0; k < len; k++)
++bucket[v2[now][k]+N];
ans[now] += bucket[w[now]-deep[now]+N] - prev;
len = v3[now].size();
for(int k = 0; k < len; k++)
--bucket[v3[now][k]+N];
use[now] = false;
}
int main(){
n = read(),m = read();
for(int i = 1; i <= n-1; i++){
int u = read(), v = read();
add_edge(u,v);
add_edge(v,u);
}
for(int i = 1; i <= n; i++)w[i] = read();
LCA();
for(int i = 1; i <= m; i++){ //预处理
int u = read(),v = read();
p[i].u = u;
p[i].v = v;
p[i].lca = qlca(u,v);
p[i].dis = dist[u]+dist[v]-dist[p[i].lca]*2;
spn[u]++;
v1[p[i].lca].push_back(u);
v2[v].push_back(p[i].dis-deep[p[i].v]);
v3[p[i].lca].push_back(p[i].dis-deep[p[i].v]);
}
dfs1(1); //从下至上
dfs2(1); //从上至下
for(int i = 1; i <= m; i++)
if(deep[p[i].u] == deep[p[i].lca]+w[p[i].lca]) ans[p[i].lca]--;
for(int i = 1; i <= n; i++)
printf("%d ",ans[i]);
printf("\n");
return 0;
}
T3 水的一批的期望dp
dp[i][j][0/1]表示在第i个时间换了j次课,这次是否成功
那么状态转移方程如下
dp[i][j][0]=min(dp[i-1][j][0]+dis[c[i-1]][c[i]]
,dp[i-1][j][1]+dis[d[i-1]][c[i]]*k[i-1]+dis[c[i-1]][c[i]]*(1-k[i-1]));
if (j>0)
dp[i][j][1]=min(dp[i-1][j-1][1]+(1-k[i])*(1-k[i-1])*dis[c[i-1]][c[i]]+(1-k[i-1])*k[i]*dis[c[i-1]][d[i]]+k[i-1]*(1-k[i])*dis[d[i-1]][c[i]]+k[i-1]*k[i]*dis[d[i-1]][d[i]]
,dp[i-1][j-1][0]+k[i]*dis[c[i-1]][d[i]]+(1-k[i])*dis[c[i-1]][c[i]]);
真的写到眼睛爆炸,而且细节贼多,一不小心就错一片
AC代码
#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
int n,m,v,e;
int c[2005],d[2005];
double dp[2005][2005][2];
double ans=1e30;
double k[2005];
int dis[305][305];
int main()
{
memset(dis,0x3f3f3f,sizeof(dis));
scanf("%d%d%d%d",&n,&m,&v,&e);
for (int i=1;i<=v;i++)
dis[i][i]=0;
for (int i=1;i<=n;i++)
scanf("%d",&c[i]);
for (int i=1;i<=n;i++)
scanf("%d",&d[i]);
for (int i=1;i<=n;i++)
scanf("%lf",&k[i]);
for (int i=1;i<=e;i++)
{
int x,y,f;
scanf("%d%d%d",&x,&y,&f);
if (x!=y)
dis[x][y]=dis[y][x]=min(f,dis[x][y]);
}
for (int kk=1;kk<=v;kk++)
for (int i=1;i<=v;i++)
for (int j=1;j<=v;j++)
dis[i][j]=min(dis[i][kk]+dis[kk][j],dis[i][j]);
for (int i=1;i<=n;i++)
for (int j=0;j<=m;j++)
dp[i][j][0]=dp[i][j][1]=1e30;
dp[1][1][1]=0.0;
dp[1][0][0]=0.0;
for (int i=2;i<=n;i++)
for (int j=0;j<=m;j++)
{
dp[i][j][0]=min(dp[i-1][j][0]+dis[c[i-1]][c[i]]
,dp[i-1][j][1]+dis[d[i-1]][c[i]]*k[i-1]+dis[c[i-1]][c[i]]*(1-k[i-1]));
if (j>0)
dp[i][j][1]=min(dp[i-1][j-1][1]+(1-k[i])*(1-k[i-1])*dis[c[i-1]][c[i]]+(1-k[i-1])*k[i]*dis[c[i-1]][d[i]]+k[i-1]*(1-k[i])*dis[d[i-1]][c[i]]+k[i-1]*k[i]*dis[d[i-1]][d[i]]
,dp[i-1][j-1][0]+k[i]*dis[c[i-1]][d[i]]+(1-k[i])*dis[c[i-1]][c[i]]);
}
for (int i=0;i<=m;i++)
ans=min(ans,min(dp[n][i][0],dp[n][i][1]));
printf("%0.2lf",ans);
return 0;
}
day2
T1感觉很简单但是调了一个小时很难受
直接杨辉三角形求组合数然后去模统计答案就好了
AC代码
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=2*1e3+5;
int c[maxn][maxn],ans[maxn][maxn];
int cnt[maxn];
int n,m,Mod,t;
void yuchuli()
{
for (int i=0;i<=maxn-1;i++)
c[i][0]=1;
for (int i=1;i<=maxn-1;i++)
for (int j=1;j<=i;j++)
{
c[i][j]=(c[i-1][j]%Mod+c[i-1][j-1]%Mod)%Mod;
if (!c[i][j]) ans[i][j]=1;
}
for (int i=1;i<=maxn-1;i++)
for (int j=1;j<=i;j++)
{
if (i!=j)
ans[i][j]=ans[i-1][j]+ans[i][j-1]-ans[i-1][j-1]+(c[i][j]==0);
else
ans[i][j]=ans[i][j-1]+(c[i][j]==0);
}
}
int main()
{
freopen("problem.in","r",stdin);
freopen("problem.out","w",stdout);
scanf("%d%d",&t,&Mod);
yuchuli();
while (t--)
{
scanf("%d%d",&n,&m);
printf("%d\n",ans[n][min(n,m)]);
}
return 0;
}
T2蚯蚓考场上没想明白,时间全部砸在T3上面了
其实感性的想一想,在你切割之后,放进队列的是同样没有加长的蚯蚓,每次切完之后伸长的量又相同,所以他本身就是单调的,只是答案用一个优先队列去维护就可以了(tail++和++tail天壤之别导致调试一万年)
#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int maxn=1e7+5;
int cut1[maxn],cut2[maxn],len[maxn];
int n,m,q,u,v,t;
priority_queue<int> ans;
double p;
bool cmp(int a,int b)
{
return a>b;
}
int main()
{
scanf("%d%d%d%d%d%d",&n,&m,&q,&u,&v,&t);
int head=1,tail=0;
int head1=1,tail1=0;
int head2=1,tail2=0;
for (int i=1;i<=n;i++)
{
scanf("%d",&len[i]);
tail++;
}
sort(len+1,len+n+1,cmp);
p=(double)(1.0*u)/(1.0*v);
for (int i=1;i<=m;i++)
{
int top;
if(head>tail)
{
if(cut1[head1]>cut2[head2])
top=cut1[head1++];
else
top=cut2[head2++];
}
else if(len[head]>=cut1[head1]&&len[head]>=cut2[head2])
top=len[head++];
else if(cut1[head1]>=cut2[head2]&&len[head]<=cut1[head1])
top=cut1[head1++];
else top=cut2[head2++];
top+=(i-1)*q;
int a1=floor(top*1.0*p);
int a2=top-a1;
a1-=i*q;
a2-=i*q;
cut1[++tail1]=a1;
cut2[++tail2]=a2;
if (i%t==0)
printf("%d ",top);
}
printf("\n");
for (int i=head;i<=tail;i++)
ans.push(len[i]);
for (int i=head1;i<=tail1;i++)
ans.push(cut1[i]);
for (int i=head2;i<=tail2;i++)
ans.push(cut2[i]);
for (int i=1;ans.size();i++)
{
if (i%t==0)
printf("%d ",ans.top()+m*q);
ans.pop();
}
return 0;
又到了激动人心的T3了,是一道经典的状压dp原题
状压主要是用二进制位和子集来表示状态,通常适用于数据比较小的时候(因为他是pow(2,n))
好了来一波dp啊
首先要预处理,这里要涉及精度问题,很开心的是我没有过精度,被卡掉了两组
算了dp方程自己看吧
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int maxn=20;
const int eps=1e-10;
struct hanshu
{
double a,b;
int num;
}f[maxn][maxn];
int dp[1<<maxn];
double x[maxn],y[maxn];
int t,n,m;
void init()
{
memset(dp,0x3f3f3f,sizeof(dp));
memset(f,0,sizeof(f));
memset(x,0,sizeof(x));
memset(y,0,sizeof(y));
}
void getf(int u,int v)
{
double x1=x[u];
double y1=y[u];
double x2=x[v];
double y2=y[v];
double tmp=(y2*x1-y1*x2)/(x1*(x2*x2-x2*x1));
double b=(y1-tmp*x1*x1)/x1;
if (tmp-eps>=eps||b<=-eps)
return ;
f[u][v].a=f[v][u].a=tmp;
f[u][v].b=f[v][u].b=b;
f[u][v].num=f[v][u].num=f[u][v].num|(1<<(u-1));
f[u][v].num=f[v][u].num=f[u][v].num|(1<<(v-1));
return ;
}
void yuli()
{
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
{
if (x[i]==x[j])
continue;
getf(i,j);
}
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
for (int k=1;k<=n;k++)
if (fabs(f[i][j].a*x[k]*x[k]+f[i][j].b*x[k]-y[k])<=eps&&k!=j&&k!=i)
f[i][j].num=f[j][i].num=f[i][j].num|(1<<(k-1));
}
int main()
{
scanf("%d",&t);
int cmp=t;
while (t--)
{
scanf("%d%d",&n,&m);
init();
for (int i=1;i<=n;i++)
scanf("%lf%lf",&x[i],&y[i]);
yuli();
dp[0]=0;
for (int i=1;i<=n;i++)
f[i][i].num=(f[i][i].num|(1<<i-1));
for(int s=0;s<=(1<<n)-1;s++)
{
int t=1;
while(s>>(t-1)&1)
{
t+=1;
}
dp[s|1<<(t-1)]=std::min(dp[s|(1<<t-1)],dp[s]+1);
for(int j=t+1;j<=n;j++)
{
dp[s|f[t][j].num]=std::min(dp[s|f[t][j].num],dp[s]+1);
}
}
printf("%d\n",dp[(1<<n)-1]);
}
return 0;
}
在这里输出的时候有个地方可以注意一下,这里显然不会有n^2条抛物线,所以在状态转移的时候就从找到的第一个没有被打的开始转移,寻找一个子集中有这个点的来进行状态转移,这样就会快很多。比n^2 for 不知道快到哪里去了
好了总结完了,两天下来300+多一点点这就很难受了,所以还要努力啊