水题略过(然而被Hack了 以后要更加谨慎)
#include<bits/stdc++.h> using namespace std; int main() { //freopen("t.txt","r",stdin); int a[4][4]; //memset(a,0,sizeof(a)); for(int i=0;i<4;i++) for(int j=0;j<4;j++) scanf("%d",&a[i][j]); bool flag=false; for(int i=0;i<4;i++) { if(!a[i][3])continue; if(a[i][0]||a[i][1]||a[i][2]||a[(i+1)%4][0]||a[(i+2)%4][1]||a[(i+3)%4][2]){flag=true;break;} } if(flag)printf("YES\n"); else printf("NO\n"); return 0; }
很简单的DP 由于不关掉本层的灯不能上楼,问题就好办了。
dp[i][0]表示从顶层到第i层全关完且从左边出发的minicost dp[i][1]表示从右边出发的
转移方程是显而易见的
#include<bits/stdc++.h> using namespace std; typedef long long int LL; int bulb[16][110],le[16],ri[16]; LL dp[16][2]; int main() { //freopen("t.txt","r",stdin); int n,m; scanf("%d%d",&n,&m); for(int i=0;i<n;i++) { le[i]=ri[i]=-1; getchar(); for(int j=0;j<m+2;j++) { char c=getchar(); if(c=='0')bulb[i][j]=0; else bulb[i][j]=1; //scanf("%d",&bulb[i][j]); if(!bulb[i][j])continue; if(le[i]==-1)le[i]=j; else le[i]=min(le[i],j); if(ri[i]==-1)ri[i]=j; else ri[i]=max(ri[i],j); } } dp[0][0]=max(0,ri[0]); if(le[0]==-1)dp[0][1]=0; else dp[0][1]=(m+2)-le[0]-1; for(int i=1;i<n;i++) { if(dp[i-1][0]==0&&dp[i-1][1]==0) dp[i][0]=max(0,ri[i]); else dp[i][0]=max(0,ri[i])+min(max(0,ri[i])+1+dp[i-1][0],m+2-max(0,ri[i])+dp[i-1][1]); int lx; if(le[i]==-1)lx=0; else lx=(m+2)-le[i]-1; if(dp[i-1][0]==0&&dp[i-1][1]==0) dp[i][1]=lx; else dp[i][1]= lx+min(m+2-lx+dp[i-1][0],lx+1+dp[i-1][1]); } printf("%I64d\n",dp[n-1][0]); return 0; }
二分答案判断是否可行即可。复杂度O(nlognlogn)
#include<bits/stdc++.h> using namespace std; typedef long long int LL; LL mid; LL n,s; struct suv { LL v; LL x; }; vector<suv>num; bool cmp(suv a,suv b) { return a.v+a.x*mid<b.v+b.x*mid; } LL doa() { sort(num.begin(),num.end(),cmp); LL sum=0; for(int i=0;i<mid;i++) { sum+=num[i].v+num[i].x*mid; } if(sum>s)return -1; else return sum; } int main() { //freopen("t.txt","r",stdin); num.clear(); scanf("%I64d%I64d",&n,&s); num.resize(n); LL min=1e+15; int minj=-1; for(int i=0;i<n;i++) { scanf("%I64d",&num[i].v); num[i].x=i+1; } LL l=0,r=n; LL sm; LL bestm=0,bestk=0; while(l<=r) { mid=(l+r)>>1; sm=doa(); if(sm==-1)r=mid-1; else l=mid+1,bestm=mid,bestk=sm; } printf("%I64d %I64d\n",bestm,bestk); return 0; }
这道题 可以说又臭又长。。不知道是不是埃及洋文好的人不多。
总结一下题意: 给定一个特殊的DAG 怎么特殊呢? 不会存在一个点指向多个点。
每次询问添加一条边,询问有多少个点进入了特殊集合 满足这个集合中所有点都有指向的点,且他们指向的点也均在这个集合中。
反向dfs一下就可以了。。
#include <bits/stdc++.h> using namespace std; #define ll int #define N ((ll)101*1000) ll n,m,k,q,a[N],b[N],lst[N],strt[N],fnsh[N],sz[N],now; vector <ll> e[N]; bool dad[N]; void dfs(ll x) { sz[x]=1,strt[x]=now++; for(auto u:e[x]) dfs(u),sz[x]+=sz[u]; fnsh[x]=now; } int main() { ios_base::sync_with_stdio(0);cin.tie(0); cin>>n>>m>>k>>q; for(int i=0;i<k;i++) { ll x,y; cin>>x>>y; if(lst[y])e[lst[y]].push_back(x),dad[x]=1; lst[y]=x; } for(int i=1;i<=n;i++) if(!dad[i]) dfs(i); while(q--) { ll x,y; cin>>x>>y; if(!lst[y]){cout<<"0\n";continue;} if(strt[x]<strt[lst[y]] && strt[lst[y]]<fnsh[x])cout<<sz[x]<<"\n"; else cout<<"0\n"; } return 0; }
很有趣的一道Nim游戏题
题目给的树的性质该怎么利用呢?
所有叶子节点到根节点的距离要么都是偶数要么都是奇数。
我们是不是可以考虑把所有节点分成两种类型,一种是到根节点的距离的奇偶性与叶节点相同(集合P),一种不同(集合Q)。
如果对手移动P中节点p,如果P中还有一个节点的苹果数量和p相同,我就可以模仿对手。
如果对手移动Q中节点,我也可以模仿(吃掉或者继续往下走)。
所以需要考虑的就是P中节点的异或值x。如果为0,最初就是必胜态。
不为0该怎么办呢? 考虑在Q中找一个恰当的节点替换当前节点。
需要的苹果数为A【i】^x 为什么呢?(学好近世代数很重要)
看代码吧 很简单。
#include<bits/stdc++.h> using namespace std; struct node { int y,next; }a[100010]; int first[100010],len; int A[100010]; int d[100010],t; int s[21000000]; void ins(int x,int y) { a[++len].y=y; a[len].next=first[x]; first[x]=len; } void dfs(int x,int fa) { int k,y,ok=1; d[x]=d[fa]^1; for(k=first[x];k;k=a[k].next) { y=a[k].y; ok=0; dfs(y,x); } if(ok)t=d[x];//判断奇偶 } int main() { int n,i,j,x,sum=0; long long ans=0; scanf("%d",&n); for(i=1;i<=n;i++) scanf("%d",&A[i]); for(i=2;i<=n;i++) { scanf("%d",&x); ins(x,i); } dfs(1,0); x=0; for(i=1;i<=n;i++) if(d[i]==t)x^=A[i],sum++; else s[A[i]]++; if(!x)ans+=(long long)sum*(sum-1)/2+(long long)(n-sum)*(n-sum-1)/2;//最初就是必胜态 for(i=1;i<=n;i++) if(d[i]==t)ans+=s[A[i]^x]; printf("%I64d\n",ans); return 0; }