P1955 [NOI2015] 程序自动分析
题目描述
在实现程序自动分析的过程中,常常需要判定一些约束条件是否能被同时满足。
考虑一个约束满足问题的简化版本:假设 x1,x2,x3,⋯ 代表程序中出现的变量,给定 n 个形如 xi=xj 或 xi=xj 的变量相等/不等的约束条件,请判定是否可以分别为每一个变量赋予恰当的值,使得上述所有约束条件同时被满足。例如,一个问题中的约束条件为:x1=x2,x2=x3,x3=x4,x4=x1,这些约束条件显然是不可能同时被满足的,因此这个问题应判定为不可被满足。
现在给出一些约束满足问题,请分别对它们进行判定。
输入格式
输入的第一行包含一个正整数 t,表示需要判定的问题个数。注意这些问题之间是相互独立的。
对于每个问题,包含若干行:
第一行包含一个正整数 n,表示该问题中需要被满足的约束条件个数。接下来 n 行,每行包括三个整数 i,j,e,描述一个相等/不等的约束条件,相邻整数之间用单个空格隔开。若 e=1,则该约束条件为 xi=xj。若 e=0,则该约束条件为 xi=xj。
输出格式
输出包括 t 行。
输出文件的第 k 行输出一个字符串YES或者NO(字母全部大写),YES表示输入中的第 k 个问题判定为可以被满足,NO表示不可被满足。
输入输出样例
输入 #1复制
2 2 1 2 1 1 2 0 2 1 2 1 2 1 1
输出 #1复制
NO YES
输入 #2复制
2 3 1 2 1 2 3 1 3 1 1 4 1 2 1 2 3 1 3 4 1 1 4 0
输出 #2复制
YES NO
说明/提示
【样例解释 1】
在第一个问题中,约束条件为:x1=x2,x1=x2。这两个约束条件互相矛盾,因此不可被同时满足。
在第二个问题中,约束条件为:x1=x2,x1=x2。这两个约束条件是等价的,可以被同时满足。
【样例说明 2】
在第一个问题中,约束条件有三个:x1=x2,x2=x3,x3=x1。只需赋值使得 x1=x2=x3,即可同时满足所有的约束条件。
在第二个问题中,约束条件有四个:x1=x2,x2=x3,x3=x4,x4=x1。由前三个约束条件可以推出 x1=x2=x3=x4,然而最后一个约束条件却要求 x1=x4,因此不可被满足。
【数据范围】
所有测试数据的范围和特点如下表所示:
| 测试点编号 | n 的规模 | i,j 的规模 | 约定 |
|---|---|---|---|
| 1 | 1≤n≤10 | 1≤i,j≤104 | 1≤t≤10e∈{0,1} |
| 2 | |||
| 3 | 1≤n≤100 | ||
| 4 | |||
| 5 | 1≤n≤105 | ||
| 6 | |||
| 7 | |||
| 8 | 1≤n≤105 | 1≤i,j≤109 | |
| 9 | |||
| 10 |
实现代码:
#include <cstdio> #include <iostream> #include <algorithm> #include <cstring> #include <string> #include <cmath> #include <cstdlib> using namespace std; int t,n,f[1000007],book[1000007*3]; struct node{ int x,y,e; }a[1000001]; bool cmp(node a,node b){ return a.e>b.e; } inline void first(int kkk){ for(int i=1;i<=kkk;i++) f[i]=i; } int get(int x){ if(x==f[x]) return x; return f[x]=get(f[x]); } int main(){ scanf("%d",&t); while(t--){ int tot=-1; memset(book,0,sizeof(book)); memset(a,0,sizeof(a)); memset(f,0,sizeof(f)); int flag=1; scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%d %d %d",&a[i].x,&a[i].y,&a[i].e); book[++tot]=a[i].x; book[++tot]=a[i].y; } sort(book,book+tot); int reu=unique(book,book+tot)-book; for(int i=1;i<=n;++i){ a[i].x=lower_bound(book,book+reu,a[i].x)-book; a[i].y=lower_bound(book,book+reu,a[i].y)-book; } first(reu); sort(a+1,a+n+1,cmp); for(int i=1;i<=n;i++){ int r1=get(a[i].x); int r2=get(a[i].y); if(a[i].e){ f[r1]=r2; }else if(r1==r2){ printf("NO\n"); flag=0; break; } } if(flag) printf("YES\n"); } return 0; }P1884 [USACO12FEB] Overplanting S
题目描述
在一个笛卡尔平面坐标系里(则 X 轴向右是正方向,Y 轴向上是正方向),有 N (1≤N≤1000) 个矩形,第 i 个矩形的左上角坐标是 (x1,y1),右下角坐标是 (x2,y2)。问这 N 个矩形所覆盖的面积是多少?
注意:被重复覆盖的区域的面积只算一次。
输入格式
第一行,一个整数 N (1≤N≤1000)。
接下来有 N 行,每行描述一个矩形的信息,分别是矩形的 x1,y1,x2,y2(−108≤x1,y1,x2,y2≤108)。
输出格式
一个整数,被 N 个矩形覆盖的区域的面积。
输入输出样例
输入 #1复制
2 0 5 4 1 2 4 6 2
输出 #1复制
20
实现代码:
#include <iostream> #include <cstring> #include <cmath> #include <algorithm> #define maxn 1010 using namespace std; long long n,x[maxn],y[maxn],x2[maxn],y2[maxn],side[2*maxn]; int arr[maxn]; long long ans; bool cmp(int a,int b) { return y[a]>y[b]; } int main() { cin >> n; for (int i=0;i<n;i++) { cin >> x[i] >> y[i] >> x2[i] >> y2[i]; side[2*i]=x[i]; side[2*i+1]=x2[i]; } sort(side,side+2*n); ans=0; for (int i=1;i<2*n;i++) { if (side[i-1]==side[i]) continue; int m=0; for (int j=0;j<n;j++) { if (x[j]<=side[i-1] && side[i]<=x2[j]) arr[m++]=j; } sort(arr,arr+m,cmp); long long h=y[arr[0]],d=y2[arr[0]],w=side[i]-side[i-1]; for (int j=1;j<m;j++) { int temp=arr[j]; if (y[temp]>d) { ans+=(h-y[temp])*w; h=y[temp]; } else { ans+=(h-d)*w; h=y[temp]; } if (y2[temp]<d) d=y2[temp]; } ans+=(h-d)*w; } cout << ans << endl; }P2004 领地选择
题目描述
作为在虚拟世界里统帅千军万马的领袖,小 Z 认为天时、地利、人和三者是缺一不可的,所以,谨慎地选择首都的位置对于小 Z 来说是非常重要的。
首都被认为是一个占地 C×C 的正方形。小 Z 希望你寻找到一个合适的位置,使得首都所占领的位置的土地价值和最高。
输入格式
第一行三个整数 N,M,C,表示地图的宽和长以及首都的边长。
接下来 N 行每行 M 个整数,表示了地图上每个地块的价值。价值可能为负数。
输出格式
一行两个整数 X,Y,表示首都左上角的坐标。保证最优解是唯一的。
输入输出样例
输入 #1复制
3 4 2 1 2 3 1 -1 9 0 2 2 0 1 1
输出 #1复制
1 2
说明/提示
对于 60% 的数据,N,M≤50。
对于 90% 的数据,N,M≤300。
对于 100% 的数据,1≤N,M≤103,1≤C≤min(N,M),每块地价值的绝对值不超过 32767。
实现代码:
#include<bits/stdc++.h> using namespace std; int n,m,c; const int N=1e3+10; int a[N][N],hang[N][N]; long long sum[N][N]; int main(){ cin>>n>>m>>c; for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ cin>>a[i][j]; hang[i][j]=a[i][j]+hang[i][j-1]; sum[i][j]=hang[i][j]+sum[i-1][j]; } } long long X,Y,maxn=0; for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ if((i+c-1>n)||(j+c-1>m)) continue; long long k=sum[i+c-1][j+c-1]+sum[i-1][j-1]-sum[i-1][j+c-1]-sum[i+c-1][j-1]; if(k>maxn){ maxn=k; X=i; Y=j; } } } cout<<X<<" "<<Y; return 0; }P3017 [USACO11MAR] Brownie Slicing G
题目描述
Bessie 烤了一个长方形的布朗尼,可以看作是一个 R×C 的网格(1≤R≤500;1≤C≤500),由小方块组成。在第 i 行,第 j 列的方块中有 Nij(0≤Nij≤4,000)颗巧克力豆。
Bessie 想把布朗尼分成 A×B 块(1≤A≤R;1≤B≤C):每头牛一块。布朗尼的切割方式是先进行 A−1 次水平切割(总是在整数坐标上),将布朗尼分成 A 条带。然后每条带独立地进行 B−1 次垂直切割,也是在整数边界上。其他 A×B−1 头牛各自选择一块布朗尼,剩下最后一块给 Bessie。由于它们很贪心,它们会把巧克力豆最少的一块留给 Bessie。
假设 Bessie 以最优方式切割布朗尼,求 Bessie 能获得的最多巧克力豆数。
例如,考虑一个 5 行 4 列的布朗尼,巧克力豆分布如下:
1 2 2 1 3 1 1 1 2 0 1 3 1 1 1 1 1 1 1 1Bessie 必须将布朗尼分成 4 条水平带,每条带有两块。Bessie 可以这样切割布朗尼:
1 2 | 2 1 --------- 3 | 1 1 1 --------- 2 0 1 | 3 --------- 1 1 | 1 1 1 1 | 1 1因此,当其他贪心的牛选择它们的布朗尼块时,Bessie 仍然可以得到 3 颗巧克力豆。
输入格式
* 第 1 行:四个用空格分隔的整数:R,C,A 和 B
* 第 2 行到第 R+1 行:第 i+1 行包含 C 个用空格分隔的整数:Ni1,...,NiC
输出格式
* 第 1 行:一个整数:Bessie 在她的布朗尼上能保证获得的最多巧克力豆数
显示翻译
题意翻译
输入输出样例
输入 #1复制
5 4 4 2 1 2 2 1 3 1 1 1 2 0 1 3 1 1 1 1 1 1 1 1
输出 #1复制
3
说明/提示
(由 ChatGPT 4o 翻译)
实现代码:
#include<iostream> #include<cstdio> using namespace std; int r,c,a,b,map[501][501],s[501][501],ans; bool check(int x) { int now=0,num=0; for (int i=1;i<=r;i++) { int dis=0,sum=0; for (int j=1;j<=c;j++) if (dis+(s[i][j]-s[i][j-1])-(s[now][j]-s[now][j-1])<x) dis+=(s[i][j]-s[i][j-1])-(s[now][j]-s[now][j-1]); else { sum++; dis=0; } if (sum>=b) { now=i;num++; } } if (num<a) return 0; return 1; } int main() { cin>>r>>c>>a>>b; for (int i=1;i<=r;i++) for (int j=1;j<=c;j++) cin>>map[i][j]; for (int i=1;i<=r;i++) for (int j=1;j<=c;j++) s[i][j]=s[i-1][j]+s[i][j-1]+map[i][j]-s[i-1][j-1]; int h=0,t=s[r][c]; while (h<=t) { int mid=(h+t)/2; if (check(mid)) { h=mid+1; ans=mid; } else t=mid-1; } cout<<ans; }P3406 海底高铁
题目描述
该铁路经过 N 个城市,每个城市都有一个站。不过,由于各个城市之间不能协调好,于是乘车每经过两个相邻的城市之间(方向不限),必须单独购买这一小段的车票。第 i 段铁路连接了城市 i 和城市 i+1(1≤i<N)。如果搭乘的比较远,需要购买多张车票。第 i 段铁路购买纸质单程票需要 Ai 博艾元。
虽然一些事情没有协调好,各段铁路公司也为了方便乘客,推出了 IC 卡。对于第 i 段铁路,需要花 Ci 博艾元的工本费购买一张 IC 卡,然后乘坐这段铁路一次就只要扣 Bi(Bi<Ai) 元。IC 卡可以提前购买,有钱就可以从网上买得到,而不需要亲自去对应的城市购买。工本费不能退,也不能购买车票。每张卡都可以充值任意数额。对于第 i 段铁路的 IC 卡,无法乘坐别的铁路的车。
Uim 现在需要出差,要去 M 个城市,从城市 P1 出发分别按照 P1,P2,P3,⋯,PM 的顺序访问各个城市,可能会多次访问一个城市,且相邻访问的城市位置不一定相邻,而且不会是同一个城市。
现在他希望知道,出差结束后,至少会花掉多少的钱,包括购买纸质车票、买卡和充值的总费用。
输入格式
第一行两个整数,N,M。
接下来一行,M 个数字,表示 Pi。
接下来 N−1 行,表示第 i 段铁路的 Ai,Bi,Ci。
输出格式
一个整数,表示最少花费。
输入输出样例
输入 #1复制
9 10 3 1 4 1 5 9 2 6 5 3 200 100 50 300 299 100 500 200 500 345 234 123 100 50 100 600 100 1 450 400 80 2 1 10
输出 #1复制
6394
说明/提示
2 到 3 以及 8 到 9 买票,其余买卡。
对于 30% 数据 M=2。
对于另外 30% 数据 N≤1000,M≤1000。
对于 100% 的数据 M,N≤105,Ai,Bi,Ci≤105。
实现代码:
#include<iostream> using namespace std; char ch; template<class T> inline void rd(T& x) { x = 0; int w = 1; ch = getchar(); while (ch < '0' || ch>'9') { if (ch == '-')w = -1; ch = getchar(); } while (ch >= '0' && ch <= '9') { x = (x << 1) + (x << 3) + ch - '0'; ch = getchar(); } x = x * w; } int a[100001]; unsigned long long t ,sum; int main() { int n, m,x,y,z; rd(n), rd(m); rd(x); for (int i = 2; i <= m; i++) { rd(y); if (x < y) { a[x]++; a[y]--; } else { a[x]--; a[y]++; } x = y; } for (int i = 1; i < n; i++) { t += a[i]; rd(x),rd(y),rd(z); sum += t * y + z < t * x ? t * y + z : t * x; } cout << sum; return 0; }P1083 [NOIP 2012 提高组] 借教室
题目描述
在大学期间,经常需要租借教室。大到院系举办活动,小到学习小组自习讨论,都需要向学校申请借教室。教室的大小功能不同,借教室人的身份不同,借教室的手续也不一样。
面对海量租借教室的信息,我们自然希望编程解决这个问题。
我们需要处理接下来 n 天的借教室信息,其中第 i 天学校有 ri 个教室可供租借。共有 m 份订单,每份订单用三个正整数描述,分别为 dj,sj,tj,表示某租借者需要从第 sj 天到第 tj 天租借教室(包括第 sj 天和第 tj 天),每天需要租借 dj 个教室。
我们假定,租借者对教室的大小、地点没有要求。即对于每份订单,我们只需要每天提供 dj 个教室,而它们具体是哪些教室,每天是否是相同的教室则不用考虑。
借教室的原则是先到先得,也就是说我们要按照订单的先后顺序依次为每份订单分配教室。如果在分配的过程中遇到一份订单无法完全满足,则需要停止教室的分配,通知当前申请人修改订单。这里的无法满足指从第 sj 天到第 tj 天中有至少一天剩余的教室数量不足 dj 个。
现在我们需要知道,是否会有订单无法完全满足。如果有,需要通知哪一个申请人修改订单。
输入格式
第一行包含两个正整数 n,m,表示天数和订单的数量。
第二行包含 n 个正整数,其中第 i 个数为 ri,表示第 i 天可用于租借的教室数量。
接下来有 m 行,每行包含三个正整数 dj,sj,tj,表示租借的数量,租借开始、结束分别在第几天。
每行相邻的两个数之间均用一个空格隔开。天数与订单均用从 1 开始的整数编号。
输出格式
如果所有订单均可满足,则输出只有一行,包含一个整数 0。
否则(订单无法完全满足)输出两行,第一行输出一个负整数 −1,第二行输出需要修改订单的申请人编号。
输入输出样例
输入 #1复制
4 3 2 5 4 3 2 1 3 3 2 4 4 2 4
输出 #1复制
-1 2
说明/提示
【输入输出样例说明】
第 1 份订单满足后,4 天剩余的教室数分别为 0,3,2,3。第 2 份订单要求第 2 天到第 4 天每天提供 3 个教室,而第 3 天剩余的教室数为 2,因此无法满足。分配停止,通知第 2 个申请人修改订单。
【数据范围】
对于 10% 的数据,有 1≤n,m≤10;
对于 30% 的数据,有 1≤n,m≤1000;
对于 70% 的数据,有 1≤n,m≤105;
对于 100% 的数据,有 1≤n,m≤106,0≤ri,dj≤109,1≤sj≤tj≤n。
NOIP 2012 提高组 第二天 第二题
2022.2.20 新增一组 hack 数据
实现代码:
#include<iostream> #include<cstring> #include<cstdio> using namespace std; int n,m; int diff[1000011],need[1000011],rest[1000011],r[1000011],l[1000011],d[1000011]; bool isok(int x) { memset(diff,0,sizeof(diff)); for(int i=1;i<=x;i++) { diff[l[i]]+=d[i]; diff[r[i]+1]-=d[i]; } for(int i=1;i<=n;i++) { need[i]=need[i-1]+diff[i]; if(need[i]>rest[i])return 0; } return 1; } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++)scanf("%d",&rest[i]); for(int i=1;i<=m;i++)scanf("%d%d%d",&d[i],&l[i],&r[i]); int begin=1,end=m; if(isok(m)){cout<<"0";return 0;} while(begin<end) { int mid=(begin+end)/2; if(isok(mid))begin=mid+1; else end=mid; } cout<<"-1"<<endl<<begin; }