全國信息學奧林匹克聯賽()複賽
模擬題
長樂一中
題目名稱 | 正確答案 | 序列問題 | 長途旅行 |
---|---|---|---|
英文名稱 | answer | sequence | travel |
輸入文件名 | answer.in | sequence.in | travel.in |
輸出文件名 | answer.out | sequence.out | travel.out |
時間限制 | 1s | 1s | 1s |
空間限制 | 256M | 256M | 256M |
測試點數目 | 20 | 20 | 10 |
測試點分值 | 5 | 5 | 10 |
是否有部分分 | 無 | 無 | 無 |
題目類型 | 傳統 | 傳統 | 傳統 |
是否有 SPJ | 無 | 無 | 無 |
1.正確答案
【題目描述】
小 與小 剛剛參加完 外卡組的初賽,就迫不及待的跑出考場對答案。
“吔,我的答案和你都不一樣!”,小 說道,”我們去找神犇們問答案吧”。
外卡組試卷中共有 道判斷題,小 與小 一共從其他 個神犇那問了答案。之後又從小 那裏得知,這 個神犇中有 個考了滿分, 個考了零分,其他神犇不爲滿分或零分。
這可讓小 與小 犯了難。你能幫助他們還原出標準答案嗎?如有多解則輸出字典序最小的那個。無解輸出。
【輸入格式】
第一行四個整數 , , , ,意義如上描述。
接下來 行,每一行 個字符或,表示這題這個神犇的答案。
【輸出格式】
僅一行,一個長度爲 的字符串或是。
【樣例輸入】
2 2 2 0
YY
YY
【樣例輸出】
YY
【數據範圍】
% :
% :
% :
2.序列問題
【題目描述】
小 是個善於思考的學生,她正在思考一個有關序列的問題。
她的面前浮現出了一個長度爲 的序列,她想找出兩個非空的集合 。
這兩個集合要滿足以下的條件:
-
兩個集合中的元素都爲整數,且都在 裏,即 。
-
對於集合 中任意一個元素 ,集合 中任意一個元素 ,滿足 。
-
對於大小分別爲 , 的集合 與 ,滿足
小 想知道一共有多少對這樣的集合,你能幫助她嗎?
【輸入格式】
第一行,一個整數
第二行, 個整數,代表 。
【輸出格式】
僅一行,表示最後的答案。
【樣例輸入】
4
1 2 3 3
【樣例輸出】
4
【樣例解釋】
S = {1,2}, T = {3}, 1 ^ 2 = 3 = 3 (^爲異或)
S = {1,2}, T = {4}, 1 ^ 2 = 3 = 3
S = {1,2}, T = {3,4} ,1 ^ 2 = 3 & 3 = 3 (&爲與運算)
S = {3}, T = {4} 3 = 3 = 3
【數據範圍】
%:
%:
%:
3.長途旅行
【題目描述】
是一個愛旅遊的探險家,也是一名強迫症患者。現在 想要在 國進行一次長途旅行, 國擁有 個城市(編號爲 ),城市之間有 條道路,可能某個城市到自己有一條道路,也有可能兩個城市之間有多條道路,通過每條道路都要花費一些時間。 從 號城市開始出發,目的地爲 號城市。由於 想要好好參觀一下 國,所以 想要旅行恰好 小時。爲了讓自己的旅行更有意思, 決定不在任何一個時刻停留(走一條到城市自己的路並不算停留)。 想知道是否能夠花恰好 小時到達 號城市(每個城市可經過多次)。現在這個問題交給了你。
若可以恰好到達輸出否則輸出。(不含引號)。
【輸入格式】
第一行一個正整數 ,表示數據組數。
每組數據第一行 個整數,分別爲 。
接下來 行,每行 個整數 代表城市 和城市 之間有一條耗時爲 的雙向邊。
【輸出格式】
對於每組數據輸出或者.
【樣例輸入】
2
3 3 11
0 2 7
0 1 6
1 2 5
2 1 10000
1 0 1
【樣例輸出】
Possible
Impossible
【樣例解釋】
第一組:
第二組:顯然偶數時間都是不可能的。
【數據範圍】
%:
另有 %:
%:
題解
轉自博客園-一入OI深似海,轉載地址:https://www.cnblogs.com/yanlifneg/p/5843482.html
T1
/*
自己還是太弱~沒看出來要用hash 只是覺得自己的作法慢~
QAQ
50分暴力 先排序 一樣的縮成一種 然後枚舉正確答案是哪個
q==0 p==0的情況沒考慮到~
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 30010
#define maxm 510
using namespace std;
int n,m,p,q,cnt;
string g[maxn];
struct node{
int len;
string s;
}k[maxn];
int cmp(int a[maxm],int b[maxm]){
for(int i=1;i<=m;i++){
if(a[i]<b[i])return 1;
if(a[i]>b[i])return 0;
}
return 1;
}
int main()
{
freopen("answer.in","r",stdin);
freopen("answer.out","w",stdout);
scanf("%d%d%d%d",&n,&m,&p,&q);
for(int i=1;i<=n;i++)cin>>g[i];
sort(g+1,g+1+n);
int l=1,r;
for(r=2;r<=n;r++){
if(g[r]==g[l])continue;
k[++cnt].s=g[l];
k[cnt].len=r-l;l=r;
}
k[++cnt].s=g[l];
k[cnt].len=r-l;
int falg=0;
for(int i=1;i<=cnt;i++){
if(k[i].len!=p)continue;
int sum=0;
string x;x.clear();
for(int j=0;j<m;j++)
if(k[i].s[j]=='Y')x+='N';
else x+='Y';
for(int j=1;j<=cnt;j++)
if(k[j].s==x){
sum+=k[j].len;
break;
}
if(sum==q){
cout<<k[i].s;
falg=1;break;
}
}
if(falg==0)
for(int i=1;i<=cnt;i++){
if(k[i].len!=q)continue;
int sum=0;
string x;x.clear();
for(int j=0;j<m;j++)
if(k[i].s[j]=='Y')x+='N';
else x+='Y';
for(int j=1;j<=cnt;j++)
if(k[j].s==x){
sum+=k[j].len;
break;
}
if(sum==p){
cout<<x;
falg=1;break;
}
}
if(!falg)printf("-1\n");
return 0;
}
正解hash:
/*
正解hash
思路和之前的有相似之處
先排序 只不過沒有類似離散化的處理
把每個人的答案放入hash表 這裏用鏈表處理了碰撞的情況
然後同樣的枚舉正確答案 這不過用了hash表加速
對於pq==0的情況 枚舉答案 按字典序小的來
那難道不會T到飛嗎 不成了2^500的了嗎
答案是不會的 這裏的枚舉是針對pq==0的情況來的
結束的條件是 找到與每個人都不一樣的(存在一個即可)的就停下
所以枚舉最多30000次
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 100010
#define mod 10007
#define MOD 23333
#define bas 19
#define BAS 119
using namespace std;
int n,m,p,q,hash[maxn],HASH[maxn],ans,falg;
int num,head[maxn],cnt[maxn],t,T;
struct edge{
int v,pre;
}e[maxn*2];
struct node{
char s[510];
bool operator < (const node &x) const {
return strcmp(s,x.s)<0;
}
}a[maxn];
void Insert(int from,int to){
for(int i=head[from];i;i=e[i].pre)
if(e[i].v==to){
cnt[i]++;return;
}
num++;e[num].v=to;
e[num].pre=head[from];
head[from]=num;
cnt[num]++;
}
int Query(int from,int to){
for(int i=head[from];i;i=e[i].pre)
if(e[i].v==to)return cnt[i];
return 0;
}
void Yan(){
for(int i=1;i<=n;i++){
t=0,T=0;
for(int j=0;j<m;j++){
t=t*bas+(a[i].s[j]=='Y');t%=mod;
T=T*BAS+(a[i].s[j]=='Y');T%=MOD;
}
hash[i]=t;HASH[i]=T;
Insert(t,T);
}
for(int i=1;i<=n;i++){
if(Query(hash[i],HASH[i])==p){
int t=0,T=0;
for(int j=0;j<m;j++){
t=t*bas+(a[i].s[j]=='N');t%=mod;
T=T*BAS+(a[i].s[j]=='N');T%=MOD;
}
if(Query(t,T)==q){
ans=i;
falg=1;break;
}
if(falg)break;
}
}
if(falg)printf("%s\n",a[ans].s);
else printf("-1\n");
}
void Li(){
for(int i=1;i<=n;i++){
int t=0,T=0;
for(int j=0;j<m;j++){
t=t*bas+(a[i].s[j]=='Y');t%=mod;
T=T*BAS+(a[i].s[j]=='Y');T%=MOD;
}
hash[i]=t;HASH[i]=T;
Insert(t,T);
}
for(int i=n;i>=1;i--){
if(Query(hash[i],HASH[i])==q){
t=0,T=0;
for(int j=0;j<m;j++){
t=t*bas+(a[i].s[j]=='N');t%=mod;
T=T*BAS+(a[i].s[j]=='N');T%=MOD;
}
if(Query(t,T)==p){
ans=i;
falg=1;break;
}
if(falg)break;
}
}
if(falg){
for(int i=0;i<m;i++)
if(a[ans].s[i]=='N')printf("Y");
else printf("N");
}
else printf("-1\n");
}
void Feng(){
for(int i=1;i<=n;i++){
t=0,T=0;
for(int j=0;j<m;j++){
t=t*bas+(a[i].s[j]=='Y');t%=mod;
T=T*BAS+(a[i].s[j]=='Y');T%=MOD;
}
Insert(t,T);
t=0;T=0;
for(int j=0;j<m;j++){
t=t*bas+(a[i].s[j]=='N');t%=mod;
T=T*BAS+(a[i].s[j]=='N');T%=MOD;
}
Insert(t,T);
}
char r[510];
for(int i=0;i<m;i++)r[i]='N';
while(1){
t=0,T=0;
for(int i=0;i<m;i++){
t=t*bas+(r[i]=='Y');t%=mod;
T=T*BAS+(r[i]=='Y');T%=MOD;
}
if(Query(t,T)==0){
falg=1;break;
}
for(int i=m-1;i>=0;i--)
if(r[i]=='Y')r[i]='N';
else{
r[i]='Y';break;
}
}
if(falg)printf("%s\n",r);
else printf("-1\n");
}
int main()
{
freopen("answer.in","r",stdin);
freopen("answer.out","w",stdout);
scanf("%d%d%d%d",&n,&m,&p,&q);
for(int i=1;i<=n;i++)
scanf("%s",a[i].s);
sort(a+1,a+1+n);
if(p)Yan();
else if(q)Li();
else Feng();
return 0;
}
T2
/*
30分暴力枚舉集合不說了
其實這題需要高精的....
維護i到n &值爲j的方案數
維護1到i ^值爲j的方案數
然後枚舉斷點 乘起來
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#define maxn 2050
#define ll long long
using namespace std;
ll n,a[maxn],b[maxn],sum[maxn],f[maxn][maxn+200],g[maxn][maxn+200],ans;
int main()
{
freopen("sequence.in","r",stdin);
freopen("sequence.out","w",stdout);
cin>>n;
for(ll i=1;i<=n;i++)
cin>>a[i];
for(ll i=1;i<=n;i++)
b[i]=a[n-i+1];
for(ll i=1;i<=n;i++){
for(ll j=0;j<=2048;j++)
f[i][j]=sum[j^a[i]];
f[i][a[i]]++;
for(ll j=0;j<=2048;j++)
sum[j]+=f[i][j];
}
memset(sum,0,sizeof(sum));
for(ll i=0;i<n;i++){
for(ll j=0;j<=2048;j++)
g[i+1][j&b[i+1]]+=sum[j];
g[i+1][b[i+1]]++;
for(ll j=0;j<=2048;j++)
sum[j]+=g[i+1][j];
}
memset(sum,0,sizeof(sum));
for(ll i=1;i<n;i++){
for(ll j=0;j<2048;j++)
sum[j]+=f[i][j];
for(ll j=0;j<2048;j++)
ans+=sum[j]*g[n-i][j];
}
cout<<ans;
return 0;
}
T3
暴力dp30:
/*
暴力dp 30
狀態f[i][j]表示到了i號節點走了j的狀態是否存在
可以水過T比較小的數據
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#define maxn 1000010
using namespace std;
int T,t,n,m,f[51][maxn],g[51][51];
void Clear(){
memset(f,0,sizeof(f));
memset(g,0,sizeof(g));
}
int main()
{
freopen("travel.in","r",stdin);
freopen("travel.out","w",stdout);
scanf("%d",&T);
while(T--){
scanf("%d%d%d",&n,&m,&t);
int u,v,s;Clear();
for(int i=1;i<=m;i++){
scanf("%d%d%d",&u,&v,&s);
u++;v++;
g[u][v]=g[v][u]=s;
}
f[1][0]=1;
for(int j=1;j<=t;j++)
for(int i=1;i<=n;i++)
for(int k=1;k<=n;k++){
if(!g[i][k]||j-g[i][k]<0)continue;
f[i][j]=f[i][j]||f[k][j-g[i][k]];
}
if(f[n][t])printf("Possible\n");
else printf("Impossible\n");
}
return 0;
}
正解SFPA:
/*
正解是最短路~~~~
一開始以爲圖論 後來認爲是dp 沒想到最後又回到圖論了~~
前面dp做法的瓶頸很顯然是T太大~
但是出入的邊的權值和要小的多 所以會在某個環了轉圈
假設有一條路徑走一遍的時間爲t 中間有一個長度爲p的環
那這條路可以認爲是t+p*k長度的
所以我們只需要維護這個多出來的t就好了 先選一個環
保險起見 選從零出發的最小的環 長度設爲x
定義dis[i][j] 表示到了i 時間爲j+k*x 且k最小 這裏跑最短路找最小
爲什麼找最小呢 因爲只有當dis[n][T%x]<=T 時纔可以 所以爲了儘量滿足條件
維護最小的dis
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define maxn 210
#define ll long long
using namespace std;
ll T,t,n,m,num,head[maxn],dis[maxn][maxn*100],mxx,f[maxn][maxn];
struct node{
ll v,t,pre;
}e[maxn*2];
struct point{
int v,s;
};
queue<point>q;
void Clear(){
memset(head,0,sizeof(head));
memset(f,0,sizeof(f));
num=0;
}
ll min(ll a,ll b){
return a<b?a:b;
}
void Add(ll from,ll to,ll dis){
num++;e[num].v=to;
e[num].t=dis;
e[num].pre=head[from];
head[from]=num;
}
void SPFA(){
memset(dis,127/3,sizeof(dis));
q.push((point){1,0});
f[1][0]=1;dis[1][0]=0;
while(!q.empty()){
ll x=q.front().v;
ll s=q.front().s;
q.pop();f[x][s]=0;
for(int i=head[x];i;i=e[i].pre){
ll v=e[i].v;
ll di=s+e[i].t;
di%=mxx;//這裏分清誰做下標誰是dis值
if(dis[v][di]>s+e[i].t){
dis[v][di]=s+e[i].t;
if(f[v][di]==0){
f[v][di]=1;
q.push((point){v,di});
}
}
}
}
}
int main()
{
freopen("travel.in","r",stdin);
freopen("travel.out","w",stdout);
cin>>T;
while(T--){
cin>>n>>m>>t;
ll u,v,s;Clear();
mxx=0x7fffffff;
for(int i=1;i<=m;i++){
cin>>u>>v>>s;
u++;v++;
Add(u,v,s);Add(v,u,s);
if(u==1||v==1)mxx=min(mxx,s);
}
if(mxx==0x7fffffff){//不連通
printf("Impossible\n");
continue;
}
mxx*=2;
SPFA();
if(dis[n][t%mxx]<=t)printf("Possible\n");
else printf("Impossible\n");
}
return 0;
}