这两天做了noip2011 的题。
于是来写写题解。
若有错误请大家指出。。。
DAY 1
1、
直接判断点是否在矩形内,看最后一次是哪一张就是了,注意边界。
或者你也可以倒着来 注意 break;
反正对于矩形 四点的坐标确定也就确定了
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<cstdio>
using namespace std;
#define mem(a,x) memset(a,0,sizeof(a))
struct node{
int x,y,a,b;
}q[10050];
int n;
int tax,tay;
int ans;
void readdata(){
scanf("%d",&n);
for( int i=1 ;i<=n ;i++)
{
scanf("%d%d%d%d",&q[i].x,&q[i].y,&q[i].a,&q[i].b);
}
scanf("%d%d",&tax,&tay);
}
int main(){
readdata();
for(int i=1; i<=n; i++)
{
if(q[i].x<=tax && q[i].x+q[i].a>=tax && q[i].y<=tay && q[i].y+q[i].b>=tay)
ans=i;
}
if(ans == 0)printf("-1");
else printf("%d",ans);
return 0;
}
2、
此题首先得理清题意。
好然后谁都一定可以来一个暴力。
然后 你也可以来一个线段树 但有些注意条件
#include <cstdio>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <cmath>
using namespace std;
#define GETMID int mid = (l + r) >> 1
#define lson u << 1
#define rson u << 1 | 1
#define Lson u << 1, l, mid
#define Rson u << 1 | 1, mid + 1, r
#define Maxn (200000 + 10)
int n, k, p;
int pri[Maxn];
int col[51][200001];
int kcnt = 0;
int pos[51];
int vis[51] = {0};
long long Ans = 0;
int mi[Maxn * 4];
void Build(int u, int l, int r) {
if(l == r) {
mi[u] = pri[l];
return;
}
GETMID;
Build(Lson);
Build(Rson);
mi[u] = min(mi[lson], mi[rson]);
}
int Query(int u, int l, int r, int L, int R) {
if(L <= l && r <= R) return mi[u];
GETMID;
int ret = 1000;
if(L <= mid) ret = min(ret, Query(Lson, L, R));
if(R > mid) ret = min(ret, Query(Rson, L, R));
return ret;
}
int main() {
scanf("%d%d%d", &n, &k, &p);
for(int i = 1; i <= n; ++i) {
int x;
scanf("%d%d", &x, &pri[i]);
if(!vis[x]) {
vis[x] = 1;
col[++kcnt][++col[kcnt][0]] = i;
pos[x] = kcnt;
}
else col[pos[x]][++col[pos[x]][0]] = i;
}
Build(1, 1, n);
int last, ns, ret;
for(int i = 1; i <= kcnt; ++i) {
last = 1;
for(int j = 1; j <= col[i][0]; ++j) {
ret = Query(1, 1, n, col[i][last], col[i][j]);
if(ret > p) continue;
Ans += (long long)(j - last) * (long long)(col[i][0] - j + 1);
last = j;
}
}
printf("%I64d\n", Ans);
return 0;
}
然后你还可以想一下,得到一些简单的方法。
维护前缀和,count[i][j]为前1..i颜色j的数量,记录最后出现ok[i]=1的i。如果一个区间可以由靠左的ok[i]满足,一定可以由区间内靠右的ok[i]满足,不妨让最靠右的ok[i]满足。从1到n枚举区间右端点。通过前缀和计算左端点的方案数。
我觉得用两个数组记比较好。
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
int n,ys,p;
bool ok,f;
int ans=0;
int k[200005],c[200005];
int y[51]={0},s[51]={0},w[51];
int main()
{
memset(w,-1,sizeof(w));
scanf("%d%d%d",&n,&ys,&p);
for(int i=1;i<=n;i++)
scanf("%d%d",&k[i],&c[i]);
for(int i=1;i<=n;i++){
ok=0;
if(c[i]<=p)ok=1;
for(int j=i+1;j<=n;j++){
if(c[j]<=p)ok=1;
if(k[j]==k[i]){
if(ok){
ok=0;w[k[i]]=-1;y[k[i]]++;s[k[i]]+=y[k[i]];}
else{
if(w[k[i]]==-1){w[k[i]]=y[k[i]];}
s[k[i]]+=w[k[i]];
y[k[i]]++;
}
break;}
}
}
for(int i=0;i<ys;i++)
ans+=s[i];
printf("%d",ans);
return 0;
}
3、
这是一道搜索 dfs
不过真的是要打好多啊
判重 剪枝什么的一定要做好
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
using namespace std;
struct node
{
int t[6];
int c[6][8];
node()
{
memset(t,0,sizeof(t));
memset(c,0,sizeof(c));
}
};
int n,ans[6][3];
bool flag,b[6][8],Istrue=false;
node solve(node g)//将标记不用消除的格子依次码好,即完成了清除
{
node h;
for(int i=1;i<=5;i++)
{
for(int j=1;j<=g.t[i];j++)
{
if(g.c[i][j]&&!b[i][j])
{
h.t[i]++;
h.c[i][h.t[i]]=g.c[i][j];
}
}
}
return h;
}
node clear(node g)
{
node h;
flag=false;
memset(b,0,sizeof(b));
for(int i=1;i<=5;i++)//每列
{
for(int j=1;j<=g.t[i];j++)//每列从下到上的棋子
{
if(i<=3)//横向
{
if(g.c[i][j]==g.c[i+1][j]&&g.c[i][j]==g.c[i+2][j])//连续三个相同
{
b[i][j]=b[i+1][j]=b[i+2][j]=true;//标记要消掉的格子
flag=true;
}
}
if(j<=g.t[i]-2)//纵向
{
if(g.c[i][j]==g.c[i][j+1]&&g.c[i][j]==g.c[i][j+2])//同上
{
b[i][j]=b[i][j+1]=b[i][j+2]=true;
flag=true;
}
}
}
}
if(flag)//如果有三个颜色连续的棋子
{
h=solve(g);//清除
return clear(h);//直到不能清除为止
} else return g;
}
void print()
{
for(int i=1;i<=n;i++)
printf("%d %d %d\n",ans[i][0],ans[i][1],ans[i][2]);
Istrue=true;
}
void dfs(node g,int k)
{
int l;
node h;
flag=true;
for(int i=1;i<=5;i++)
{
if(g.t[i])//如果还没有完全消除
{
flag=false;
break;
}
}
if(flag)//如果刚好在第n步完全消除则输出
{
if(k>n) print(); else return;
}
if (Istrue) return ;//如果over
if(k>n) return;//如果超过n步
for(int i=1;i<=5;i++)//对于每列
{
for(int j=1;j<=g.t[i];j++)//每行
{
if(i<5)//前4列,考虑右移的情况,最优化剪枝,右移优于左移
{
if(j>g.t[i+1])//如果该列高于第后一列 ,即可以直接右移
{
h=g;//直接移动
h.t[i+1]++;
h.c[i+1][h.t[i+1]]=h.c[i][j];
h.c[i][j]=0;
h.t[i]--;
for(l=j;l<=h.t[i];l++)//第i列悬空的下落
h.c[i][l]=h.c[i][l+1];
h=clear(h);//清除
ans[k][0]=i-1;//记录所走方法
ans[k][1]=j-1;
ans[k][2]=1;
dfs(h,k+1);//继续走下一步
if (Istrue) return ;
}
else//需要交换
{
h=g;
l=h.c[i][j];//交换两个棋子
h.c[i][j]=h.c[i+1][j];
h.c[i+1][j]=l;
h=clear(h);//清除
ans[k][0]=i-1;//记录方案
ans[k][1]=j-1;
ans[k][2]=1;
dfs(h,k+1);//继续走下一步
if (Istrue) return ;
}
}
if(i>1)//如果是后4列,需要考虑左移的情况
{
if(j>g.t[i-1])//如果该列高于前一列
{
h=g;//直接移动
h.t[i-1]++;
h.c[i-1][h.t[i-1]]=h.c[i][j];
h.c[i][j]=0;
h.t[i]--;
for(l=j;l<=h.t[i];l++)
h.c[i][l]=h.c[i][l+1];
h=clear(h);//清除
ans[k][0]=i-1;//记录方案
ans[k][1]=j-1;
ans[k][2]=-1;
dfs(h,k+1);
if (Istrue) return ;
}
}
}
}
}
int main()
{
int j;
node g;
scanf("%d",&n);
for(int i=1;i<=5;i++)//读入棋盘初始状态
{
scanf("%d",&j);
while(j)
{
g.t[i]++;
g.c[i][g.t[i]]=j;
scanf("%d",&j);
}
}
dfs(g,1);//从第一步开始搜索
if (!Istrue) printf("-1\n");
return 0;
}
DAY 2
1、
数学题对吧
恩那就不多说了 注意时刻取模,还有组合数的递推公式。
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int M = 10007;
int a,b,k,n,m;
int f[1005][1005];
int ans;
int main(){
init();
scanf("%d%d%d%d%d",&a,&b,&k,&n,&m);
a%=M; b%=M;
for(int i=1;i<=k;i++) {f[i][i]=f[i][0]=1;f[i][1]=i;}
for(int i=3;i<=k;i++)
for(int j=2;j<i;j++)
f[i][j]=(f[i-1][j]+f[i-1][j-1])%M;
ans = f[k][m] %M;
for(int i=1 ;i<=n ;i++) ans=(ans*a) % M;
for(int i=1 ;i<=m ;i++) ans=(ans*b) % M;
printf("%d",ans);
}
2、
这道题读完题后应该会想到二分吧。
注意初始的ans要long long 还要附一个很大很大的值。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <cmath>
using namespace std;
#define LL long long
LL ans=1e14;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
LL lmax(LL x,LL y){
if(x>y) return x;
return y;
}
LL lmin(LL x,LL y){
if(x<y) return x;
return y;
}
LL labs(LL x){
if(x>0) return x;
return -x;
}
int mw=-1;
int n,m;
LL S;
int w[200005],v[200005];
int l[200005],r[200005];
LL sum[200005],c[200005];
LL cal(int x){
LL tem = 0;
for(int i=1 ;i<=n ;i++){
sum[i] = sum[i-1];
c[i] = c[i-1];
if(w[i]>=x)
{
sum[i]+=v[i];
c[i]++;
}
}
for(int i=1;i<=m;i++)
{
tem+=(c[r[i]]-c[l[i]-1])*(sum[r[i]]-sum[l[i]-1]);
}
return tem;
}
int main(){
n = read(); m = read(); scanf("%I64d",&S);
LL mw;
for(int i=1 ;i<=n ;i++)
{w[i] =read();
v[i] =read();
mw=lmax(mw,w[i]);
}
for(int i=1 ;i<=m ;i++)
{l[i] =read();
r[i] =read();
}
int l=0;
int r=mw+1;
while(l < r){
int mid=(l+r)>>1;
LL t=cal(mid);
LL cmp =labs(t-S);
ans=lmin(ans,cmp);
if(t<=S)r=mid;
else l=mid+1;
}
printf("%I64d",ans);
return 0;
}
3、
最后一道题
要是没想到的话过掉一点还是可行的。
然后这道题肯定是贪心
发现给某个D[i]减一后会使后面连续的人等车的车站的乘客提前上车。直到一个车站leave[j]>get[j](车等人),人来的时间是不能跟改变的。所以提前了上车时间的旅客提前了的一分钟要在等人上浪费掉,所以旅行时间不会减小。也就是说令right[i]为i后面第一个满足车等人的站点 那么在i+1到right[i]这个区间内下车的人的旅行时间都会减小一。维护下车人数的前缀和S。
对于K
1.k很小全部用掉
2.D[i]很小全部减掉(必须保证每个D[i]>=0)
3.i+1..tright[i]中最小的Get[j]-Leave[ j ] (把这个值减成负的会破坏现有的区间的关系,减成负的就变成车等人了)
#include<stdio.h>
#include<stdlib.h>
#define max(a,b) (a>b ? (a):(b))
long long i,j,k,n,m,l;
long long time[1100],f[1100],g[1100],sum[1100],d[1100];
long long a[11000],b[11000],t[11000],ans;
void init()
{
long long i,j;
scanf("%I64d%I64d%I64d",&n,&m,&k);
for (i=1;i<n;i++)
scanf("%I64d",&d[i]);
for (i=1;i<=m;i++)
{
scanf("%I64d%I64d%I64d",&t[i],&a[i],&b[i]);
sum[b[i]]++;
f[a[i]]=max(f[a[i]],t[i]);
}
return ;
}
void in_time()
{
long long i,j,l;
time[1]=0;
for (i=1;i<=n;i++)
time[i]=max(time[i-1],f[i-1])+d[i-1];
g[n]=n;
g[n-1]=n;
for (i=n-2;i>=1;i--)
if (time[i+1]<=f[i+1])
g[i]=i+1;
else g[i]=g[i+1];
for (i=1;i<=m;i++)
ans+=time[b[i]]-t[i];
for (i=1;i<=n;i++)
sum[i]=sum[i-1]+sum[i];
return ;
}
void work()
{
long long i,j;
long long maxs=0,x=0;
for (i=1;i<n;i++)
if (maxs<sum[g[i]]-sum[i]&&d[i]>0)
maxs=sum[g[i]]-sum[i],
x=i;
long long l=x,r=g[x];
d[x]--;
ans-=maxs;
if (r>n-1)
r=n-1;
for (i=l;i<=r;i++)
time[i]=max(f[i-1],time[i-1])+d[i-1];
for (i=r;i>=l;i--)
if (time[i+1]<=f[i+1])
g[i]=i+1;
else
g[i]=g[i+1];
return ;
}
int main()
{
init();
in_time();
for (i=1;i<=k;i++)
work();
printf("%I64d\n",ans);
return 0;
}