2019 Multi-University Training Contest 9
1002 Rikka with Cake
题目链接
题意:给你一个n*m的蛋糕,在蛋糕上有许多点,然后根据点和方向切,求最后能切多少块蛋糕。
思路:将每个点排序,离散化x轴建线段树。我们把竖着切和横着切看作两种操作。竖着切就是对于此下标,单点更新,横着切可以看作下标到两端的某一段的区间求和。向上切和向下切两个方向分开讨论,跑两遍线段树。最后答案加上1,即原有的那个蛋糕。
正解:通过几何学中的欧拉公式
其中V代表顶点个数,E代表边数,F代表块数。计算可得最后答案就是交点数C+1。用线段树或树状数组求交点。
#include <bits/stdc++.h>
#define maxn 100005
typedef long long ll;
using namespace std;
int n,m,k;
// f 1 2 3 4 上 右 下 左
struct line{
int x,y,f;
}li[maxn];
int xCopy[maxn];
int size;
void Init_Hash_x(){
for(int i=0;i<k;i++)
xCopy[i]=li[i].x;
sort(xCopy,xCopy+k);
size=unique(xCopy,xCopy+k)-xCopy;
}
int Hash(int x){
return lower_bound(xCopy,xCopy+size,x)-xCopy+1;
}
bool cmp1(line a,line b){
return a.y<b.y;
}
bool cmp2(line a,line b){
return a.y>b.y;
}
void input(){
scanf("%d%d%d",&n,&m,&k);
int i;
char s[3];
for(i=0;i<k;i++){
scanf("%d%d%s",&li[i].x,&li[i].y,s);
if(s[0]=='U')
li[i].f=1;
else if(s[0]=='R')
li[i].f=2;
else if(s[0]=='D')
li[i].f=3;
else if(s[0]=='L')
li[i].f=4;
}
Init_Hash_x();
sort(li,li+k,cmp1);
}
struct LR_sum_SEG {
ll val[maxn << 2];
void pushup(int p) {
val[p] = val[p << 1 | 1]+val[p << 1];
}
void build(int p, int l, int r) {
if (l == r) {
val[p] = 0;
return;
}
int mid = l + r >> 1;
build(p << 1, l, mid);
build(p << 1 | 1, mid + 1, r);
pushup(p);
}
void update(int p, int l, int r, int index, int x) {
if(l==r){
val[p]+=x;
return;
}
int mid = l + r >> 1;
if(index<=mid) update(p<<1,l,mid,index,x);
else update(p<<1|1,mid+1,r,index,x);
pushup(p);
}
int query(int p, int l, int r, int L,int R) {
if(L<=l&&r<=R){
return val[p];
}
int mid = l + r >> 1;
int ans=0;
if(L<=mid) ans+=query(p<<1,l,mid,L,R);
if(R>mid) ans+=query(p<<1|1,mid+1,r,L,R);
return ans;
}
}SEG;
void solve(){
ll ans=1;
int i;
SEG.build(1,1,100002);
for(i=0;i<k;i++){
if(li[i].f==1){
SEG.update(1,1,100002,Hash(li[i].x)+1,1);
}else if(li[i].f==2){
ans+=SEG.query(1,1,100002,Hash(li[i].x)+1,100002);
}else if(li[i].f==4){
ans+=SEG.query(1,1,100002,1,Hash(li[i].x)+1);
}
}
sort(li,li+k,cmp2);
SEG.build(1,1,100002);
for(i=0;i<k;i++){
if(li[i].f==3){
SEG.update(1,1,100002,Hash(li[i].x)+1,1);
}else if(li[i].f==2){
ans+=SEG.query(1,1,100002,Hash(li[i].x)+1,100002);
}else if(li[i].f==4){
ans+=SEG.query(1,1,100002,1,Hash(li[i].x)+1);
}
}
printf("%lld\n",ans);
}
int main(){
int t;
scanf("%d",&t);
while(t--){
input();
solve();
}
} 1005 Rikka with Game
题目链接
题意:一个全是小写字母的字符串,A、B两个人都可以轮流选择其中一个字母,使其+1。(即a->b,b->c....y->z,z->a)或者可以立刻结束游戏。先手的人希望将字符串字典序小,后手的人希望字典序尽量大。求最后的答案。
思路:1、当第一个字母是a-x中其中一个时,先手会直接结束游戏。若先手选择第一个字母+1,后手可以选择继续增加或者终止游戏,字典序一定会增加。
2、当第一个字母为y时,先手、后手都不会动这一位。若先手增加,后手可以终止在z上,字典序增加。若后手增加,先手可以继续增加,使其变为a。增加y都是亏的。
3、当第一个字母为z时,先手必然增加为a。回到第一种情况
结论就是,找到第一位不是y的位,如果它是z,则修改成b,否则不变
#include <bits/stdc++.h>
using namespace std;
string s;
void input(){
cin>>s;
}
void solve(){
for(int i=0;i<s.size();i++){
if(s[i]=='y')
continue;
else if(s[i]=='z')
{
s[i]='b';
break;
}else
break;
}
cout<<s<<endl;
}
int main(){
int t;
scanf("%d",&t);
while(t--){
input();
solve();
}
} 1006 Rikka with Coin
题目链接
题意:有无数个10、20、50、100的硬币,有n个物品其价格为wi,求找到最少的硬币数,满足能凑整所有的wi,若不能输出-1
思路:对于10的硬币,使用的次数在[0,1]之间,当我们选择两个10硬币,可以选择一个10、一个20的硬币更优。
对于20的硬币,使用次数在[0,3]之间,当我们选择四个20硬币,可以选择一个10、一个20、一个50的硬币更优。
对于50的硬币,使用次数在[0,1]之间,当我们选择两个50硬币,可以选择一个50、一个100的硬币更优。
故我们可以枚举这些硬币出现次数,然后多余的用100补。取多种解法中最小答案。
#include <bits/stdc++.h>
#define maxn 105
#define inf 0x3f3f3f3f
using namespace std;
int n;
int w[maxn];
int l;
void input(){
scanf("%d",&n);
for(int i=0;i<n;i++)
scanf("%d",&w[i]);
}
bool check(int x,int y,int z){
vector <int>v;
int i,j,k;
for(i=0;i<=x;i++)
for(j=0;j<=y;j++)
for(k=0;k<=z;k++)
v.push_back(10*i+j*20+k*50);
l=0;
for(i=0;i<n;i++){
int flag=1;
int temp=inf;
for(j=0;j<v.size();j++){
if(w[i]-v[j]>=0&&(w[i]-v[j])%100==0)
temp=min(temp,(w[i]-v[j])/100);
}
if(temp==inf)
return 0;
// cout<<l<<" "<<temp<<endl;
l=max(l,temp);
}
return 1;
}
void solve(){
int i,j,k;
int ans=inf;
for(i=0;i<2;i++){
for(j=0;j<4;j++){
for(k=0;k<2;k++){
if(check(i,j,k)){
// cout<<i<<' '<<j<<' '<<k<<' '<<l<<endl;
ans=min(ans,i+j+k+l);
}
}
}
}
if(ans==inf)
printf("-1\n");
else
printf("%d\n",ans);
}
int main(){
int t;
scanf("%d",&t);
while(t--){
input();
solve();
}
}
/*
1
4
110 160 190 410
*/ 1007 Rikka with Travels
题目链接
题意:给你一个n个节点的树,在上面寻找到两条互不重叠的路径。要求以两条路径长度作为值,寻找至多的二元组(L1,L2)数量。
思路:首先我们在树上找到最长的两条互不重叠的路径,其小于它们的路径也包含在了其中。可以证明要寻找最长的两条路径,直径的两个端点一定包含在这些路径中。
求出原树的直径。将直径上的边都拆去。再跑直径上所有点的DFS,可知以直径上点为端点的最长扩展子路径len[i]。
当两个端点分别包含在两个路径中时,O(n)枚举一个端点到直径上某个点作为扩展路径点,即dis[i]+len[i]。另一个端点到这个点之间的最长路径可以记录max扩展maxx[i]和dis[i]得到。
当两个端点包含在同一个路径中时,可以将所有和直径点相连的端点再拆去。在所有子图上都跑DFS找最长直径。
得到的两两一组的最大路径长度后,O(n)更新。链式前向星建图。
#include <bits/stdc++.h>
#define maxn 100005
typedef long long ll;
using namespace std;
int n,m;
struct edge{
int to,next;
int book;
}G[maxn<<1];
int head[maxn];
int edgeNum=0;
int S,T,now;
int Fa[maxn];
int pre[maxn];
int book[maxn];
int dis[maxn];
int vis[maxn];
int len[maxn];
int val[maxn];
int maxx[maxn];
void add_edge(int a,int b)//添加单向边 类似链表存储 head[i]代表链表头 例如n=1e6 m<=2*n时不能通过邻接表存储
{
G[edgeNum].to=b;
G[edgeNum].next=head[a];
head[a]=edgeNum++;
}
void dfs(int fa,int x,int step){
int i;
if(step>now){
now=step;
S=x;
}
for(i=head[x];i!=-1;i=G[i].next)
{
if(G[i].to==fa||G[i].book)continue;
else dfs(x,G[i].to,step+1);
}
}
void dfs2(int fa,int x,int step){
int i;
Fa[x]=fa;
dis[x]=step;
if(step>now){
now=step;
T=x;
}
for(i=head[x];i!=-1;i=G[i].next)
{
if(G[i].to==fa||G[i].book)continue;
else dfs2(x,G[i].to,step+1);
}
}
void dfs3(int fa,int x,int step){
int i;
vis[x]=1;
if(step>now)
now=step;
for(i=head[x];i!=-1;i=G[i].next)
{
if(G[i].to==fa||G[i].book||vis[G[i].to])continue;
else dfs3(x,G[i].to,step+1);
}
}
void input(){
scanf("%d",&n);
edgeNum=0;
memset(vis,0,sizeof vis);
for(int i=0;i<maxn<<1;i++)
G[i].book=0;
for(int i=0;i<maxn;i++){
book[i]=0;
val[i]=0;
head[i]=-1;
}
int u,v;
for(int i=1;i<n;i++)
{
scanf("%d%d",&u,&v);
add_edge(u,v);
add_edge(v,u);
}
}
void solve(){
now=-1;
dfs(-1,1,0);
now=-1;
dfs2(-1,S,0);
int i,j;
for(i=T;i!=-1;i=Fa[i]){
book[i]=1;
vis[i]=1;
}
for(i=T;i!=-1;i=Fa[i])
for(j=head[i];j!=-1;j=G[j].next)
if(book[G[j].to])
G[j].book=1;
int original=S;
int lst;
for(i=T;i!=-1;i=Fa[i]){
now=-1;
dfs(-1,i,0);
len[i]=now;
if(i==T){
pre[i]=-1;
lst=i;
}else{
pre[i]=lst;
lst=i;
}
// cout<<"len:"<<i<<" "<<len[i]<<endl;
}
for(i=S;i!=-1;i=pre[i]){
if(i==S){
maxx[i]=len[i];
}else{
maxx[i]=max(maxx[Fa[i]],dis[i]+len[i]+1);
}
}
S=original;
// cout<<"T:"<<T<<' '<<"S:"<<S<<endl;
int temp;
for(i=T;i!=-1;i=Fa[i]){
// cout<<"!"<<i<<endl;
if(i==T){
temp=i;
}else{
int len1=dis[T]-dis[i]+len[i]+1;
int len2=maxx[Fa[i]];
// cout<<"!"<<len1<<" "<<len2<<endl;
val[len1]=max(val[len1],len2);
val[len2]=max(val[len2],len1);
}
}
for(i=T;i!=-1;i=Fa[i])
for(j=head[i];j!=-1;j=G[j].next)
G[j].book=1;
for(i=0;i<edgeNum;i++)
if(book[G[i].to])
G[i].book=1;
int L=0;
int final=T;
for(i=1;i<=n;i++)
{
if(!vis[i]){
// cout<<i<<endl;
now=-1;
dfs(-1,i,0);
now=-1;
dfs3(-1,S,0);
L=max(L,now);
}
}
dis[final]++;
if(n!=dis[final])
L++;
// cout<<"!"<<L<<' '<<dis[final]<<endl;
val[dis[final]]=max(val[dis[final]],L);
val[L]=max(val[L],dis[final]);
ll sum=0;
for(int i=n;i>=1;i--){
if(val[i]<val[i+1])
val[i]=val[i+1];
sum+=val[i];
}
printf("%lld\n",sum);
}
int main(){
// freopen("in","r",stdin);
// freopen("out","w",stdout);
int t;
scanf("%d",&t);
while(t--){
input();
solve();
}
} 1008 Rikka with Stable Marriage
题目链接
题意:给你a数组和b数组要求其两两异或,得到最后和最大。
思路:杭电多校第五场,有道类似的题,求最后数值字典序最小。那道正解用栈和字典树维护,可以用两个指针在两个字典树上跑,贪心求最后答案。这题类似。建立两个字典树,用两个指针在字典树上跑,记录每个结点出现次数。贪心思路,若两个指针能够不同就先不同,否则就相同。
#include <bits/stdc++.h>
#define maxn 100005
typedef long long ll;
using namespace std;
int n;
int tol[2]={1,1};
int t[2][31*maxn][2];
int val[2][31*maxn];
int ans[maxn];
int a[maxn];
int b[maxn];
void insert(int x,int flag){
int u=0;
for(int i=30;i>=0;i--){
int v=(x>>i)&1;
if(!t[flag][u][v]){
t[flag][u][v]=tol[flag]++;
}
u=t[flag][u][v];
val[flag][u]++;
}
}
int query(){
int p0=0;
int p1=0;
int x=0;
for(int i=30;i>=0;i--){
if(val[0][t[0][p0][0]]&&val[1][t[1][p1][1]]){
p0=t[0][p0][0];
val[0][p0]--;
p1=t[1][p1][1];
val[1][p1]--;
x+=(1<<i);
}else if(val[0][t[0][p0][1]]&&val[1][t[1][p1][0]]){
p0=t[0][p0][1];
val[0][p0]--;
p1=t[1][p1][0];
val[1][p1]--;
x+=(1<<i);
}else if(val[0][t[0][p0][0]]&&val[1][t[1][p1][0]]){
p0=t[0][p0][0];
val[0][p0]--;
p1=t[1][p1][0];
val[1][p1]--;
}else{
p0=t[0][p0][1];
val[0][p0]--;
p1=t[1][p1][1];
val[1][p1]--;
}
}
return x;
}
void input(){
scanf("%d",&n);
int i;
for(i=1;i<=n;i++){
scanf("%d",&a[i]);
insert(a[i],0);
}
for(i=1;i<=n;i++){
scanf("%d",&b[i]);
insert(b[i],1);
}
}
void solve(){
int i;
ll sum=0;
for(i=1;i<=n;i++){
ans[i]=query();
// cout<<ans[i]<<endl;
sum+=ans[i];
}
printf("%lld\n",sum);
}
int main(){
int T;
scanf("%d",&T);
while(T--){
tol[0]=tol[1]=1;
memset(val,0,sizeof val);
memset(t,0,sizeof t);
input();
solve();
}
}
京公网安备 11010502036488号