集训队日常训练20180525-DIV1
A.2805
N*M的图,每次浇水(X1,Y1)-(X2,Y2)围成的矩形,问最后有多少点被浇水了。
暴力。
#include<bits/stdc++.h> using namespace std; bool g[245][245]; int main() { int X,Y,I; cin>>X>>Y>>I; while(I--) { int X1,Y1,X2,Y2; cin>>X1>>Y1>>X2>>Y2; for(int i=X1;i<=X2;i++) for(int j=Y1;j<=Y2;j++) g[i][j]=1; } int ans=0; for(int i=1;i<=X;i++) for(int j=1;j<=Y;j++) ans+=g[i][j]; printf("%d",ans); return 0; }
这个题数据可以出到N,M<=5000,复杂度O(n^2)。做法二维差分约束。
B.2366
公司每月盈利s,亏损d,连续5个月一次报表都为亏损,问一年最多可以盈利多少。
构造题。
如果d>4*s,那么ssssdssssdss,每连续5个月都为亏
如果d>3/2*s,那么sssddsssddss。
如果d>2/3*s,那么ssdddssdddss。
如果d>1/4*s,那么sddddsddddsd。
否则dddddddddddd。
#include<bits/stdc++.h> using namespace std; #define ll __int64 int main() { ll s,d,val; while(cin>>s>>d) { ///+s -d if(d>4*s)val=10*s-2*d; else if(d>1.5*s)val=8*s-4*d; else if(d>2./3*s)val=6*s-6*d; else if(d>0.25*s)val=3*s-9*d; else val=0; if(val>0)cout<<val<<endl; else cout<<"Deficit"<<endl; } return 0; }
C.2282
给若干个关系,a比b重,问那些珠子不可能是中值。
弗洛伊德Floyd,g[i][j]=1代表i比j重,f[i][j]=1代表i比j轻。
由于数据的问题,会出现环路比如1比2重,2比3重,3比1重。
#include<bits/stdc++.h> using namespace std; int main() { int t; cin>>t; while(t--) { int n,m; cin>>n>>m; int g[105][105]={0},f[105][105]={0}; for(int i=1;i<=m;i++) { int u,v; cin>>u>>v; g[u][v]=1; f[v][u]=1; } for(int k=1;k<=n;k++) for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) g[i][j]|=g[i][k]&g[k][j], f[i][j]|=f[i][k]&f[k][j]; int ans=0; for(int i=1;i<=n;i++) { int suml=0,sumr=0; for(int j=1;j<=n;j++) { if(i==j)continue; if(g[i][j])suml++; if(f[i][j])sumr++; } if(suml>=(n+1)/2||sumr>=(n+1)/2)ans++; } cout<<ans<<endl; } return 0; }
D.4427
N个孩子,M种颜色,a[i]代表i颜色的大理石数目,小孩只喜欢相同颜色的大理石。小孩的嫉妒值为给一个孩子的最大大理石数量。如何分,嫉妒值最小,输出最小的嫉妒值。
二分嫉妒值,看人数是否能达到n。
#include <bits/stdc++.h> using namespace std; int a[300005],n,m; bool check(int x) { int ans=0; for(int i=0;i<m;i++) ans+=(a[i]+x-1)/x; return ans>n; } int main() { scanf("%d%d",&n,&m); for(int i=0;i<m;i++) scanf("%d",&a[i]); int l=1,r=1e9,ans=-1; while(l<=r) { int mid=(l+r)>>1; if(!check(mid)) ans=mid,r=mid-1; else l=mid+1; } printf("%d\n",ans); return 0; }
E.3573
N个项目D[i]代表D[i]分钟完成,V[i]代表价值,每分钟只能做一个项目,问M分钟内做出的最大价值。
贪心+优先队列。
首先按D排序从小到大,D相同按V从大到小排序。
如果a[i].d>qu.size()&&qu.size()<m说明有空可以放,直接放入。
否则如果a[i].v>优先队列最小的v,就换掉。
最后统计优先队列里所有v的和。
#include<bits/stdc++.h> using namespace std; struct T { int d,v; bool operator<(const T &t)const{ return d<t.d||(d==t.d&&v>t.v); } }a[100005]; priority_queue<int,vector<int>,greater<int> >qu; int main() { int n,m; while(~scanf("%d%d",&n,&m)) { for(int i=0;i<n;i++) scanf("%d%d",&a[i].d,&a[i].v); sort(a,a+n); for(int i=0;i<n;i++) { if(a[i].d>qu.size()&&qu.size()<m) qu.push(a[i].v); else { if(!qu.empty()&&a[i].v>qu.top()) qu.pop(),qu.push(a[i].v); } } int ans=0; while(!qu.empty()) ans+=qu.top(),qu.pop(); printf("%d\n",ans); } return 0; }
F.2283
N个数,每次取连续K个,取不相交的三段,问最大和。
dp[i][j]代表当前点i取了j次的最大和。
需要用前缀和优化dp(快速算出[i,j]的和)。
dp[i][j]=max(max(dp[i][j],dp[i-1][j]),dp[i-k][j-1]+pre[i]-pre[i-k]);
#include <bits/stdc++.h> using namespace std; const int N=50005; int dp[N][4],pre[N]; int main() { int T,n,k; scanf("%d",&T); while(T--) { memset(dp,0,sizeof dp); scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&pre[i]),pre[i]+=pre[i-1]; scanf("%d",&k); for(int i=k;i<=n;i++) for(int j=0;j<=3;j++) { if(j) dp[i][j]=max(dp[i][j],dp[i-k][j-1]+pre[i]-pre[i-k]); dp[i][j]=max(dp[i][j],dp[i-1][j]); } printf("%d\n",dp[n][3]); } return 0; }
G.2586
N盏灯,初始都为灭。对于0操作,[L,R]取反。对于1操作,输出[L,R]亮灯的数量。
线段树区间取反+区间求和。
#include<bits/stdc++.h> using namespace std; const int N=1e5+5; int cnt[N<<2]; int lazy[N<<2]; void pushdown(int l,int r,int rt) { if(!lazy[rt])return; lazy[rt<<1]^=1; lazy[rt<<1|1]^=1; int mid=(l+r)>>1; cnt[rt<<1]=mid-l+1-cnt[rt<<1]; cnt[rt<<1|1]=r-mid-cnt[rt<<1|1]; lazy[rt]=0; } void update(int L,int R,int l,int r,int rt) { if(L<=l&&r<=R) { lazy[rt]^=1; cnt[rt]=r-l+1-cnt[rt]; return; } int mid=(l+r)>>1; pushdown(l,r,rt); if(L<=mid)update(L,R,l,mid,rt<<1); if(R>mid)update(L,R,mid+1,r,rt<<1|1); cnt[rt]=cnt[rt<<1]+cnt[rt<<1|1]; } int query(int L,int R,int l,int r,int rt) { if(L<=l&&r<=R)return cnt[rt]; int mid=(l+r)>>1,ans=0; pushdown(l,r,rt); if(L<=mid)ans+=query(L,R,l,mid,rt<<1); if(R>mid)ans+=query(L,R,mid+1,r,rt<<1|1); cnt[rt]=cnt[rt<<1]+cnt[rt<<1|1]; return ans; } int main() { int n,m,op,l,r; scanf("%d%d",&n,&m); while(m--) { scanf("%d%d%d",&op,&l,&r); if(op==0)update(l,r,1,n,1); else printf("%d\n",query(l,r,1,n,1)); } return 0; }
H.2593
N个二进制数,Q个查询。对于每个查询s,输出满足条件前min(len[i],s)个数都相同的数的个数(i是q的前缀或者q是i的前缀)。
由于读入过大,建议使用快读。
字典树,sum[rt]表示字典树节点编号rt是多少个串的结尾,ss[rt]表示字典树节点编号rt是多少个串的非结尾。
每次查询,如果查询串未到结尾就是ans+=ss[rt],如果结尾就是ans+=ss[rt]+sum[rt]。
#include<bits/stdc++.h> using namespace std; #define ll long long #define maxn 500005 int Scan() { int res = 0, ch, flag = 0; if((ch = getchar()) == '-') flag = 1; else if(ch >= '0' && ch <= '9') res = ch - '0'; while((ch = getchar()) >= '0' && ch <= '9' ) res = res * 10 + ch - '0'; return flag ? -res : res; } int tot=1; int trie[maxn][2],sum[maxn],ss[maxn]; bool isw[maxn]; //查询整个单词用 int num; void insert() { int k; scanf("%d",&k); int rt=1; for(int i=1;i<=k;i++) { int x=Scan(); if(trie[rt][x]==0) trie[rt][x]=++tot; int g=trie[rt][x]; if(i!=k) sum[g]++; else ss[g]++; rt=trie[rt][x];//为下个字母的插入做准备 } } void find() { int k; scanf("%d",&k); int rt=1; int g=1; for(int i=1;i<=k;i++) { int x=Scan(); if(trie[rt][x]==0||g==0) { g=0; continue; } int tot=trie[rt][x]; if(i!=k) num+=ss[tot]; else num+=sum[tot]+ss[tot]; rt=trie[rt][x];//为查询下个字母做准备 } //查询整个单词时,应该return isw[rt] }//查找 int main() { int n,m,k; scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) insert(); for(int i=1;i<=m;i++) { num=0; find(); printf("%d\n",num); } }
I.2616
N个数取任意个满足和是F的倍数。
背包dp,dp[i][j]表示选到前i件物品,%m的余数为j的方案数。
初始条件dp[i][x]=1。
转移:dp[i][j]+=dp[i-1][j]+dp[i-1][((j-x)%m+m)%m];
#include <bits/stdc++.h> using namespace std; const int MD=100000000; int dp[2005][1005]; int main() { int n,m; scanf("%d%d",&n,&m); dp[0][0]=1; for(int i=1,x;i<=n;i++) { scanf("%d",&x); for(int j=0;j<m;j++) dp[i][j]=dp[i-1][j]; for(int j=0;j<m;j++) dp[i][j]=(dp[i][j]+dp[i-1][((j-x)%m+m)%m])%MD; } printf("%d\n",(dp[n][0]-1+MD)%MD); return 0; }
J.3521
输出所有N位数且每次移除最后一个数也都是质数的数。例如7331:7331,733,73,7都是质数。
深搜。
#include<stdio.h> int n; int p(int a) { if(a==1)return 0; for(int i=2;i*i<=a;i++) if(a%i==0) return 0; return 1; } void dfs(int s,int m) { for(int i=1;i<=9;i++) { if(p(m*10+i)) { if(n==s)printf("%d\n",m*10+i); dfs(s+1,m*10+i); } } } int main() { scanf("%d",&n); dfs(1,0); return 0; }
K.2906
N*M的01矩阵,求最大全1子矩阵。
这里读入数据较大,建议使用快读。
王知昆《浅谈用极大化思想解决最大子矩阵问题》
https://www.cnblogs.com/Konjakmoyu/p/5787633.html
#include<cstdio> #define N 2005 int n,m,i,j,ans,l[N],r[N],h[N],lmax,rmax,a[N][N]; int main(){ while(~scanf("%d%d",&n,&m)){ for(ans=0,i=1;i<=n;i++)for(j=1;j<=m;j++)scanf("%d",&a[i][j]); for(i=1;i<=m;i++)h[i]=0,l[i]=1,r[i]=m; for(i=1;i<=n;i++){ for(lmax=j=1;j<=m;j++)if(a[i][j]){ h[j]++; if(lmax>l[j])l[j]=lmax; }else h[j]=0,l[j]=1,r[j]=m,lmax=j+1; for(rmax=j=m;j;j--)if(a[i][j]){ if(rmax<r[j])r[j]=rmax; if((r[j]-l[j]+1)*h[j]>ans)ans=(r[j]-l[j]+1)*h[j]; }else rmax=j-1; } printf("%d\n",ans); } }